diff --git a/.gitmodules b/.gitmodules index a813fbefd..139c1e5b6 100644 --- a/.gitmodules +++ b/.gitmodules @@ -34,6 +34,7 @@ [submodule "src/external/icu"] path = src/external/icu url = ../darling-icu.git + branch = darling [submodule "src/external/libxslt"] path = src/external/libxslt url = ../darling-libxslt.git @@ -43,15 +44,19 @@ [submodule "src/external/curl"] path = src/external/curl url = ../darling-curl.git + branch = darling [submodule "src/external/shell_cmds"] path = src/external/shell_cmds url = ../darling-shell_cmds.git + branch = darling [submodule "src/external/file_cmds"] path = src/external/file_cmds url = ../darling-file_cmds.git + branch = darling [submodule "src/external/bash"] path = src/external/bash url = ../darling-bash.git + branch = darling [submodule "src/external/darling-dmg"] path = src/external/darling-dmg url = ../darling-dmg.git @@ -61,6 +66,7 @@ [submodule "src/external/liblzma"] path = src/external/liblzma url = ../darling-liblzma.git + branch = darling [submodule "src/external/cfnetwork"] path = src/external/cfnetwork url = ../darling-cfnetwork.git @@ -70,66 +76,86 @@ [submodule "src/external/less"] path = src/external/less url = ../darling-less.git + branch = darling [submodule "src/external/grep"] path = src/external/grep url = ../darling-grep.git + branch = darling [submodule "src/external/pcre"] path = src/external/pcre url = ../darling-pcre.git + branch = darling [submodule "src/external/awk"] path = src/external/awk url = ../darling-awk.git + branch = darling [submodule "src/external/adv_cmds"] path = src/external/adv_cmds url = ../darling-adv_cmds.git + branch = darling [submodule "src/external/AppKit"] path = src/external/AppKit url = ../darling-appkit.git [submodule "src/external/nano"] path = src/external/nano url = ../darling-nano.git + branch = darling [submodule "src/external/zip"] path = src/external/zip url = ../darling-zip.git + branch = darling [submodule "src/external/python"] path = src/external/python url = ../darling-python.git + branch = darling [submodule "src/external/expat"] path = src/external/expat url = ../darling-expat.git + branch = darling [submodule "src/external/sqlite"] path = src/external/sqlite url = ../darling-sqlite.git + branch = darling [submodule "src/external/libauto"] path = src/external/libauto url = ../darling-libauto.git + branch = darling [submodule "src/external/coretls"] path = src/external/coretls url = ../darling-coretls.git + branch = darling [submodule "src/external/security"] path = src/external/security url = ../darling-security.git + branch = darling [submodule "src/external/libxpc"] path = src/external/libxpc url = ../darling-libxpc.git + branch = darling [submodule "src/external/gnutar"] path = src/external/gnutar url = ../darling-gnutar.git + branch = darling [submodule "src/external/gpatch"] path = src/external/gpatch url = ../darling-gpatch.git + branch = darling [submodule "src/external/gnudiff"] path = src/external/gnudiff url = ../darling-gnudiff.git + branch = darling [submodule "src/external/apr"] path = src/external/apr url = ../darling-apr.git + branch = darling [submodule "src/external/libarchive"] path = src/external/libarchive url = ../darling-libarchive.git + branch = darling [submodule "src/external/file"] path = src/external/file url = ../darling-file.git + branch = darling [submodule "src/external/corecrypto"] path = src/external/corecrypto url = ../darling-corecrypto.git diff --git a/cmake/darling_exe.cmake b/cmake/darling_exe.cmake index 824ed5a41..38cba2b36 100644 --- a/cmake/darling_exe.cmake +++ b/cmake/darling_exe.cmake @@ -3,18 +3,17 @@ if(COMMAND cmake_policy) cmake_policy(SET CMP0011 NEW) endif(COMMAND cmake_policy) +include(use_ld64) FUNCTION(add_darling_executable exe) foreach(f IN LISTS ARGN) set(files ${files} ${f}) endforeach(f) - SET(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/darling") - SET(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) - SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) - add_executable(${exe} ${files}) - target_link_libraries(${exe} system_c system_kernel system dyld "-Wl,--whole-archive" dexe_startup "-Wl,--no-whole-archive" "-shared") - set_target_properties(${exe} PROPERTIES COMPILE_FLAGS "-Denviron=__darwin_environ -Ddlopen=__darwin_dlopen -Ddlclose=__darwin_dlclose -Ddlsym=__darwin_dlsym -Ddladdr=__darwin_dladdr -Ddlerror=__darwin_dlerror") + set_property(TARGET ${exe} APPEND_STRING PROPERTY + LINK_FLAGS " ${CMAKE_EXE_LINKER_FLAGS} -L${CMAKE_BINARY_DIR}/src/csu/CMakeFiles/csu.dir ") + use_ld64(${exe}) + add_dependencies(${exe} csu) ENDFUNCTION(add_darling_executable) diff --git a/cmake/darling_lib.cmake b/cmake/darling_lib.cmake new file mode 100644 index 000000000..887927a6a --- /dev/null +++ b/cmake/darling_lib.cmake @@ -0,0 +1,31 @@ +if(COMMAND cmake_policy) + cmake_policy(SET CMP0003 NEW) + cmake_policy(SET CMP0011 NEW) +endif(COMMAND cmake_policy) + +include(use_ld64) + +FUNCTION(add_darling_library name) + foreach(f IN LISTS ARGN) + set(files ${files} ${f}) + endforeach(f) + + set(CMAKE_SKIP_RPATH TRUE) + add_library(${name} ${files}) + + # Link using Apple's ld64 + set_target_properties(${name} PROPERTIES + SUFFIX ".dylib" + NO_SONAME TRUE) + + set_property(TARGET ${name} APPEND_STRING PROPERTY + LINK_FLAGS " ${CMAKE_SHARED_LINKER_FLAGS}") + + if (DYLIB_INSTALL_NAME) + set_property(TARGET ${name} APPEND_STRING PROPERTY + LINK_FLAGS " -Wl,-dylib_install_name,${DYLIB_INSTALL_NAME} ") + endif (DYLIB_INSTALL_NAME) + + use_ld64(${name}) +ENDFUNCTION(add_darling_library) + diff --git a/cmake/use_ld64.cmake b/cmake/use_ld64.cmake new file mode 100644 index 000000000..21e555697 --- /dev/null +++ b/cmake/use_ld64.cmake @@ -0,0 +1,26 @@ +FUNCTION(use_ld64 target) + set_property(TARGET ${target} APPEND_STRING PROPERTY + LINK_FLAGS " -B ${CMAKE_BINARY_DIR}/src/external/cctools-port/cctools/ld64/src/ -target x86_64-apple-darwin11 \ + -Wl,-dylib_file,/usr/lib/system/libsystem_c.dylib:${CMAKE_BINARY_DIR}/src/libc/libsystem_c.dylib \ + -Wl,-dylib_file,/usr/lib/system/libsystem_kernel.dylib:${CMAKE_BINARY_DIR}/src/kernel/libsystem_kernel.dylib \ + -Wl,-dylib_file,/usr/lib/system/libunwind.dylib:${CMAKE_BINARY_DIR}/src/libunwind/libunwind.dylib \ + -Wl,-dylib_file,/usr/lib/system/libsystem_m.dylib:${CMAKE_BINARY_DIR}/src/libm/libsystem_m.dylib \ + -Wl,-dylib_file,/usr/lib/system/libcompiler_rt.dylib:${CMAKE_BINARY_DIR}/src/external/compiler-rt/lib/builtins/libcompiler_rt.dylib \ + -Wl,-dylib_file,/usr/lib/system/libdyld.dylib:${CMAKE_BINARY_DIR}/src/dyld-apple/libdyld.dylib \ + -Wl,-dylib_file,/usr/lib/system/libsystem_info.dylib:${CMAKE_BINARY_DIR}/src/libinfo/libsystem_info.dylib \ + -Wl,-dylib_file,/usr/lib/system/libsystem_blocks.dylib:${CMAKE_BINARY_DIR}/src/external/compiler-rt/lib/BlocksRuntime/libsystem_blocks.dylib \ + -Wl,-dylib_file,/usr/lib/libc++abi.dylib:${CMAKE_BINARY_DIR}/src/external/libcxxabi/src/libc++abi.dylib \ + -Wl,-dylib_file,/usr/lib/system/liblaunch.dylib:${CMAKE_BINARY_DIR}/src/launchd/liblaunch/liblaunch.dylib \ + -Wl,-dylib_file,/usr/lib/system/libkeymgr.dylib:${CMAKE_BINARY_DIR}/src/keymgr/libkeymgr.dylib \ + -Wl,-dylib_file,/usr/lib/system/libquarantine.dylib:${CMAKE_BINARY_DIR}/src/quarantine/libquarantine.dylib \ + -Wl,-dylib_file,/usr/lib/system/libremovefile.dylib:${CMAKE_BINARY_DIR}/src/libremovefile/libremovefile.dylib \ + -Wl,-dylib_file,/usr/lib/system/libcopyfile.dylib:${CMAKE_BINARY_DIR}/src/copyfile/libcopyfile.dylib \ + -Wl,-dylib_file,/usr/lib/system/libsystem_coreservices.dylib:${CMAKE_BINARY_DIR}/src/libsystem_coreservices/libsystem_coreservices.dylib \ + -Wl,-dylib_file,/usr/lib/system/libsystem_notify.dylib:${CMAKE_BINARY_DIR}/src/libnotify/libsystem_notify.dylib \ + -Wl,-dylib_file,/usr/lib/system/libdispatch.dylib:${CMAKE_BINARY_DIR}/src/external/libdispatch/src/libdispatch.dylib \ + -Wl,-dylib_file,/usr/lib/system/libsystem_malloc.dylib:${CMAKE_BINARY_DIR}/src/libmalloc/libsystem_malloc.dylib ") + + add_dependencies(${target} x86_64-apple-darwin11-ld) + +ENDFUNCTION(use_ld64) + diff --git a/kernel-include/sys/codesign.h b/kernel-include/sys/codesign.h index a1001b72c..ca225ce58 100644 --- a/kernel-include/sys/codesign.h +++ b/kernel-include/sys/codesign.h @@ -29,46 +29,250 @@ #ifndef _SYS_CODESIGN_H_ #define _SYS_CODESIGN_H_ -#include - /* code signing attributes of a process */ -#define CS_VALID 0x0001 /* dynamically valid */ -#define CS_HARD 0x0100 /* don't load invalid pages */ -#define CS_KILL 0x0200 /* kill process if it becomes invalid */ -#define CS_EXEC_SET_HARD 0x1000 /* set CS_HARD on any exec'ed process */ -#define CS_EXEC_SET_KILL 0x2000 /* set CS_KILL on any exec'ed process */ -#define CS_KILLED 0x10000 /* was killed by kernel for invalidity */ -#define CS_RESTRICT 0x20000 /* tell dyld to treat restricted */ +#define CS_VALID 0x0000001 /* dynamically valid */ +#define CS_ADHOC 0x0000002 /* ad hoc signed */ +#define CS_GET_TASK_ALLOW 0x0000004 /* has get-task-allow entitlement */ +#define CS_INSTALLER 0x0000008 /* has installer entitlement */ + +#define CS_HARD 0x0000100 /* don't load invalid pages */ +#define CS_KILL 0x0000200 /* kill process if it becomes invalid */ +#define CS_CHECK_EXPIRATION 0x0000400 /* force expiration checking */ +#define CS_RESTRICT 0x0000800 /* tell dyld to treat restricted */ +#define CS_ENFORCEMENT 0x0001000 /* require enforcement */ +#define CS_REQUIRE_LV 0x0002000 /* require library validation */ +#define CS_ENTITLEMENTS_VALIDATED 0x0004000 /* code signature permits restricted entitlements */ + +#define CS_ALLOWED_MACHO (CS_ADHOC | CS_HARD | CS_KILL | CS_CHECK_EXPIRATION | CS_RESTRICT | CS_ENFORCEMENT | CS_REQUIRE_LV) + +#define CS_EXEC_SET_HARD 0x0100000 /* set CS_HARD on any exec'ed process */ +#define CS_EXEC_SET_KILL 0x0200000 /* set CS_KILL on any exec'ed process */ +#define CS_EXEC_SET_ENFORCEMENT 0x0400000 /* set CS_ENFORCEMENT on any exec'ed process */ +#define CS_EXEC_SET_INSTALLER 0x0800000 /* set CS_INSTALLER on any exec'ed process */ + +#define CS_KILLED 0x1000000 /* was killed by kernel for invalidity */ +#define CS_DYLD_PLATFORM 0x2000000 /* dyld used to load this is a platform binary */ +#define CS_PLATFORM_BINARY 0x4000000 /* this is a platform binary */ +#define CS_PLATFORM_PATH 0x8000000 /* platform binary by the fact of path (osx only) */ +#define CS_DEBUGGED 0x10000000 /* process is currently or has previously been debugged and allowed to run with invalid pages */ +#define CS_SIGNED 0x20000000 /* process has a signature (may have gone invalid) */ +#define CS_DEV_CODE 0x40000000 /* code is dev signed, cannot be loaded into prod signed code (will go away with rdar://problem/28322552) */ + +#define CS_ENTITLEMENT_FLAGS (CS_GET_TASK_ALLOW | CS_INSTALLER) + +/* MAC flags used by F_ADDFILESIGS_* */ +#define MAC_VNODE_CHECK_DYLD_SIM 0x1 /* tells the MAC framework that dyld-sim is being loaded */ /* csops operations */ #define CS_OPS_STATUS 0 /* return status */ #define CS_OPS_MARKINVALID 1 /* invalidate process */ #define CS_OPS_MARKHARD 2 /* set HARD flag */ #define CS_OPS_MARKKILL 3 /* set KILL flag (sticky) */ -#define CS_OPS_PIDPATH 4 /* get executable's pathname */ +#ifdef KERNEL_PRIVATE +/* CS_OPS_PIDPATH 4 */ +#endif #define CS_OPS_CDHASH 5 /* get code directory hash */ #define CS_OPS_PIDOFFSET 6 /* get offset of active Mach-o slice */ #define CS_OPS_ENTITLEMENTS_BLOB 7 /* get entitlements blob */ #define CS_OPS_MARKRESTRICT 8 /* set RESTRICT flag (sticky) */ -#define CS_OPS_SET_STATUS 9 -#define CS_OPS_BLOB 10 -#define CS_OPS_IDENTITY 11 +#define CS_OPS_SET_STATUS 9 /* set codesign flags */ +#define CS_OPS_BLOB 10 /* get codesign blob */ +#define CS_OPS_IDENTITY 11 /* get codesign identity */ +#define CS_OPS_CLEARINSTALLER 12 /* clear INSTALLER flag */ + +/* + * Magic numbers used by Code Signing + */ +enum { + CSMAGIC_REQUIREMENT = 0xfade0c00, /* single Requirement blob */ + CSMAGIC_REQUIREMENTS = 0xfade0c01, /* Requirements vector (internal requirements) */ + CSMAGIC_CODEDIRECTORY = 0xfade0c02, /* CodeDirectory blob */ + CSMAGIC_EMBEDDED_SIGNATURE = 0xfade0cc0, /* embedded form of signature data */ + CSMAGIC_EMBEDDED_SIGNATURE_OLD = 0xfade0b02, /* XXX */ + CSMAGIC_EMBEDDED_ENTITLEMENTS = 0xfade7171, /* embedded entitlements */ + CSMAGIC_DETACHED_SIGNATURE = 0xfade0cc1, /* multi-arch collection of embedded signatures */ + CSMAGIC_BLOBWRAPPER = 0xfade0b01, /* CMS Signature, among other things */ + + CS_SUPPORTSSCATTER = 0x20100, + CS_SUPPORTSTEAMID = 0x20200, + + CSSLOT_CODEDIRECTORY = 0, /* slot index for CodeDirectory */ + CSSLOT_INFOSLOT = 1, + CSSLOT_REQUIREMENTS = 2, + CSSLOT_RESOURCEDIR = 3, + CSSLOT_APPLICATION = 4, + CSSLOT_ENTITLEMENTS = 5, + + CSSLOT_ALTERNATE_CODEDIRECTORIES = 0x1000, /* first alternate CodeDirectory, if any */ + CSSLOT_ALTERNATE_CODEDIRECTORY_MAX = 5, /* max number of alternate CD slots */ + CSSLOT_ALTERNATE_CODEDIRECTORY_LIMIT = CSSLOT_ALTERNATE_CODEDIRECTORIES + CSSLOT_ALTERNATE_CODEDIRECTORY_MAX, /* one past the last */ + + CSSLOT_SIGNATURESLOT = 0x10000, /* CMS Signature */ + + CSTYPE_INDEX_REQUIREMENTS = 0x00000002, /* compat with amfi */ + CSTYPE_INDEX_ENTITLEMENTS = 0x00000005, /* compat with amfi */ + + CS_HASHTYPE_SHA1 = 1, + CS_HASHTYPE_SHA256 = 2, + CS_HASHTYPE_SHA256_TRUNCATED = 3, + CS_HASHTYPE_SHA384 = 4, + + CS_SHA1_LEN = 20, + CS_SHA256_TRUNCATED_LEN = 20, + + CS_CDHASH_LEN = 20, /* always - larger hashes are truncated */ + CS_HASH_MAX_SIZE = 48, /* max size of the hash we'll support */ +}; + + +#define KERNEL_HAVE_CS_CODEDIRECTORY 1 +#define KERNEL_CS_CODEDIRECTORY_HAVE_PLATFORM 1 + +/* + * C form of a CodeDirectory. + */ +typedef struct __CodeDirectory { + uint32_t magic; /* magic number (CSMAGIC_CODEDIRECTORY) */ + uint32_t length; /* total length of CodeDirectory blob */ + uint32_t version; /* compatibility version */ + uint32_t flags; /* setup and mode flags */ + uint32_t hashOffset; /* offset of hash slot element at index zero */ + uint32_t identOffset; /* offset of identifier string */ + uint32_t nSpecialSlots; /* number of special hash slots */ + uint32_t nCodeSlots; /* number of ordinary (code) hash slots */ + uint32_t codeLimit; /* limit to main image signature range */ + uint8_t hashSize; /* size of each hash in bytes */ + uint8_t hashType; /* type of hash (cdHashType* constants) */ + uint8_t platform; /* platform identifier; zero if not platform binary */ + uint8_t pageSize; /* log2(page size in bytes); 0 => infinite */ + uint32_t spare2; /* unused (must be zero) */ + /* Version 0x20100 */ + uint32_t scatterOffset; /* offset of optional scatter vector */ + /* Version 0x20200 */ + uint32_t teamOffset; /* offset of optional team identifier */ + /* followed by dynamic content as located by offset fields above */ +} CS_CodeDirectory; + +/* + * Structure of an embedded-signature SuperBlob + */ + +typedef struct __BlobIndex { + uint32_t type; /* type of entry */ + uint32_t offset; /* offset of entry */ +} CS_BlobIndex; + +typedef struct __SC_SuperBlob { + uint32_t magic; /* magic number */ + uint32_t length; /* total length of SuperBlob */ + uint32_t count; /* number of index entries following */ + CS_BlobIndex index[]; /* (count) entries */ + /* followed by Blobs in no particular order as indicated by offsets in index */ +} CS_SuperBlob; + +#define KERNEL_HAVE_CS_GENERICBLOB 1 +typedef struct __SC_GenericBlob { + uint32_t magic; /* magic number */ + uint32_t length; /* total length of blob */ + char data[]; +} CS_GenericBlob; + +typedef struct __SC_Scatter { + uint32_t count; // number of pages; zero for sentinel (only) + uint32_t base; // first page number + uint64_t targetOffset; // offset in target + uint64_t spare; // reserved +} SC_Scatter; -#define CS_OPS_SIGPUP_INSTALL 20 -#define CS_OPS_SIGPUP_DROP 21 -#define CS_OPS_SIGPUP_VALIDATE 22 #ifndef KERNEL -__BEGIN_DECLS +#include +#include +__BEGIN_DECLS /* code sign operations */ int csops(pid_t pid, unsigned int ops, void * useraddr, size_t usersize); -/* works only with CS_OPS_PIDPATH and CS_OPS_ENTITLEMENTS_BLOB */ int csops_audittoken(pid_t pid, unsigned int ops, void * useraddr, size_t usersize, audit_token_t * token); +__END_DECLS + +#else /* !KERNEL */ + +#include + +#include +#include + +struct vnode; +struct cs_blob; +struct fileglob; + +__BEGIN_DECLS +int cs_valid(struct proc *); +int cs_enforcement(struct proc *); +int cs_require_lv(struct proc *); +int cs_system_require_lv(void); +uint32_t cs_entitlement_flags(struct proc *p); +int cs_entitlements_blob_get(struct proc *, void **, size_t *); +int cs_restricted(struct proc *); +uint8_t * cs_get_cdhash(struct proc *); + +struct cs_blob * csproc_get_blob(struct proc *); +struct cs_blob * csvnode_get_blob(struct vnode *, off_t); +void csvnode_print_debug(struct vnode *); + +off_t csblob_get_base_offset(struct cs_blob *); +vm_size_t csblob_get_size(struct cs_blob *); +vm_address_t csblob_get_addr(struct cs_blob *); +const char * csblob_get_teamid(struct cs_blob *); +const char * csblob_get_identity(struct cs_blob *); +const uint8_t * csblob_get_cdhash(struct cs_blob *); +int csblob_get_platform_binary(struct cs_blob *); +unsigned int csblob_get_flags(struct cs_blob *blob); + +int csblob_get_entitlements(struct cs_blob *, void **, size_t *); + +const CS_GenericBlob * + csblob_find_blob(struct cs_blob *, uint32_t, uint32_t); +const CS_GenericBlob * + csblob_find_blob_bytes(const uint8_t *, size_t, uint32_t, uint32_t); +void * csblob_entitlements_dictionary_copy(struct cs_blob *csblob); +void csblob_entitlements_dictionary_set(struct cs_blob *csblob, void * entitlements); + +/* + * Mostly convenience functions below + */ + +const char * csproc_get_teamid(struct proc *); +const char * csvnode_get_teamid(struct vnode *, off_t); +int csproc_get_platform_binary(struct proc *); +int csproc_get_prod_signed(struct proc *); +const char * csfg_get_teamid(struct fileglob *); +int csfg_get_path(struct fileglob *, char *, int *); +int csfg_get_platform_binary(struct fileglob *); +uint8_t * csfg_get_cdhash(struct fileglob *, uint64_t, size_t *); +int csfg_get_prod_signed(struct fileglob *); + +extern int cs_debug; + +#ifdef XNU_KERNEL_PRIVATE + +void cs_init(void); +int cs_allow_invalid(struct proc *); +int cs_invalid_page(addr64_t vaddr, boolean_t *cs_killed); +int csproc_get_platform_path(struct proc *); + +#if !SECURE_KERNEL +extern int cs_enforcement_panic; +#endif + +#endif /* XNU_KERNEL_PRIVATE */ + __END_DECLS -#endif /* ! KERNEL */ + + +#endif /* KERNEL */ #endif /* _SYS_CODESIGN_H_ */ diff --git a/platform-include/sys/csr.h b/platform-include/sys/csr.h new file mode 100644 index 000000000..1349ec948 --- /dev/null +++ b/platform-include/sys/csr.h @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2014 Apple Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_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. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * 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_OSREFERENCE_LICENSE_HEADER_END@ + */ + +#ifndef _SYS_CSR_H_ +#define _SYS_CSR_H_ + +#include +#include +#include + +#ifdef __APPLE_API_PRIVATE + +typedef uint32_t csr_config_t; +typedef uint32_t csr_op_t; + +/* Rootless configuration flags */ +#define CSR_ALLOW_UNTRUSTED_KEXTS (1 << 0) +#define CSR_ALLOW_UNRESTRICTED_FS (1 << 1) +#define CSR_ALLOW_TASK_FOR_PID (1 << 2) +#define CSR_ALLOW_KERNEL_DEBUGGER (1 << 3) +#define CSR_ALLOW_APPLE_INTERNAL (1 << 4) +#define CSR_ALLOW_DESTRUCTIVE_DTRACE (1 << 5) /* name deprecated */ +#define CSR_ALLOW_UNRESTRICTED_DTRACE (1 << 5) +#define CSR_ALLOW_UNRESTRICTED_NVRAM (1 << 6) +#define CSR_ALLOW_DEVICE_CONFIGURATION (1 << 7) +#define CSR_ALLOW_ANY_RECOVERY_OS (1 << 8) + +#define CSR_VALID_FLAGS (CSR_ALLOW_UNTRUSTED_KEXTS | \ + CSR_ALLOW_UNRESTRICTED_FS | \ + CSR_ALLOW_TASK_FOR_PID | \ + CSR_ALLOW_KERNEL_DEBUGGER | \ + CSR_ALLOW_APPLE_INTERNAL | \ + CSR_ALLOW_UNRESTRICTED_DTRACE | \ + CSR_ALLOW_UNRESTRICTED_NVRAM | \ + CSR_ALLOW_DEVICE_CONFIGURATION | \ + CSR_ALLOW_ANY_RECOVERY_OS) + + +/* CSR capabilities that a booter can give to the system */ +#define CSR_CAPABILITY_UNLIMITED (1 << 0) +#define CSR_CAPABILITY_CONFIG (1 << 1) +#define CSR_CAPABILITY_APPLE_INTERNAL (1 << 2) + +#define CSR_VALID_CAPABILITIES (CSR_CAPABILITY_UNLIMITED | CSR_CAPABILITY_CONFIG | CSR_CAPABILITY_APPLE_INTERNAL) + +#ifdef PRIVATE +/* Private system call interface between Libsyscall and xnu */ + +/* Syscall flavors */ +enum csr_syscalls { + CSR_SYSCALL_CHECK, + CSR_SYSCALL_GET_ACTIVE_CONFIG, +}; + +#endif /* PRIVATE */ + +__BEGIN_DECLS + +#ifdef XNU_KERNEL_PRIVATE +void csr_init(void); +#endif + +/* Syscalls */ +int csr_check(csr_config_t mask); +int csr_get_active_config(csr_config_t *config); + +__END_DECLS + +#endif /* __APPLE_API_PRIVATE */ + +#endif /* _SYS_CSR_H_ */ diff --git a/platform-include/sys/fcntl.h b/platform-include/sys/fcntl.h index 411e56827..2de689085 100644 --- a/platform-include/sys/fcntl.h +++ b/platform-include/sys/fcntl.h @@ -1,6 +1,5 @@ -// Modified by Lubos Dolezel for Darling /* - * Copyright (c) 2000-2007 Apple Inc. All rights reserved. + * Copyright (c) 2000-2013 Apple Inc. All rights reserved. * * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ * @@ -83,25 +82,10 @@ #endif /* We should not be exporting size_t here. Temporary for gcc bootstrapping. */ -#ifndef _SIZE_T -#define _SIZE_T -typedef __darwin_size_t size_t; -#endif - -#ifndef _MODE_T -typedef __darwin_mode_t mode_t; -#define _MODE_T -#endif - -#ifndef _OFF_T -typedef __darwin_off_t off_t; -#define _OFF_T -#endif - -#ifndef _PID_T -typedef __darwin_pid_t pid_t; -#define _PID_T -#endif +#include +#include +#include +#include /* * File status flags: these are used by open(2), fcntl(2). @@ -130,9 +114,9 @@ typedef __darwin_pid_t pid_t; #endif #define O_NONBLOCK 0x0004 /* no delay */ #define O_APPEND 0x0008 /* set append mode */ -#ifndef O_SYNC /* allow simultaneous inclusion of */ -#define O_SYNC 0x0080 /* synch I/O file integrity */ -#endif + +#include + #if !defined(_POSIX_C_SOURCE) || defined(_DARWIN_C_SOURCE) #define O_SHLOCK 0x0010 /* open with shared file lock */ #define O_EXLOCK 0x0020 /* open with exclusive file lock */ @@ -148,6 +132,22 @@ typedef __darwin_pid_t pid_t; #define FDEFER 0x2000 /* defer for next gc pass */ #define FHASLOCK 0x4000 /* descriptor holds advisory lock */ #endif + +#if __DARWIN_C_LEVEL >= 200809L +/* + * Descriptor value for the current working directory + */ +#define AT_FDCWD -2 + +/* + * Flags for the at functions + */ +#define AT_EACCESS 0x0010 /* Use effective ids in access check */ +#define AT_SYMLINK_NOFOLLOW 0x0020 /* Act on the symlink itself not the target */ +#define AT_SYMLINK_FOLLOW 0x0040 /* Act on target of symlink */ +#define AT_REMOVEDIR 0x0080 /* Path refers to directory */ +#endif + #if !defined(_POSIX_C_SOURCE) || defined(_DARWIN_C_SOURCE) #define O_EVTONLY 0x8000 /* descriptor requested for event notifications only */ #endif @@ -168,9 +168,7 @@ typedef __darwin_pid_t pid_t; #define O_SYMLINK 0x200000 /* allow open of a symlink */ #endif -#ifndef O_DSYNC /* allow simultaneous inclusion of */ -#define O_DSYNC 0x400000 /* synch I/O data integrity */ -#endif +#include #ifdef KERNEL #define FNODIRECT 0x800000 /* fcntl(F_NODIRECT, 1) */ @@ -188,9 +186,18 @@ typedef __darwin_pid_t pid_t; #define FSINGLE_WRITER 0x4000000 /* fcntl(F_SINGLE_WRITER, 1) */ #endif +#ifdef KERNEL +#define O_CLOFORK 0x8000000 /* implicitly set FD_CLOFORK */ +#endif + +#ifdef KERNEL +#define FUNENCRYPTED 0x10000000 +#endif + /* Data Protection Flags */ #if !defined(_POSIX_C_SOURCE) || defined(_DARWIN_C_SOURCE) #define O_DP_GETRAWENCRYPTED 0x0001 +#define O_DP_GETRAWUNENCRYPTED 0x0002 #endif @@ -225,8 +232,8 @@ typedef __darwin_pid_t pid_t; */ #if !defined(_POSIX_C_SOURCE) || defined(_DARWIN_C_SOURCE) -#define CPF_OVERWRITE 1 -#define CPF_IGNORE_MODE 2 +#define CPF_OVERWRITE 0x0001 +#define CPF_IGNORE_MODE 0x0002 #define CPF_MASK (CPF_OVERWRITE|CPF_IGNORE_MODE) #endif @@ -245,7 +252,9 @@ typedef __darwin_pid_t pid_t; #define F_GETLK 7 /* get record locking information */ #define F_SETLK 8 /* set record locking information */ #define F_SETLKW 9 /* F_SETLK; wait if blocked */ -#define F_SETLKWTIMEOUT 10 +#if __DARWIN_C_LEVEL >= __DARWIN_C_FULL +#define F_SETLKWTIMEOUT 10 /* F_SETLK; wait if blocked, return on timeout */ +#endif /* __DARWIN_C_LEVEL >= __DARWIN_C_FULL */ #if !defined(_POSIX_C_SOURCE) || defined(_DARWIN_C_SOURCE) #define F_FLUSH_DATA 40 #define F_CHKCLEAN 41 /* Used for regression test */ @@ -253,8 +262,9 @@ typedef __darwin_pid_t pid_t; #define F_SETSIZE 43 /* Truncate a file without zeroing space */ #define F_RDADVISE 44 /* Issue an advisory read async with no copy to user */ #define F_RDAHEAD 45 /* turn read ahead off/on for this fd */ -#define F_READBOOTSTRAP 46 /* Read bootstrap from disk */ -#define F_WRITEBOOTSTRAP 47 /* Write bootstrap on disk */ +/* + * 46,47 used to be F_READBOOTSTRAP and F_WRITEBOOTSTRAP + */ #define F_NOCACHE 48 /* turn data caching off/on for this fd */ #define F_LOG2PHYS 49 /* file offset to device offset */ #define F_GETPATH 50 /* return the full path of the fd */ @@ -272,7 +282,10 @@ typedef __darwin_pid_t pid_t; #define F_ADDSIGS 59 /* add detached signatures */ +#ifdef PRIVATE +/* Deprecated/Removed in 10.9 */ #define F_MARKDEPENDENCY 60 /* this process hosts the device supporting the fs backing this fd */ +#endif #define F_ADDFILESIGS 61 /* add signature from same file (used by dyld for shared libs) */ @@ -299,8 +312,7 @@ typedef __darwin_pid_t pid_t; #define F_SETBACKINGSTORE 70 /* Mark the file as being the backing store for another filesystem */ #define F_GETPATH_MTMINFO 71 /* return the full path of the FD, but error in specific mtmd circumstances */ -#define F_GETCODEDIR 72 -/* 72 is free. It used to be F_GETENCRYPTEDDATA, which is now removed. */ +#define F_GETCODEDIR 72 /* Returns the code directory, with associated hashes, to the caller */ #define F_SETNOSIGPIPE 73 /* No SIGPIPE generated on EPIPE */ #define F_GETNOSIGPIPE 74 /* Status of SIGPIPE for this fd */ @@ -312,7 +324,46 @@ typedef __darwin_pid_t pid_t; #define F_GETPROTECTIONLEVEL 77 /* Get the protection version number for this filesystem */ -#define F_FINDSIGS 78 +#define F_FINDSIGS 78 /* Add detached code signatures (used by dyld for shared libs) */ + +#ifdef PRIVATE +#define F_GETDEFAULTPROTLEVEL 79 /* Get the default protection level for the filesystem */ +#define F_MAKECOMPRESSED 80 /* Make the file compressed; truncate & toggle BSD bits */ +#define F_SET_GREEDY_MODE 81 /* + * indicate to the filesystem/storage driver that the content to be + * written should be written in greedy mode for additional speed at + * the cost of storage efficiency. A nonzero value enables it, 0 disables it. + */ + +#define F_SETIOTYPE 82 /* + * Use parameters to describe content being written to the FD. See + * flag definitions below for argument bits. + */ +#endif + +#define F_ADDFILESIGS_FOR_DYLD_SIM 83 /* Add signature from same file, only if it is signed by Apple (used by dyld for simulator) */ + +#ifdef PRIVATE +#define F_RECYCLE 84 /* Recycle vnode; debug/development builds only */ +#endif + +#define F_BARRIERFSYNC 85 /* fsync + issue barrier to drive */ + +#ifdef PRIVATE +#define F_OFD_SETLK 90 /* Acquire or release open file description lock */ +#define F_OFD_SETLKW 91 /* (as F_OFD_SETLK but blocking if conflicting lock) */ +#define F_OFD_GETLK 92 /* Examine OFD lock */ + +#define F_OFD_SETLKWTIMEOUT 93 /* (as F_OFD_SETLKW but return if timeout) */ +#define F_OFD_GETLKPID 94 /* get record locking information */ + +#define F_SETCONFINED 95 /* "confine" OFD to process */ +#define F_GETCONFINED 96 /* is-fd-confined? */ +#endif + +#define F_ADDFILESIGS_RETURN 97 /* Add signature from same file, return end offset in structure on sucess */ +#define F_CHECK_LV 98 /* Check if Library Validation allows this Mach-O file to be mapped into the calling process */ + // FS-specific fcntl()'s numbers begin at 0x00010000 and go up #define FCNTL_FS_SPECIFIC_BASE 0x00010000 @@ -325,6 +376,9 @@ typedef __darwin_pid_t pid_t; /* file descriptor flags (F_GETFD, F_SETFD) */ #define FD_CLOEXEC 1 /* close-on-exec flag */ +#if PRIVATE +#define FD_CLOFORK 2 /* close-on-fork flag */ +#endif /* record locking flags (F_GETLK, F_SETLK, F_SETLKW) */ #define F_RDLCK 1 /* shared or read lock */ @@ -337,64 +391,28 @@ typedef __darwin_pid_t pid_t; #define F_PROV 0x080 /* Non-coalesced provisional lock */ #define F_WAKE1_SAFE 0x100 /* its safe to only wake one waiter */ #define F_ABORT 0x200 /* lock attempt aborted (force umount) */ +#define F_OFD_LOCK 0x400 /* Use "OFD" semantics for lock */ +#endif + +#if PRIVATE +/* + * ISOCHRONOUS attempts to sustain a minimum platform-dependent throughput + * for the duration of the I/O delivered to the driver. + */ +#define F_IOTYPE_ISOCHRONOUS 0x0001 #endif /* * [XSI] The values used for l_whence shall be defined as described * in */ -#ifndef SEEK_SET -#define SEEK_SET 0 /* set file offset to offset */ -#define SEEK_CUR 1 /* set file offset to current plus offset */ -#define SEEK_END 2 /* set file offset to EOF plus offset */ -#endif /* !SEEK_SET */ +#include /* * [XSI] The symbolic names for file modes for use as values of mode_t * shall be defined as described in */ -#ifndef S_IFMT -/* File type */ -#define S_IFMT 0170000 /* [XSI] type of file mask */ -#define S_IFIFO 0010000 /* [XSI] named pipe (fifo) */ -#define S_IFCHR 0020000 /* [XSI] character special */ -#define S_IFDIR 0040000 /* [XSI] directory */ -#define S_IFBLK 0060000 /* [XSI] block special */ -#define S_IFREG 0100000 /* [XSI] regular */ -#define S_IFLNK 0120000 /* [XSI] symbolic link */ -#define S_IFSOCK 0140000 /* [XSI] socket */ -#if !defined(_POSIX_C_SOURCE) || defined(_DARWIN_C_SOURCE) -#define S_IFWHT 0160000 /* OBSOLETE: whiteout */ -#endif - -/* File mode */ -/* Read, write, execute/search by owner */ -#define S_IRWXU 0000700 /* [XSI] RWX mask for owner */ -#define S_IRUSR 0000400 /* [XSI] R for owner */ -#define S_IWUSR 0000200 /* [XSI] W for owner */ -#define S_IXUSR 0000100 /* [XSI] X for owner */ -/* Read, write, execute/search by group */ -#define S_IRWXG 0000070 /* [XSI] RWX mask for group */ -#define S_IRGRP 0000040 /* [XSI] R for group */ -#define S_IWGRP 0000020 /* [XSI] W for group */ -#define S_IXGRP 0000010 /* [XSI] X for group */ -/* Read, write, execute/search by others */ -#define S_IRWXO 0000007 /* [XSI] RWX mask for other */ -#define S_IROTH 0000004 /* [XSI] R for other */ -#define S_IWOTH 0000002 /* [XSI] W for other */ -#define S_IXOTH 0000001 /* [XSI] X for other */ - -#define S_ISUID 0004000 /* [XSI] set user id on execution */ -#define S_ISGID 0002000 /* [XSI] set group id on execution */ -#define S_ISVTX 0001000 /* [XSI] directory restrcted delete */ - -#if !defined(_POSIX_C_SOURCE) || defined(_DARWIN_C_SOURCE) -#define S_ISTXT S_ISVTX /* sticky bit: not supported */ -#define S_IREAD S_IRUSR /* backward compatability */ -#define S_IWRITE S_IWUSR /* backward compatability */ -#define S_IEXEC S_IXUSR /* backward compatability */ -#endif -#endif /* !S_IFMT */ +#include #if !defined(_POSIX_C_SOURCE) || defined(_DARWIN_C_SOURCE) /* allocate flags (F_PREALLOCATE) */ @@ -421,6 +439,19 @@ struct flock { short l_whence; /* type of l_start */ }; +#include + +#if __DARWIN_C_LEVEL >= __DARWIN_C_FULL +/* + * Advisory file segment locking with time out - + * Information passed to system by user for F_SETLKWTIMEOUT + */ +struct flocktimeout { + struct flock fl; /* flock passed for file locking */ + struct timespec timeout; /* timespec struct for timeout */ +}; +#endif /* __DARWIN_C_LEVEL >= __DARWIN_C_FULL */ + #if !defined(_POSIX_C_SOURCE) || defined(_DARWIN_C_SOURCE) /* * advisory file read data type - @@ -440,6 +471,51 @@ struct radvisory { #pragma pack() #endif /* KERNEL */ +#ifndef KERNEL +/** Information the user passes in to get the codeblobs out of the kernel */ +typedef struct fcodeblobs { + void *f_cd_hash; + size_t f_hash_size; + void *f_cd_buffer; + size_t f_cd_size; + unsigned int *f_out_size; + int f_arch; + int __padding; +} fcodeblobs_t; +#endif /* KERNEL */ + +#ifdef KERNEL +typedef struct user32_fcodeblobs { + user32_addr_t f_cd_hash; + user32_size_t f_hash_size; + user32_addr_t f_cd_buffer; + user32_size_t f_cd_size; + user32_addr_t f_out_size; + int f_arch; +} user32_fcodeblobs_t; + +/* LP64 version of fcodeblobs */ +typedef struct user64_fcodeblobs { + user64_addr_t f_cd_hash; + user64_size_t f_hash_size; + user64_addr_t f_cd_buffer; + user64_size_t f_cd_size; + user64_addr_t f_out_size; + int f_arch; + int __padding; +} user64_fcodeblobs_t; + +/* kernel version of fcodeblobs */ +typedef struct user_fcodeblobs { + user_addr_t f_cd_hash; + user_size_t f_hash_size; + user_addr_t f_cd_buffer; + user_size_t f_cd_size; + user_addr_t f_out_size; + int f_arch; +} user_fcodeblobs_t; +#endif /* KERNEL */ + /* * detached code signatures data type - * information passed by user to system used by F_ADDSIGS and F_ADDFILESIGS. @@ -452,7 +528,7 @@ typedef struct fsignatures { size_t fs_blob_size; } fsignatures_t; #ifdef KERNEL -/* LP64 version of fsignatures. all pointers +/* LP64 version of fsignatures. all pointers * grow when we're dealing with a 64-bit process. * WARNING - keep in sync with fsignatures */ @@ -472,6 +548,43 @@ typedef struct user_fsignatures { } user_fsignatures_t; #endif /* KERNEL */ +/* + * DYLD needs to check if the object is allowed to be combined + * into the main binary. This is done between the code signature + * is loaded and dyld is doing all the work to process the LOAD commands. + * + * While this could be done in F_ADDFILESIGS.* family the hook into + * the MAC module doesn't say no when LV isn't enabled and then that + * is cached on the vnode, and the MAC module never gets change once + * a process that library validation enabled. + */ +typedef struct fchecklv { + off_t lv_file_start; + size_t lv_error_message_size; + void *lv_error_message; +} fchecklv_t; + +#ifdef KERNEL +/* LP64 version of fchecklv. all pointers + * grow when we're dealing with a 64-bit process. + * WARNING - keep in sync with fsignatures + */ + +typedef struct user32_fchecklv { + user32_off_t lv_file_start; + user32_size_t lv_error_message_size; + user32_addr_t lv_error_message; +} user32_fchecklv_t; + +typedef struct user_fchecklv { + off_t lv_file_start; + user_size_t lv_error_message_size; + user_addr_t lv_error_message; +} user_fchecklv_t; + +#endif /* KERNEL */ + + /* lock operations for flock(2) */ #define LOCK_SH 0x01 /* shared file lock */ #define LOCK_EX 0x02 /* exclusive file lock */ @@ -497,7 +610,7 @@ typedef struct fbootstraptransfer { } fbootstraptransfer_t; #ifdef KERNEL -/* LP64 version of fbootstraptransfer. all pointers +/* LP64 version of fbootstraptransfer. all pointers * grow when we're dealing with a 64-bit process. * WARNING - keep in sync with fbootstraptransfer */ @@ -591,11 +704,9 @@ struct user_fopenfrom { #ifndef KERNEL #if !defined(_POSIX_C_SOURCE) || defined(_DARWIN_C_SOURCE) -#ifndef _FILESEC_T -struct _filesec; -typedef struct _filesec *filesec_t; -#define _FILESEC_T -#endif + +#include + typedef enum { FILESEC_OWNER = 1, FILESEC_GROUP = 2, @@ -615,6 +726,9 @@ typedef enum { __BEGIN_DECLS int open(const char *, int, ...) __DARWIN_ALIAS_C(open); +#if __DARWIN_C_LEVEL >= 200809L +int openat(int, const char *, int, ...) __DARWIN_NOCANCEL(openat) __OSX_AVAILABLE_STARTING(__MAC_10_10, __IPHONE_8_0); +#endif int creat(const char *, mode_t) __DARWIN_ALIAS_C(creat); int fcntl(int, int, ...) __DARWIN_ALIAS_C(fcntl); #if !defined(_POSIX_C_SOURCE) || defined(_DARWIN_C_SOURCE) @@ -637,7 +751,10 @@ int fileport_makeport(int, fileport_t*); int fileport_makefd(fileport_t); #endif /* PRIVATE */ int openx_np(const char *, int, filesec_t); -/* data-protected non-portable open(2) */ +/* + * data-protected non-portable open(2) : + int open_dprotected_np(user_addr_t path, int flags, int class, int dpflags, int mode) + */ int open_dprotected_np ( const char *, int, int, int, ...); int flock(int, int); filesec_t filesec_init(void); diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0cc0f2606..97e79ef6a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -18,23 +18,23 @@ include_directories("${CMAKE_CURRENT_SOURCE_DIR}") add_subdirectory(bootstrap_cmds) add_subdirectory(external/cctools-port/cctools/ld64/src) add_subdirectory(util) -add_subdirectory(libmach-o) -add_subdirectory(libdyld) -add_subdirectory(dyld) +#add_subdirectory(libmach-o) +#add_subdirectory(libdyld) +#add_subdirectory(dyld) if (NOT DARLING_NO_EXECUTABLES) - add_subdirectory(motool) + # add_subdirectory(motool) add_subdirectory(external/darling-dmg) endif (NOT DARLING_NO_EXECUTABLES) -if (FRAMEWORK_COREAUDIO) - add_subdirectory(AudioToolbox) - add_subdirectory(AudioUnit) -endif (FRAMEWORK_COREAUDIO) -if (FRAMEWORK_APPKIT) - add_subdirectory(external/AppKit) -endif (FRAMEWORK_APPKIT) -add_subdirectory(IOKit) +#if (FRAMEWORK_COREAUDIO) +# add_subdirectory(AudioToolbox) +# add_subdirectory(AudioUnit) +#endif (FRAMEWORK_COREAUDIO) +#if (FRAMEWORK_APPKIT) +# add_subdirectory(external/AppKit) +#endif (FRAMEWORK_APPKIT) +#add_subdirectory(IOKit) add_definitions(-target x86_64-apple-darwin11) include(darling_lib) @@ -69,77 +69,79 @@ add_subdirectory(duct/src) add_subdirectory(libresolv) add_subdirectory(libstdcxx) add_subdirectory(libffi) -add_subdirectory(external/libobjc2) +add_subdirectory(dyld-apple) +#add_subdirectory(external/libobjc2) add_subdirectory(external/libkqueue) add_subdirectory(external/libdispatch) -add_subdirectory(external/zlib) -add_subdirectory(external/bzip2) -add_subdirectory(external/libxml2) -add_subdirectory(external/libxslt) -add_subdirectory(external/icu/icuSources) -add_subdirectory(external/corefoundation) -add_subdirectory(external/openssl/src) -add_subdirectory(external/foundation) -add_subdirectory(external/curl) -add_subdirectory(external/liblzma) -add_subdirectory(external/cfnetwork) -add_subdirectory(external/pcre) -add_subdirectory(external/sqlite) -add_subdirectory(external/openpam) -add_subdirectory(CFF) -add_subdirectory(SystemConfiguration) -add_subdirectory(CoreServices) -add_subdirectory(ApplicationServices) -add_subdirectory(VideoDecodeAcceleration) -add_subdirectory(DiskArbitration) -add_subdirectory(DebugSymbols) -add_subdirectory(xar) -add_subdirectory(libedit) +#add_subdirectory(external/zlib) +#add_subdirectory(external/bzip2) +#add_subdirectory(external/libxml2) +#add_subdirectory(external/libxslt) +#add_subdirectory(external/icu/icuSources) +#add_subdirectory(external/corefoundation) +#add_subdirectory(external/openssl/src) +#add_subdirectory(external/foundation) +#add_subdirectory(external/curl) +#add_subdirectory(external/liblzma) +#add_subdirectory(external/cfnetwork) +#add_subdirectory(external/pcre) +#add_subdirectory(external/sqlite) +#add_subdirectory(external/openpam) +#add_subdirectory(CFF) +#add_subdirectory(SystemConfiguration) +#add_subdirectory(CoreServices) +#add_subdirectory(ApplicationServices) +#add_subdirectory(VideoDecodeAcceleration) +#add_subdirectory(DiskArbitration) +#add_subdirectory(DebugSymbols) +#add_subdirectory(xar) +#add_subdirectory(libedit) +add_subdirectory(external/compiler-rt/lib/BlocksRuntime) add_subdirectory(external/compiler-rt/lib/builtins) -add_subdirectory(CommonCrypto) +#add_subdirectory(CommonCrypto) add_subdirectory(csu) -add_subdirectory(external/python/2.6/Python-2.6.9) -add_subdirectory(external/python/2.7/Python-2.7.10) -add_subdirectory(external/ruby) -add_subdirectory(external/expat) -add_subdirectory(external/libauto) -add_subdirectory(external/libarchive/libarchive) -add_subdirectory(external/apr) -add_subdirectory(external/corecrypto) +#add_subdirectory(external/python/2.6/Python-2.6.9) +#add_subdirectory(external/python/2.7/Python-2.7.10) +#add_subdirectory(external/ruby) +#add_subdirectory(external/expat) +#add_subdirectory(external/libauto) +#add_subdirectory(external/libarchive/libarchive) +#add_subdirectory(external/apr) +#add_subdirectory(external/corecrypto) #add_subdirectory(external/security) # work in progress -add_subdirectory(sandbox) -add_subdirectory(Cocoa) -add_subdirectory(external/file/file) -add_subdirectory(external/libxpc) -add_subdirectory(external/openssl_certificates) - -if (NOT DARLING_NO_EXECUTABLES) - add_subdirectory(external/shell_cmds) - add_subdirectory(external/file_cmds) - add_subdirectory(external/text_cmds) - add_subdirectory(external/adv_cmds) - add_subdirectory(external/network_cmds) - add_subdirectory(external/bash) - add_subdirectory(external/less) - add_subdirectory(external/grep) - add_subdirectory(external/awk) - add_subdirectory(external/groff) - add_subdirectory(external/nano) - add_subdirectory(external/man) - add_subdirectory(external/bc) - add_subdirectory(external/zip/zip/zip30) - add_subdirectory(external/zip/unzip/unzip-5.52) - add_subdirectory(tools) - add_subdirectory(external/installer) - add_subdirectory(external/gnutar/gnutar) - add_subdirectory(external/gpatch) - add_subdirectory(external/gnudiff) - add_subdirectory(launchd/src) - add_subdirectory(launchd/support) - add_subdirectory(external/openssh) - add_subdirectory(external/top) - add_subdirectory(external/perl) -endif (NOT DARLING_NO_EXECUTABLES) +#add_subdirectory(sandbox) +#add_subdirectory(Cocoa) +#add_subdirectory(external/file/file) +#add_subdirectory(external/libxpc) +#add_subdirectory(external/openssl_certificates) + +#if (NOT DARLING_NO_EXECUTABLES) +# add_subdirectory(external/shell_cmds) +# add_subdirectory(external/file_cmds) +# add_subdirectory(external/text_cmds) +# add_subdirectory(external/adv_cmds) +# add_subdirectory(external/network_cmds) +# add_subdirectory(external/bash) +# add_subdirectory(external/less) +# add_subdirectory(external/grep) +# add_subdirectory(external/awk) +# add_subdirectory(external/groff) +# add_subdirectory(external/nano) +# add_subdirectory(external/man) +# add_subdirectory(external/bc) +# add_subdirectory(external/zip/zip/zip30) +# add_subdirectory(external/zip/unzip/unzip-5.52) +# add_subdirectory(tools) +# add_subdirectory(external/installer) +# add_subdirectory(external/gnutar/gnutar) +# add_subdirectory(external/gpatch) +# add_subdirectory(external/gnudiff) +# add_subdirectory(launchd/src) +# add_subdirectory(launchd/support) +# add_subdirectory(external/openssh) +# add_subdirectory(external/top) +# add_subdirectory(external/perl) +#endif (NOT DARLING_NO_EXECUTABLES) ###################### # libc++ & libc++abi # diff --git a/src/copyfile/CMakeLists.txt b/src/copyfile/CMakeLists.txt index 374c11770..206597236 100644 --- a/src/copyfile/CMakeLists.txt +++ b/src/copyfile/CMakeLists.txt @@ -3,7 +3,7 @@ project(copyfile) cmake_minimum_required(VERSION 2.4.0) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -nostdinc -fblocks") -set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -nostdlib -Wl,--version-script=${DARLING_TOP_DIRECTORY}/darwin.map") +set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -nostdlib") include_directories(${DARLING_TOP_DIRECTORY}/platform-include) include_directories(${DARLING_TOP_DIRECTORY}/src/libinfo/membership.subproj/) @@ -25,11 +25,9 @@ set(copyfile_sources # xattr_flags.c # Doesn't build yet (XPC) ) -SET(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/darling") -SET(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) -SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) - -add_library(system_copyfile SHARED ${copyfile_sources}) -target_link_libraries(system_copyfile system_kernel system_c) +set(DYLIB_INSTALL_NAME "/usr/lib/system/libcopyfile.dylib") +add_darling_library(system_copyfile SHARED ${copyfile_sources}) +set_target_properties(system_copyfile PROPERTIES OUTPUT_NAME "copyfile") +target_link_libraries(system_copyfile PRIVATE system_kernel system_c system_blocks system_info system_dyld) install(TARGETS system_copyfile DESTINATION ${CMAKE_INSTALL_LIBDIR}/darling) diff --git a/src/csu/CMakeLists.txt b/src/csu/CMakeLists.txt index b0ca6c936..40c45f2a2 100644 --- a/src/csu/CMakeLists.txt +++ b/src/csu/CMakeLists.txt @@ -13,12 +13,11 @@ add_definitions(-D__APPLE__ -D__DYNAMIC__) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -nostdinc -D__DARWIN_UNIX03 -fPIC -w") -include_directories(${DARLING_TOP_DIRECTORY}/src/libc/include/FreeBSD) +#include_directories(${DARLING_TOP_DIRECTORY}/src/libc/include/FreeBSD) set(csu_SRCS - crt.c - start.S + crt1.10.6.S ) -add_library(csu STATIC ${csu_SRCS}) +add_darling_library(csu STATIC ${csu_SRCS}) diff --git a/src/csu/crt1.10.6.S b/src/csu/crt1.10.6.S new file mode 120000 index 000000000..d015f4baf --- /dev/null +++ b/src/csu/crt1.10.6.S @@ -0,0 +1 @@ +start.S \ No newline at end of file diff --git a/src/csu/start.S b/src/csu/start.S index 8bf0f6994..455df955c 100644 --- a/src/csu/start.S +++ b/src/csu/start.S @@ -25,11 +25,6 @@ #include -#ifdef DARLING -# define start _start -# define _main main -#endif - #if __ppc__ && __DYNAMIC__ // // Force stub section next to __text section to minimize chance that @@ -187,7 +182,7 @@ Lapple2:cmpq $0,(%rcx) # look for NULL ending env[] array add $8,%rcx # once found, next pointer is "apple" parameter now in %rcx call _main movl %eax,%edi # pass result from main() to exit() - call _exit@PLT # need to use call to keep stack aligned + call _exit # need to use call to keep stack aligned hlt #endif #endif // __x86_64__ diff --git a/src/duct/include/CrashReporterClient.h b/src/duct/include/CrashReporterClient.h index 8cb2b3dc9..37254f8af 100644 --- a/src/duct/include/CrashReporterClient.h +++ b/src/duct/include/CrashReporterClient.h @@ -1 +1,15 @@ -inline void CRSetCrashLogMessage(const char* text) {} +#ifndef CRASHREPORTERCLIENT_H_ +#define CRASHREPORTERCLIENT_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +void CRSetCrashLogMessage(const char* text); +void CRSetCrashLogMessage2(const char* path); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/duct/src/CMakeLists.txt b/src/duct/src/CMakeLists.txt index 9ad34fae1..92b99bdf1 100644 --- a/src/duct/src/CMakeLists.txt +++ b/src/duct/src/CMakeLists.txt @@ -10,10 +10,7 @@ add_definitions(-D__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__=1080) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -nostdinc -D__DARWIN_UNIX03 -fPIC -w") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -nostdlib -Wl,-flat_namespace -Wl,-undefined,suppress") -SET(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/darling") -SET(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) -SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) - +set(DYLIB_INSTALL_NAME "/usr/lib/system/libsystem_duct.dylib") add_darling_library(system_duct SHARED commpage.c libnotify.c numcpus.c CRGetCrashLogMessage.c acl.c bootstrap.c dyld.c dns_sd.c sa_dst_compare.c asl.c) diff --git a/src/duct/src/CRGetCrashLogMessage.c b/src/duct/src/CRGetCrashLogMessage.c index 7b72bc9e1..e457e44a1 100644 --- a/src/duct/src/CRGetCrashLogMessage.c +++ b/src/duct/src/CRGetCrashLogMessage.c @@ -7,3 +7,6 @@ void CRSetCrashLogMessage(const char* msg) { } +void CRSetCrashLogMessage2(const char* path) +{ +} diff --git a/src/dyld-apple/.clang-format b/src/dyld-apple/.clang-format new file mode 100644 index 000000000..1d7b0fd39 --- /dev/null +++ b/src/dyld-apple/.clang-format @@ -0,0 +1,13 @@ +Language: Cpp +BasedOnStyle: WebKit + +AlignConsecutiveDeclarations: true +AlignOperands: false +AlignTrailingComments: true +IndentWidth: 4 + +Standard: Cpp11 + +UseTab: Never + +SortIncludes: false diff --git a/src/dyld-apple/APPLE_LICENSE b/src/dyld-apple/APPLE_LICENSE new file mode 100644 index 000000000..fe81a60ca --- /dev/null +++ b/src/dyld-apple/APPLE_LICENSE @@ -0,0 +1,367 @@ +APPLE PUBLIC SOURCE LICENSE +Version 2.0 - August 6, 2003 + +Please read this License carefully before downloading this software. +By downloading or using this software, you are agreeing to be bound by +the terms of this License. If you do not or cannot agree to the terms +of this License, please do not download or use the software. + +1. General; Definitions. This License applies to any program or other +work which Apple Computer, Inc. ("Apple") makes publicly available and +which contains a notice placed by Apple identifying such program or +work as "Original Code" and stating that it is subject to the terms of +this Apple Public Source License version 2.0 ("License"). As used in +this License: + +1.1 "Applicable Patent Rights" mean: (a) in the case where Apple is +the grantor of rights, (i) claims of patents that are now or hereafter +acquired, owned by or assigned to Apple and (ii) that cover subject +matter contained in the Original Code, but only to the extent +necessary to use, reproduce and/or distribute the Original Code +without infringement; and (b) in the case where You are the grantor of +rights, (i) claims of patents that are now or hereafter acquired, +owned by or assigned to You and (ii) that cover subject matter in Your +Modifications, taken alone or in combination with Original Code. + +1.2 "Contributor" means any person or entity that creates or +contributes to the creation of Modifications. + +1.3 "Covered Code" means the Original Code, Modifications, the +combination of Original Code and any Modifications, and/or any +respective portions thereof. + +1.4 "Externally Deploy" means: (a) to sublicense, distribute or +otherwise make Covered Code available, directly or indirectly, to +anyone other than You; and/or (b) to use Covered Code, alone or as +part of a Larger Work, in any way to provide a service, including but +not limited to delivery of content, through electronic communication +with a client other than You. + +1.5 "Larger Work" means a work which combines Covered Code or portions +thereof with code not governed by the terms of this License. + +1.6 "Modifications" mean any addition to, deletion from, and/or change +to, the substance and/or structure of the Original Code, any previous +Modifications, the combination of Original Code and any previous +Modifications, and/or any respective portions thereof. When code is +released as a series of files, a Modification is: (a) any addition to +or deletion from the contents of a file containing Covered Code; +and/or (b) any new file or other representation of computer program +statements that contains any part of Covered Code. + +1.7 "Original Code" means (a) the Source Code of a program or other +work as originally made available by Apple under this License, +including the Source Code of any updates or upgrades to such programs +or works made available by Apple under this License, and that has been +expressly identified by Apple as such in the header file(s) of such +work; and (b) the object code compiled from such Source Code and +originally made available by Apple under this License. + +1.8 "Source Code" means the human readable form of a program or other +work that is suitable for making modifications to it, including all +modules it contains, plus any associated interface definition files, +scripts used to control compilation and installation of an executable +(object code). + +1.9 "You" or "Your" means an individual or a legal entity exercising +rights under this License. For legal entities, "You" or "Your" +includes any entity which controls, is controlled by, or is under +common control with, You, where "control" means (a) the power, direct +or indirect, to cause the direction or management of such entity, +whether by contract or otherwise, or (b) ownership of fifty percent +(50%) or more of the outstanding shares or beneficial ownership of +such entity. + +2. Permitted Uses; Conditions & Restrictions. Subject to the terms +and conditions of this License, Apple hereby grants You, effective on +the date You accept this License and download the Original Code, a +world-wide, royalty-free, non-exclusive license, to the extent of +Apple's Applicable Patent Rights and copyrights covering the Original +Code, to do the following: + +2.1 Unmodified Code. You may use, reproduce, display, perform, +internally distribute within Your organization, and Externally Deploy +verbatim, unmodified copies of the Original Code, for commercial or +non-commercial purposes, provided that in each instance: + +(a) You must retain and reproduce in all copies of Original Code the +copyright and other proprietary notices and disclaimers of Apple as +they appear in the Original Code, and keep intact all notices in the +Original Code that refer to this License; and + +(b) You must include a copy of this License with every copy of Source +Code of Covered Code and documentation You distribute or Externally +Deploy, and You may not offer or impose any terms on such Source Code +that alter or restrict this License or the recipients' rights +hereunder, except as permitted under Section 6. + +2.2 Modified Code. You may modify Covered Code and use, reproduce, +display, perform, internally distribute within Your organization, and +Externally Deploy Your Modifications and Covered Code, for commercial +or non-commercial purposes, provided that in each instance You also +meet all of these conditions: + +(a) You must satisfy all the conditions of Section 2.1 with respect to +the Source Code of the Covered Code; + +(b) You must duplicate, to the extent it does not already exist, the +notice in Exhibit A in each file of the Source Code of all Your +Modifications, and cause the modified files to carry prominent notices +stating that You changed the files and the date of any change; and + +(c) If You Externally Deploy Your Modifications, You must make +Source Code of all Your Externally Deployed Modifications either +available to those to whom You have Externally Deployed Your +Modifications, or publicly available. Source Code of Your Externally +Deployed Modifications must be released under the terms set forth in +this License, including the license grants set forth in Section 3 +below, for as long as you Externally Deploy the Covered Code or twelve +(12) months from the date of initial External Deployment, whichever is +longer. You should preferably distribute the Source Code of Your +Externally Deployed Modifications electronically (e.g. download from a +web site). + +2.3 Distribution of Executable Versions. In addition, if You +Externally Deploy Covered Code (Original Code and/or Modifications) in +object code, executable form only, You must include a prominent +notice, in the code itself as well as in related documentation, +stating that Source Code of the Covered Code is available under the +terms of this License with information on how and where to obtain such +Source Code. + +2.4 Third Party Rights. You expressly acknowledge and agree that +although Apple and each Contributor grants the licenses to their +respective portions of the Covered Code set forth herein, no +assurances are provided by Apple or any Contributor that the Covered +Code does not infringe the patent or other intellectual property +rights of any other entity. Apple and each Contributor disclaim any +liability to You for claims brought by any other entity based on +infringement of intellectual property rights or otherwise. As a +condition to exercising the rights and licenses granted hereunder, You +hereby assume sole responsibility to secure any other intellectual +property rights needed, if any. For example, if a third party patent +license is required to allow You to distribute the Covered Code, it is +Your responsibility to acquire that license before distributing the +Covered Code. + +3. Your Grants. In consideration of, and as a condition to, the +licenses granted to You under this License, You hereby grant to any +person or entity receiving or distributing Covered Code under this +License a non-exclusive, royalty-free, perpetual, irrevocable license, +under Your Applicable Patent Rights and other intellectual property +rights (other than patent) owned or controlled by You, to use, +reproduce, display, perform, modify, sublicense, distribute and +Externally Deploy Your Modifications of the same scope and extent as +Apple's licenses under Sections 2.1 and 2.2 above. + +4. Larger Works. You may create a Larger Work by combining Covered +Code with other code not governed by the terms of this License and +distribute the Larger Work as a single product. In each such instance, +You must make sure the requirements of this License are fulfilled for +the Covered Code or any portion thereof. + +5. Limitations on Patent License. Except as expressly stated in +Section 2, no other patent rights, express or implied, are granted by +Apple herein. Modifications and/or Larger Works may require additional +patent licenses from Apple which Apple may grant in its sole +discretion. + +6. Additional Terms. You may choose to offer, and to charge a fee for, +warranty, support, indemnity or liability obligations and/or other +rights consistent with the scope of the license granted herein +("Additional Terms") to one or more recipients of Covered Code. +However, You may do so only on Your own behalf and as Your sole +responsibility, and not on behalf of Apple or any Contributor. You +must obtain the recipient's agreement that any such Additional Terms +are offered by You alone, and You hereby agree to indemnify, defend +and hold Apple and every Contributor harmless for any liability +incurred by or claims asserted against Apple or such Contributor by +reason of any such Additional Terms. + +7. Versions of the License. Apple may publish revised and/or new +versions of this License from time to time. Each version will be given +a distinguishing version number. Once Original Code has been published +under a particular version of this License, You may continue to use it +under the terms of that version. You may also choose to use such +Original Code under the terms of any subsequent version of this +License published by Apple. No one other than Apple has the right to +modify the terms applicable to Covered Code created under this +License. + +8. NO WARRANTY OR SUPPORT. The Covered Code may contain in whole or in +part pre-release, untested, or not fully tested works. The Covered +Code may contain errors that could cause failures or loss of data, and +may be incomplete or contain inaccuracies. You expressly acknowledge +and agree that use of the Covered Code, or any portion thereof, is at +Your sole and entire risk. THE COVERED CODE IS PROVIDED "AS IS" AND +WITHOUT WARRANTY, UPGRADES OR SUPPORT OF ANY KIND AND APPLE AND +APPLE'S LICENSOR(S) (COLLECTIVELY REFERRED TO AS "APPLE" FOR THE +PURPOSES OF SECTIONS 8 AND 9) AND ALL CONTRIBUTORS EXPRESSLY DISCLAIM +ALL WARRANTIES AND/OR CONDITIONS, EXPRESS OR IMPLIED, INCLUDING, BUT +NOT LIMITED TO, THE IMPLIED WARRANTIES AND/OR CONDITIONS OF +MERCHANTABILITY, OF SATISFACTORY QUALITY, OF FITNESS FOR A PARTICULAR +PURPOSE, OF ACCURACY, OF QUIET ENJOYMENT, AND NONINFRINGEMENT OF THIRD +PARTY RIGHTS. APPLE AND EACH CONTRIBUTOR DOES NOT WARRANT AGAINST +INTERFERENCE WITH YOUR ENJOYMENT OF THE COVERED CODE, THAT THE +FUNCTIONS CONTAINED IN THE COVERED CODE WILL MEET YOUR REQUIREMENTS, +THAT THE OPERATION OF THE COVERED CODE WILL BE UNINTERRUPTED OR +ERROR-FREE, OR THAT DEFECTS IN THE COVERED CODE WILL BE CORRECTED. NO +ORAL OR WRITTEN INFORMATION OR ADVICE GIVEN BY APPLE, AN APPLE +AUTHORIZED REPRESENTATIVE OR ANY CONTRIBUTOR SHALL CREATE A WARRANTY. +You acknowledge that the Covered Code is not intended for use in the +operation of nuclear facilities, aircraft navigation, communication +systems, or air traffic control machines in which case the failure of +the Covered Code could lead to death, personal injury, or severe +physical or environmental damage. + +9. LIMITATION OF LIABILITY. TO THE EXTENT NOT PROHIBITED BY LAW, IN NO +EVENT SHALL APPLE OR ANY CONTRIBUTOR BE LIABLE FOR ANY INCIDENTAL, +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR RELATING +TO THIS LICENSE OR YOUR USE OR INABILITY TO USE THE COVERED CODE, OR +ANY PORTION THEREOF, WHETHER UNDER A THEORY OF CONTRACT, WARRANTY, +TORT (INCLUDING NEGLIGENCE), PRODUCTS LIABILITY OR OTHERWISE, EVEN IF +APPLE OR SUCH CONTRIBUTOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES AND NOTWITHSTANDING THE FAILURE OF ESSENTIAL PURPOSE OF ANY +REMEDY. SOME JURISDICTIONS DO NOT ALLOW THE LIMITATION OF LIABILITY OF +INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS LIMITATION MAY NOT APPLY +TO YOU. In no event shall Apple's total liability to You for all +damages (other than as may be required by applicable law) under this +License exceed the amount of fifty dollars ($50.00). + +10. Trademarks. This License does not grant any rights to use the +trademarks or trade names "Apple", "Apple Computer", "Mac", "Mac OS", +"QuickTime", "QuickTime Streaming Server" or any other trademarks, +service marks, logos or trade names belonging to Apple (collectively +"Apple Marks") or to any trademark, service mark, logo or trade name +belonging to any Contributor. You agree not to use any Apple Marks in +or as part of the name of products derived from the Original Code or +to endorse or promote products derived from the Original Code other +than as expressly permitted by and in strict compliance at all times +with Apple's third party trademark usage guidelines which are posted +at http://www.apple.com/legal/guidelinesfor3rdparties.html. + +11. Ownership. Subject to the licenses granted under this License, +each Contributor retains all rights, title and interest in and to any +Modifications made by such Contributor. Apple retains all rights, +title and interest in and to the Original Code and any Modifications +made by or on behalf of Apple ("Apple Modifications"), and such Apple +Modifications will not be automatically subject to this License. Apple +may, at its sole discretion, choose to license such Apple +Modifications under this License, or on different terms from those +contained in this License or may choose not to license them at all. + +12. Termination. + +12.1 Termination. This License and the rights granted hereunder will +terminate: + +(a) automatically without notice from Apple if You fail to comply with +any term(s) of this License and fail to cure such breach within 30 +days of becoming aware of such breach; + +(b) immediately in the event of the circumstances described in Section +13.5(b); or + +(c) automatically without notice from Apple if You, at any time during +the term of this License, commence an action for patent infringement +against Apple; provided that Apple did not first commence +an action for patent infringement against You in that instance. + +12.2 Effect of Termination. Upon termination, You agree to immediately +stop any further use, reproduction, modification, sublicensing and +distribution of the Covered Code. All sublicenses to the Covered Code +which have been properly granted prior to termination shall survive +any termination of this License. Provisions which, by their nature, +should remain in effect beyond the termination of this License shall +survive, including but not limited to Sections 3, 5, 8, 9, 10, 11, +12.2 and 13. No party will be liable to any other for compensation, +indemnity or damages of any sort solely as a result of terminating +this License in accordance with its terms, and termination of this +License will be without prejudice to any other right or remedy of +any party. + +13. Miscellaneous. + +13.1 Government End Users. The Covered Code is a "commercial item" as +defined in FAR 2.101. Government software and technical data rights in +the Covered Code include only those rights customarily provided to the +public as defined in this License. This customary commercial license +in technical data and software is provided in accordance with FAR +12.211 (Technical Data) and 12.212 (Computer Software) and, for +Department of Defense purchases, DFAR 252.227-7015 (Technical Data -- +Commercial Items) and 227.7202-3 (Rights in Commercial Computer +Software or Computer Software Documentation). Accordingly, all U.S. +Government End Users acquire Covered Code with only those rights set +forth herein. + +13.2 Relationship of Parties. This License will not be construed as +creating an agency, partnership, joint venture or any other form of +legal association between or among You, Apple or any Contributor, and +You will not represent to the contrary, whether expressly, by +implication, appearance or otherwise. + +13.3 Independent Development. Nothing in this License will impair +Apple's right to acquire, license, develop, have others develop for +it, market and/or distribute technology or products that perform the +same or similar functions as, or otherwise compete with, +Modifications, Larger Works, technology or products that You may +develop, produce, market or distribute. + +13.4 Waiver; Construction. Failure by Apple or any Contributor to +enforce any provision of this License will not be deemed a waiver of +future enforcement of that or any other provision. Any law or +regulation which provides that the language of a contract shall be +construed against the drafter will not apply to this License. + +13.5 Severability. (a) If for any reason a court of competent +jurisdiction finds any provision of this License, or portion thereof, +to be unenforceable, that provision of the License will be enforced to +the maximum extent permissible so as to effect the economic benefits +and intent of the parties, and the remainder of this License will +continue in full force and effect. (b) Notwithstanding the foregoing, +if applicable law prohibits or restricts You from fully and/or +specifically complying with Sections 2 and/or 3 or prevents the +enforceability of either of those Sections, this License will +immediately terminate and You must immediately discontinue any use of +the Covered Code and destroy all copies of it that are in your +possession or control. + +13.6 Dispute Resolution. Any litigation or other dispute resolution +between You and Apple relating to this License shall take place in the +Northern District of California, and You and Apple hereby consent to +the personal jurisdiction of, and venue in, the state and federal +courts within that District with respect to this License. The +application of the United Nations Convention on Contracts for the +International Sale of Goods is expressly excluded. + +13.7 Entire Agreement; Governing Law. This License constitutes the +entire agreement between the parties with respect to the subject +matter hereof. This License shall be governed by the laws of the +United States and the State of California, except that body of +California law concerning conflicts of law. + +Where You are located in the province of Quebec, Canada, the following +clause applies: The parties hereby confirm that they have requested +that this License and all related documents be drafted in English. Les +parties ont exige que le present contrat et tous les documents +connexes soient rediges en anglais. + +EXHIBIT A. + +"Portions Copyright (c) 1999-2003 Apple Computer, Inc. All Rights +Reserved. + +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." diff --git a/src/dyld-apple/CMakeLists.txt b/src/dyld-apple/CMakeLists.txt new file mode 100644 index 000000000..39663e4d9 --- /dev/null +++ b/src/dyld-apple/CMakeLists.txt @@ -0,0 +1,70 @@ +project(dyld-apple) + +cmake_minimum_required(VERSION 2.4.0) + +if(COMMAND cmake_policy) + cmake_policy(SET CMP0003 NEW) +endif(COMMAND cmake_policy) + +include (darling_exe) + +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -nostdinc") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -nostdinc -std=c++11") +set(CMAKE_SHARED_LINKER_FLAGS_SAVED "${CMAKE_SHARED_LINKER_FLAGS}") +set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -nostdlib -Wl,-flat_namespace -Wl,-undefined,suppress") + +add_definitions(-DTARGET_OS_MAC=1) +add_definitions(-DHAVE_STDINT_H=1) +add_definitions(-D__APPLE__ -D__DYNAMIC__) + +include_directories(${CMAKE_SOURCE_DIR}/src/launchd/liblaunch) +include_directories(${CMAKE_SOURCE_DIR}/src/libc/darwin) +include_directories(${CMAKE_SOURCE_DIR}/src/libc/gen) +include_directories(${CMAKE_SOURCE_DIR}/src ${CMAKE_SOURCE_DIR}/src/sandbox) +include_directories(${CMAKE_SOURCE_DIR}/src/external/libcxx/include) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/launch-cache) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}) +include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR}/include + ${CMAKE_CURRENT_SOURCE_DIR}/include/mach-o) + +set(DYLIB_INSTALL_NAME "/usr/lib/system/libdyld.dylib") +add_darling_library(system_dyld SHARED + src/dyldAPIsInLibSystem.cpp + src/dyld_stub_binder.S + src/dyldLibSystemGlue.c + src/threadLocalHelpers.S + src/dyldLock.cpp + src/threadLocalVariables.c + src/start_glue.S +) +set_target_properties(system_dyld PROPERTIES + OUTPUT_NAME "dyld" + COMPILE_FLAGS "-fno-exceptions -fno-rtti") +target_link_libraries(system_dyld PRIVATE system_c system_kernel compiler_rt launch) + +set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS_SAVED} -nostdlib") + +add_darling_executable(system_loader + src/dyldAPIs.cpp + src/dyld.cpp + src/dyld_gdb.cpp + src/dyldInitialization.cpp + src/dyldNew.cpp + src/dyld_process_info.cpp + src/dyld_process_info_notify.cpp + src/ImageLoader.cpp + src/ImageLoaderMachOClassic.cpp + src/ImageLoaderMachOCompressed.cpp + src/ImageLoaderMachO.cpp + src/ImageLoaderMegaDylib.cpp + src/dyldExceptions.c + src/glue.c + src/dyldStartup.S +) + +#use_ld64(system_loader) +set_target_properties(system_loader PROPERTIES OUTPUT_NAME "dyld") +set_property(TARGET system_loader APPEND_STRING PROPERTY LINK_FLAGS " -dylinker") +target_link_libraries(system_loader crt1.10.6.o) + +install(TARGETS system_dyld DESTINATION ${CMAKE_INSTALL_LIBDIR}/darling) diff --git a/src/dyld-apple/System b/src/dyld-apple/System new file mode 120000 index 000000000..d4cd652a5 --- /dev/null +++ b/src/dyld-apple/System @@ -0,0 +1 @@ +../../platform-include/ \ No newline at end of file diff --git a/src/dyld-apple/bin/set-alt-dyld b/src/dyld-apple/bin/set-alt-dyld new file mode 100755 index 000000000..19ae128ec --- /dev/null +++ b/src/dyld-apple/bin/set-alt-dyld @@ -0,0 +1,30 @@ +#!/usr/bin/perl -w + +use strict; +undef $/; + +if(@ARGV == 0) +{ + print "Usage: $0 [ ...]\n"; + exit 1; +} + +my $arg; +foreach $arg (@ARGV) +{ + open IN, "<$arg" or die $!; + my $in = ; + close IN or die $!; + + if($in =~ s{/usr/lib/dyld}{/usr/local/dy}) + { + open OUT, ">$arg" or die $!; + print OUT $in; + close OUT or die $!; + } + else + { + print STDERR "ERROR: $arg\n"; + exit 1; + } +} diff --git a/src/dyld-apple/configs/base.xcconfig b/src/dyld-apple/configs/base.xcconfig new file mode 100644 index 000000000..e69de29bb diff --git a/src/dyld-apple/configs/dyld.xcconfig b/src/dyld-apple/configs/dyld.xcconfig new file mode 100644 index 000000000..13863b843 --- /dev/null +++ b/src/dyld-apple/configs/dyld.xcconfig @@ -0,0 +1,15 @@ +ALIGNMENT[arch=armv7s] = -Wl,-segalign,0x4000 + +ENTRY[sdk=*simulator*] = -Wl,-e,_start_sim +ENTRY[sdk=iphoneos*] = -Wl,-e,__dyld_start +ENTRY[sdk=macosx*] = -Wl,-e,__dyld_start + +EXPORTED_SYMBOLS_FILE[sdk=*simulator*] = $(SRCROOT)/src/dyld_sim.exp +EXPORTED_SYMBOLS_FILE[sdk=iphoneos*] = $(SRCROOT)/src/dyld.exp +EXPORTED_SYMBOLS_FILE[sdk=macosx*] = $(SRCROOT)/src/dyld.exp + +PRODUCT_NAME[sdk=*simulator*] = dyld_sim +PRODUCT_NAME[sdk=iphoneos*] = dyld +PRODUCT_NAME[sdk=macosx*] = dyld + +INSTALL_PATH = /usr/lib diff --git a/src/dyld-apple/configs/libdyld.xcconfig b/src/dyld-apple/configs/libdyld.xcconfig new file mode 100644 index 000000000..d3c6d1cc7 --- /dev/null +++ b/src/dyld-apple/configs/libdyld.xcconfig @@ -0,0 +1,8 @@ + +LIBSYSTEM_LIBS[sdk=*simulator*] = -Wl,-upward-lsystem_sim_platform -Wl,-upward-lsystem_malloc -Wl,-upward-lsystem_c -Wl,-upward-lsystem_sim_pthread -Wl,-upward-lxpc -Wl,-upward-lsystem_blocks -Wl,-upward-lsystem_sim_kernel -Wl,-upward-lsystem_sandbox -Wl,-upward-ldispatch +LIBSYSTEM_LIBS[sdk=iphoneos*] = -Wl,-upward-lsystem_platform -Wl,-upward-lsystem_malloc -Wl,-upward-lsystem_c -Wl,-upward-lsystem_pthread -Wl,-upward-lxpc -Wl,-upward-lsystem_blocks -Wl,-upward-lsystem_kernel -Wl,-upward-lsystem_sandbox -Wl,-upward-ldispatch +LIBSYSTEM_LIBS[sdk=macosx*] = -Wl,-upward-lsystem_platform -Wl,-upward-lsystem_malloc -Wl,-upward-lsystem_c -Wl,-upward-lsystem_pthread -Wl,-upward-lxpc -Wl,-upward-lsystem_blocks -Wl,-upward-lsystem_kernel -Wl,-upward-lsystem_sandbox -Wl,-upward-ldispatch + +INSTALL_PATH = /usr/lib/system + + diff --git a/src/dyld-apple/configs/update_dyld_shared_cache.xcconfig b/src/dyld-apple/configs/update_dyld_shared_cache.xcconfig new file mode 100644 index 000000000..1bed8641f --- /dev/null +++ b/src/dyld-apple/configs/update_dyld_shared_cache.xcconfig @@ -0,0 +1,18 @@ + +//:configuration = Release +LOCAL_YES = local + +//:configuration = Release +LOCAL = $(LOCAL_$(RC_PURPLE)) + +//:configuration = Release +INSTALL_PATH = $(INSTALL_LOCATION)/usr/$(LOCAL)/bin + +// don't iOS tool +MY_RELEASE_CODE_SIGN_IDENTITY_YES = +MY_RELEASE_CODE_SIGN_IDENTITY_ = - +CODE_SIGN_IDENTITY = $(MY_RELEASE_CODE_SIGN_IDENTITY_$(RC_PURPLE)) + + +CODE_SIGN_ENTITLEMENTS = launch-cache/update_dyld_shared_cache_entitlements.plist + diff --git a/src/dyld-apple/doc/ReleaseNotes.txt b/src/dyld-apple/doc/ReleaseNotes.txt new file mode 100644 index 000000000..18d5f8123 --- /dev/null +++ b/src/dyld-apple/doc/ReleaseNotes.txt @@ -0,0 +1,1771 @@ + +dyld-208 + Correct @path to still work with symlinks + +dyld-207 + dyld should give a better error message if you try to use a newer binary on older OS + backtraces of LC_MAIN binaries end in tlv_get_addr+136, instead libdyld should export "_start" symbol + Fix up @rpath based paths during introspection + Interposition tuples are not respected when binding with resolvers + +dyld-206 + Give some warning that DYLD_ env vars are disabled + +dyld-205 + Add dyld to uuidArray to enable symbolication of stackshots + +dyld-204.1 + fix initializer ordering of upwardly linked dylibs + +dyld-204 + dyld misreads very large addends + Perform method list sort even if __DATA,__objc_opt_rw is absent + Remove category attaching from dyld shared cache + Shared cache method list sorting breaks protocol extended type encodings + +dyld-203.1 + initializer not run in only upwardly linked dylib + +dyld-203 + update_dyld_shared_cache does not build with libc++ + dyld does not build with libc++ + Add functions to get min OS and sdk versions program was linked with + DYLD_FRAMEWORK_PATH not mentioned in dlopen() man page + dsc_extractor not updating LC_FUNCTION_STARTS + +dyld-202 + Get rid of crt1.o and jump straight into main() from dyld + image denied loading by gc_enforcer is left in dyld_all_images_info array + +dyld-201 + Use spin lock to guard sAllImageInfos + +dyld-200.3 + genCaches fails: "header size miscalculation 0x00006000" + +dyld-200.2 + ARCH_NAME for armv7k is defined as "armv7s" + dsc_iterator.cpp needs cases for v7 variants + +dyld-200 + dyld fails to build for armv7s + update_dyld_shared_cache should accept an 'overlay' along with a 'root' directory option +Remove PowerPC support + +-------------- +dyld-199.5 (iOS 5) + Update initial image list size + +dyld-199.4 + genCaches fails: header size miscalculation + +dyld-199.3 + Repair ivar offsets in dyld shared cache + +dyld-199.2 + improve Xcode upload of iOS dylibs + +dyld-199.1 + correctly adjust ARM movw when in dyld shared cache + +dyld-199 + update_dyld_shared_cache requires weak-linked frameworks to be present at shared cache generation time + Remove armv7 variants from dyld + +dyld-198.1 + back out previous change DYLD_XXX restrict check + +dyld-198 + corrupt load commands can pass sniff test if 32-bit wraps around + dyld should restrict DYLD_XXX debugging facilities + dyld should not allow loading of dylibs without valid signatures +enable DYLD_PRINT_REBASINGS + +dyld-197 + dyld should range check the fixup location of all bindings + range check initializers and terminators to be within image + +dyld-196.2 + dyld should support new armv7 variants + +dyld-196.1 + support re-exported symbols from re-exported libraries + +dyld-196 + movw/movt don't work in dyld shared cache + + +--------------------- +dyld-195.3 (Mac OS X 10.7.0) + update_dyld_shared_cache missed libstdc++? + i386 dyld shared cache overflows after adding libclh.dylib + +dyld-195.2 + spurious warning about embedded framework not being put in dyld shared cache + +dyld-195 + C++ 0x thread_local destructor support + update_dyld_shared_cache -verify finds differences + more verbose messages when vm_protect() fails + +dyld-194.1 +Fix uses of libc++abi-static.a + +dyld-194 + make interposing available to open source + __thread implementation doesn't preserve enough registers (ABI issue) + clang built update_dyld_shared_cache fails at runtime + +dyld-193 + dyld should honor CS_RESTRICT in _main like hasRestrictedSegment + update_dyld_shared_cache -overlay optimizations + +dyld-192.1 + dyld has NULL paths in image info array + +dyld-192 + dyld's map+slide should be an all or nothing operation + +dyld-191.3 + overriding shared cache dylibs with resolvers fails + +dyld-191.2 + dyld_stub_binder called repeatedly for $VARIANT$ functions + update_dyld_shared_cache crash when fat dylib truncated + +dyld-191.1 + update_dyld_shared_cache is failing + +dyld-191 + ASLR/PIE: dyld's "random padding" algorithm is deterministic + race condition with flat-namespace lazy binding + libdyld fails to build with clang-127 + +dyld-190.1 +remove libdyld.a target + +dyld-190 + _NSGetExecutablePath documentation is ambiguous about bufsize + 11A315: LC_DYLD_ENVIRONMENT does not expand @executable_path + dyld should avoid arc4random for dylib randomization + tune trie traversal code for 10.7 compiler + breaking libSystem into dylibs slows down dyld binding, shared cache should compensate + update_dyld_shared_cache should not be built no-pie + +dyld-189 + dyld(1) man page feedback + LC_DYLD_ENVIRONMENT should still be honored with __RESTRICT,__restrict section present + 11A307: DYLD_SHARED_REGION=private crashes + don't slide shared cache if ASLR disabled (main executable didn't slide) + +dyld-188 + Dynamic linking memory usage seems unusually high for trivial programs + +dyld-187 + madvise MADV_FREE calls for LINKEDIT are failing in dyld during launch in iOS + dyld allImageInfos messed up with duplicate IOKit + +dyld-186 + dyld: Pick up changes from libsystem_kernel reorganisation in dyld's build. + Shared Region Base Should Slide at Boot Time + Multiple /dev/urandom accesses on every process exec + +dyld-185 + text relocs don't work if only S_ATTR_EXT_RELOC is used + DYLD_FORCE_FLAT_NAMESPACE=YES causes process to spin in DYLD-STUB$$bzero + +dyld-184 + Add cache slide value to dyld_all_image_infos + +dyld-183.1 + dyld proper does not need dyld_stub_binder.o + +dyld-183 + dyld needs to call new kernel interface to register metadata + ASLR side build causes 15-30% regression in most launch times + Support version-checked framework/library override paths + Support LC_DYLD_ENVIRONMENT load command + function starts info should be copied into dyld shared cache + +dyld-182 + Merge Jasper dyld changes to trunk + +dyld-181.1 + 11A270: FNPLicensingService crashes + +dyld-181 + 11A238a: AutoDesk AutoCAD beta 4 crashes on launch (dyld) + update_dyld_shared_cache-173 project fails to build with LLVM compiler 2.0. + +dyld-180 + 8F63: dyld-179.4 fails on missing /usr/local/lib/system/libc.a + 11A238a: AutoDesk AutoCAD beta 4 crashes on launch + 11A244: overlapping files in libdyld & update_dyld_shared_cache + + +dyld-179.7 (iOS 4.2) + range check initializers + +dyld-179.6 + dyld_shared_cache_extract_dylibs shouldn't return '-1' when arch already exists + +dyld-179.5 + Need a version of dyld_shared_cache_extract_dylbs that lipo's results + Need a version of dyld_shared_cache_extract_dylbs that reports progress + back out work around for 8151909 + +dyld-179.4 + images in shared cache are bound against different IOKit than found at runtime + +dyld-179.3.1 + 11A245: Photoshop CS4 fails in SmokeTest because of crash in FNPLicensingService (dyld) + +dyld-179.3 + 11A238a: All Adobe CS4 applications crash on launch + +dyld-179.2 + 11A239: leaks throws an exception, doesn't work + +dyld-179.1 + libdsc.a in Jasper SDK needs to be arm version (is x86 version) + +dyld-179 + update_dyld_shared_cache does not work with individual symbol re-exports + +dyld-178 + Support __thread variables exported from dylibs + update dyld_all_image_infos more often, for better crash logs + +dyld-177 + We need an extractor able to pull the shared symbols cache apart + Don't put slide info in dyld shared cache in Jasper + dyld does not prevent loading of unsigned dylib + +dyld-176 + split seg info wrong for x86_64 stub helpers + +dyld-175 + ObjC optimized shared cache does not slide + +dyld-174 + Need to merge Apex ObjC method list optimizations in dyld shared cache to TOT dyld + implement ASLR for dyld shared cache for iOS + +dyld-173 + fix permissions on rebased binary not slid + +dyld-172.2 + dyld should not include SystemIntegrity headers on embedded platform +Have DYLD_PRINT_BINDINGS print out when resolver functions are called + +dyld-172.1 + fix visibility of dyld_func_lookup depending if libdyld is .a or .dylib + +dyld-172 +Add hack to work around 8151909 +Alter project to build for both kinds of libSystems + +dyld-171 +Add support for individual symbol re-exports +Add support for building for iOS + +dyld-170 + ER: more metadata for dyld errors + +dyld-169 + update_dyld_shared_cache needs to support resolver functions +Implement some cxa stuff to work with next c++abi.a + +dyld-168 + dyld should move shared cache file into /var/run instead of open-unlinking + private shared cache calls mmap twice for each range + Loading MH_DYLIB_STUB causing coalescable miscount + 11A168: update_dyld_shared_cache problems during install + use of C++ thread local initializer causes infinite recursion + (from iPhoneOS) dyld should stub out mach_error_string for 8KB RAM savings per process + (from iPhoneOS) verify that replacement is in this image + +dyld-167.2 + dyld: Use libsystem_mach.a from /usr/local/lib/dyld to build dyld + +dyld-167.1 + dyld should move shared cache file into /tmp instead of open-unlinking + +dyld-167 + dyld support for function specialization + dyld should directly call __cxa_finalize(), part 2 + dyld support for thread local variables + + +dyld-166 + ER: facility to detect which images were loaded at runtime + update_dyld_shared_cache -verify fails + + +dyld-165 + update_dyld_shared_cache should suppress warnings for embedded frameworks + Sort method lists in dyld shared cache + + +dyld-164 + Move libdyldapis over to a dylib target for Libsystem + pruneEnvironmentVariables does not update gLinkContext.apple + + +dyld-163 + Warning message is misleading + update_dyld_shared_cache should not run past end of splitseginfo if zero terminator is missing + dyld should directly call __cxa_finalize(), part 1 + + +dyld-162 + crash in update_dyld_shared_cache when checking if current cache is out of date + ER: Support upward dylib dependencies + + +dyld-161 + __builtin_return_address broke for PPC + SWBDC error: 'ld: duplicate symbol' while building 'dyld-160~48' in '11A108a-llvmgcc-2324.3' + + +dyld-160 + Environment variable to cause dyld to dump rpaths used during loading + update_dyld_shared_cache failed: internal error, new trie allocated to far from fLinkeditBase + dyld still slides executables when PIE is disabled by the kernel + + +dyld-159 + update_dyld_shared_cache fails to find roots when targetting NFS + dyld does not call initializers from all S_MOD_INIT_FUNC_POINTERS sections + move _dyld_func_lookup prototype to dyld_priv.h + + +dyld-158 + dyld attempts to load libraries via rpath when already loaded + dyld shared cache can be more random + + +dyld-157 + switch to use libc++abi-static.a instead of libstdc++-static.a + + +dyld-156 + remove DYLD_NO_PIE from man page + support multiple dylibs with non-intersected weak symbols in shared cache + dyld sends bogus library unload notice to CoreSymbolication during dlopen_preflight() + spelling: "was build against" -> "was built against" + + +dyld-155 + sjlj based exception support needs to be conditionalized for arm - not iPhoneOS + + +dyld-154 + Need to enable CoreSymbolication load/unload notices on iPhoneOS + + +dyld-153 + add field to dyld_all_image_infos pointing to dyld_all_image_infos + + +dyld-152 + Minimize system calls and mach messages during dyld startup + Why is checkSharedRegionDisable() !__LP64__ + + +dyld-151 + dyld support for sliding PIE binaries in the kernel + libdyld does not install multiple headers at installhdrs time + dlopen() crash when executable contains LC_RPATH and executable file is deleted while running + leak in dyld during dlopen when using DYLD_ variables + + +---------------------------------- + +dyld-150 ( iPhoneOS 3.1 ) + flat_namespace linkage error in update_dyld_shared_cache should be a warning not an error + Have dyld save load addr + UUID for executable images + Libsystem fails to link with latest gcc: dyld missing _dyld_func_lookup + + +dyld-149 + dlopen() not working with non-canonical paths + update_dyld_shared_cache fails creating shared cache + + +dyld-148 + shared cache file offsets are inconsistent + + +dyld-147 + move install location for update_dyld_shared_cache and man page to local + imageFileModDate in dyld_all_image_infos is sometimes bogus for the first image therein + dyld_shared_cache_util should optionally print VM load addresses for each dylib + uuid_t not defined in dyld_priv.h + + +dyld-146 + Save load information (load addr + UUID) to dyld_all_image_infos for images from outside the shared cache + update_dyld_shared_cache should improve warning above deployment target + + +dyld-145 + optimize stubs in dyld shared cache to be no-PIC + dyld_shared_cache_util built by dyld target should go in platform directory + dyld_shared_cache_util should list LC_REEXPORT_DYLIB libraries as dependents + + +dyld-144 + dyld_shared_cache_util built by dyld target should go in platform directory + API: Detect shared cache overrides + + +dyld-143 + ER: Tool to list the contents of a dyld shared cache image + ARM support for dsc_iterator + + +dyld-142 + text relocs fail in large segments + Variable whose data is overwritten needs to be marked `volatile' + dyld option to print time for each initializer, Apple's or the app's + + +dyld-141 + update_dyld_shared_cache assumes -root path contains no symlinks +sync with SnowLeopard dyld-132.13 + + +dyld-140 + load code signature from file, and before mapping the file + + +dyld-139.1 + Northstar7C62: dyld-139 fails to build + + +dyld-139 + dyld on iPhoneOS uses libgcc_eh.a which uses pthread_key_create which does not work + dyld can leak when an internal exception in thrown + support compressed LINKEDIT on iPhone +sync with SnowLeopard dyld-132.11 + + +dyld-138.1 + New mechanism to instruct dyld whether to check for libs outside the shared cache + + +dyld-138 + need to handle symlinks when dylib is in cache but file is intentionally missing + dyld should avoid stat()ing libraries present in the shared cache unless magic override file is present + + +dyld-137 + dyld reports image not found if it is in the shared cache but no binary is present on disk +sync with SnowLeopard dyld-132.10 + + +dyld-136 + dyld reports bogus fat offset when registering shared cache signature + + +dyld-135 + iPhone: Need objc optimizations in iPhone OS shared cache + + +dyld-134 + build armv6 dyld with Thumb +sync with SnowLeopard + + + +dyld-133.1 +fix for all arm architectures + + +dyld-133 + make dyld that uses shared cache on iPhone OS + + +---------------------------------- + +dyld-132.13 ( Mac OS X 10.6 ) + classic images not unmapped when unloaded + + +dyld-132.12 + dyld's dtrace notification should be done after rebasing + + +dyld-132.11 + Remove dyld workaround for McAfee VirusScan + Add gating mechanism to dyld support system order file generation process + + +dyld-132.10 + dyld's abort_report_np() doesn't abort + 10A331: CUPS crashes when calling sandbox_init + + +dyld-132.9 + dlopen_preflight() on image in shared cache leaves it loaded but not objc initialized + Exception Backtrace being obscured by _mh_execute_header + Silverlight Preferences.app crashes + + +dyld-132.8 + dlopen() leaks send right obtained from mach_thread_self() + dyld's calloc() allocates too few bytes + dyld lazy binding is not thread safe + + +dyld-132.7 + need a way other than setgid to have dyld launch a process securely, ignoring DYLD_ env vars etc + Need mapping of files to their offsets into the dyld_shared_cache files on disk + + +dyld-132.6 + update_dyld_shared_cache -overlay should check root dyld caches + NSCreateObjectFileImageFromMemory() call vm_deallocate even if application used malloc() + + +dyld-132.5 + dyld is missing some binding optimizations + symbol lookups in the new trie structure should be faster + weak binding done too early with inserted libraries + + +dyld-132.4 + improve statistics output + better error message if LC_RPATH is used in dylib destined for shared cache + dladdr() broke when image has debug symbols + 10A264: Google Earth 5.0 crashes on quit + man page should better explain update_dyld_shared_cache -root option + + +dyld-132.3 + remove setjmp/longjmp from _simple_printf in dyld + Typo in dyld(3) man page + race condition in pre-10.6 style lazy pointer binding + make cheaper dladdr() that just returns image path + 10A266 - Adobe InDesign CS4 crashes on launch + dyld is not setting imageFileModDate in dyld_image_info + Logic Pro crashes after creating a new document on SnowLeopard + + +dyld-132.2 + adopt new CoresSymbolication notification mechanism + Alter libdyld.a to not need libsystem to link with dylib1.o + 10A256a: update_dyld_shared_cache -verify crashes (probably due to lack of a shared cache) + + +dyld-132.1 + update_dyld_shared_cache failed: no writable segment in Cocoa framework + + +dyld-132 + CrashTracer: [USER] 1 crash in Neverwinter Nights 2 + + +dyld-131 + libgmalloc broken on 10A10246 and 10A251 (libgmalloc not inserted early enough) + + +dyld-130 + Rosetta circular dependency spew + @rpath and @loader_path Should be Documented in man page +Prune unneeded load commands from shared cache images + + +dyld-129 + dyld-128 no longer builds for armv6 + 10A244: iTunes crashes trying to load MobileDevice.framework + + +dyld-128 + ImageLoader objects can be made smaller + dyld spin in SecurityAgent + ImageLoaderMachO::makeImportSegmentWritable() doesn't do it + + +dyld-127 + Add all_image_infos for CrashReporter +some fixes for compressed LINKEDIT + + +dyld-126 + x86_64 export trie nodes may have large negative address + update_dyld_shared_cache man page should be updated to reflect new SL behavior + + +dyld-125 + @rpath and @loader_path should be documented in man page + Add NOP after trap instruction in _dyld_fatal_error + + +dyld-124 + update_dyld_shared_cache should not automatically run by default +Add support for arm shared caches +Add support for __program_vars section +Add more -verify sanity checks + + +dyld-123 + Add -verify option to update_dyld_shared_cache + [dyld] Errors reported by the upcoming compiler flags verifier + 10A224: update_dyld_shared_cache warns about a condition in B&I-built frameworks + stop shadowing old data structures + dyld implicit-libSystem breaks valgrind + + +dyld-122 + Need a way to determine if a gdb call to dlopen() would block + Drop Rosetta shared cache generation by default / at install + "terminaton function" misspelled + Make it easier to use shared caches from foreign systems + SnowLeopard10A210: MATLAB 7.6 crashes on launch + + +dyld-121.1 + CrashTracer: crash in iTunes at com.apple.QuickTimeComponents.component + + +dyld-121 + libdyld.a is missing dyld_stub_binder + + +dyld-120 + 10A197 - After Effects 8.0.2 fails to launch after installation + + +dyld-119 + 10A212: update_dyld_shared_cache failed: string buffer exhausted + Oracle client crashes + + +dyld-118.1 + Cope with duplicate protocol references in shared cache construction + + +dyld-118 + 10A197 vs 10A190: Applications warm launch time slowdown due to dyld + + +dyld-117 + 10A198 - Final Cut Pro 6.0.4 crashes on launch [INTRODUCED BY dyld-115 IN 10A197] + + +dyld-116 + Pandora Desktop (Adobe AIR Framework) crashes on launch [INTRODUCED BY dyld-101 IN 10A14] + dyld should use libunwind + Possible leak originating in speech at LoadEngine + update_dyld_shared_cache manpage typos 'parition', 'assignes', 'choosen' + + +dyld-115 + LINKEDIT content could be greatly compressed + + +dyld-114 + update_dyld_shared_cache needs to provide progress that the Installer can display + update_dyld_shared_cache needs to be able to look at an Installation sandbox + dyld isn't calling csdlc_notify on library unload + warning, could not bind Mail.app because realpath() failed on /AppleInternal/.../XILog.framework + + +dyld-113 + NSAddressOfSymbol(NULL) should return NULL and not crash + dlopen() should fail to load (non-pie) executables + + +dyld-112 + _replacement misspelled as _replacement in dyld-interposing.h + make _dyld_find_unwind_sections() faster + + +dyld-111 + improve bad relocation error message + Need load/unload notification sent to com.apple.vmudl service when requested + _replacement misspelled as _replacement in dyld-interposing.h + + +dyld-110 + check libSystem is correct in shared cache file before loading it + function names returned by dladdr do not match reality + update_dyld_shared_cache .map files should be written atomically + dlopen(RTLD_NOLOAD) does not need to throw an internal exception + Explanation for the RTLD_NEXT flag in dlsym(3) needs clarification + DYLD_FALLBACK_LIBRARY_PATH should not apply to dlopen() of a partial path + No shared cache present on first boot + + +dyld-109 + Safe Boot should disable dyld shared cache + CrashTracer: crash in preFetch() reading off end of LINKEDIT + DYLD_NO_PIE env variable should be documented in dyld manpage + put all dylibs in shared cache - not just ones used by more than one app + Leaked fSegmentsArray and image segments during failed dlopen_preflight + + +dyld-108.1 + armv7/armv5 specific settings needed for dyld + dyld shouldn't set VALID_ARCHS + + +dyld-108 + ER: dyld based Objective-C selector uniquing + + +dyld-107 + update_dyld_shared_cache should require all source dylibs be owned by root + Limit what might go in the dyld shared cache + DYLD_ROOT_PATH should apply to LC_RPATH rpaths + Grow initial dyld pool if needed + there should be some way to temporarily turn off -pie + If pie, ignore preferred load address + Put all_image_infos in its own section to it is easy to find + + +dyld-106 +allow update_dyld_shared_cache to be build 64-bit + dyld error handling fails in low memory, _simple_salloc() result not checked + dyld should provide executing program name in incompatible cpu-subtype error message + + +dyld-105 + It should work to set DYLD_FALLBACK_LIBRARY_PATH to empty + Remove the exceptions made in 4804594 for filemaker + Remove Tiger-era hacks in dyld for old app compatibility + Make RTLD_MAIN_ONLY public + die more gracefully when load commands are malformed + SWB: dyld build failure when built with -fstack-protector + dyld man page mis-spelling + update_dyld_shared_cache does not work when run manually + + +dyld-104 + The optimization to reuse install names as fPath was lost + Add JIT field to dyld_all_image_infos +Add _dyld_find_unwind_sections() + + +dyld-103 + NSLinkModule() can crash + dyld: obsoleted deprecated APIs +work around for +add test cases for lazy dylib loading + + +dyld-102 + Man page typo for update_dyld_shared_cache + Add "malloc is initialized" to the dyld_all_image_infos struct + better handling of open() errors + remove + Use instead of + dyld and libdyld should not force building with gcc 4.0 + + +dyld-101 + make it easier to find dyld_all_image_infos + Need _dyld_get_image_slide(const struct mach_header* mh) + dyld: push code signatures for libs to kernel + + +dyld-100 + dyld falls over when asked to map a split seg library not in the shared region + dyld: interposing does not work when replacee is thumb + dyld: all PIE programs crash on launch + BigBear: not loading libraries at their preferred addresses + dyld support for arm subtypes + dyld's prefetching should be turned off for prebound images (and perhaps entirely?) + dyld-95.3 doesn't build with gcc 4.2 +merge in arm support + ADOBE: Premiere Pro crashes on quit + +---------------------------------- + +dyld-96.2 (Mac OS X 10.5.2) + 10.5.2 Regression: 9C18 MeetingMaker crash on launch + +dyld-96.1 + update_dyld_shared_cache can crash if dylibs modified out from under it + crash when dyld interposes on system with partially invalid cache + com.apple.dyld message spew + CFSTRs cause crashes in Leopard + if system shuts down during update_dyld_shared_cache, tmp file is never cleaned up + dlopen() and dlopen_preflight() can leak on failure + + + +dyld-95.3 (Mac OS X 10.5) + Increase initial dyld pool size for 64-bit programs + + +dyld-95.2 + make ppc dyld cache a different file for rosetta + + +dyld-95.1 + McAfee VirusScan fails to launch on Leopard9A513 + + +dyld-95 + 9A516 - Keep getting disk full errors + + +dyld-94 + Leopard (9a499): dyld crash with recursive calls to dlclose() + + +dyld-93 + FileMaker Server 8.0v4 helper tools broken by @executable_path security change + Use msync(MS_SYNC) when building dyld cache + + +dyld-92 + Skype Crashes during launch + + +dyld-91.2 + dlopen() looks too far up stack, can cause crash + + +dyld-91.1 + dyld warning about dtracehelper is too noisy? + Lots of task_self_trap() system calls in ImageLoader::recursiveInitialization() + + +dyld-91 + use of @loader_path based RPATH can cause dyld to leak + Dyld_stubs should not be writable on x86 + + +dyld-90.2 + generating dyld shared cache generation on first boot makes time to MacBuddy very slow + + +dyld-90.1 + truncated dyld cache file after panic causes hang at boot + + +dyld-90 + stop special casing main executables initializers + DYLD_INSERT_LIBRARIES doesn't work correctly with initializer functions + + +dyld-89 + dyld could asynchronously request pages it will need + handle when argv[0] is NULL. + Foundation built on 9A436 doesn't launch 64 bit apps +partial fix for: Dyld_stubs should not be writable on x86 + + +dyld-88 + update_dyld_shared_cache keeps spewing messages to console + optimize LINKEDIT region of dyld shared cache + Support extended __dyld section with NXArgc, etc addresses +remove call to __xlocale_init() +Update __OPEN_SOURCE__ conditionals + + +dyld-87 + CFM games use private _dyld_fork_* routines - add back + better handling of NOLOAD with symlinked dylibs + + +dyld-86.1 +Fix DYLD_SHARED_REGION=private +update man page + + +dyld-86 + update_dyld_shared_cache fails on @executable_path framework + [Leopard]: 9A441/442: unable to log in after switching to arabic + dlopen via CFBundleLoad keeps searching after finding a loadable object + + +dyld-85.2 + MatLab 2007a (7.4) doesn't launch on Leopard9A441 +Never optimize ppc shared cache on intel +Fix LINKEDIT size in shared cache .map files +Fix how PIEs are moved to work with NX +Call pthread_init_keys to work with latest Libc + + +dyld-85.1 + Leopard9A447: Meeting Maker and Microsoft apps will not launch on Intel. + + +dyld-85 + 9A436: Adobe: Photoshop CS3 crashed on pressing Command-C after Command-A + Use _dyld_initializer + + +dyld-84 + 9A438 dlopen_preflight() corrupts all_image_info list causing Symbolication crashes + B&I needs an ENV variable to turn off "dyld: ioctl to register dtrace DOF section failed" warnings + remove support for __image_notify sections +remove all update_prebinding code + + +dyld-83 + use _simple_dprintf() instead of fprintf() +remove -progress option from update_dyld_shared_cache +update_dyld_shared_cache no longer tells rosetta to flush its caches + update_dyld_shared_cache error message gives an errno value rather than an error string + dyld interposing doesn't work with dlsym() lookup + dlopen_preflight() of MH_BUNDLE leaks + integrate ImageLoader changes into leopard dyld + translated (ppc) dyld should not attempt to register DOF sections + Some dyld library paths are not canonicalized, causing tools using those paths to defenestrate themselves + + +dyld-82.5 + REGR: Leopard9A419: Firefox hangs on launch + + +dyld-82.4 + Leopard9A420: interposing of libMallocDebug or libgmalloc broken +Fix so problems like are warnings instead of errors + + +dyld-82.3 + 9A420: dyld: ioctl to register dtrace DOF section failed + + +dyld-82.2 + dyld frees string returned by dyld_image_state_change_handler + better handling than "corrupt binary, library ordinal too big" + + +dyld-82.1 + dyld changes needed to support read-only DOF + + + +dyld-82 + don't need to hold dyld global lock while running initializers + dyld leaks when dlopen fails + dyld leaks two blocks after bundle unload + + +dyld-81 + auto update dyld shared caches if missing or out of date + + +dyld-80.1 + Erronious "unsafe use of @rpath" dyld error + 9A384: update_dyld_shared_cache fails after ditto'ing matador root with debug info + Uninitialized ImageLoader->fRegisteredDOF field + + +dyld-80 (Leopard9A400) + Use new shared region syscalls + @rpath does not work with -rpath @executable_path/... + Firefox causes segfault during update_dyld_shared_cache + + +dyld-79.3 + Use new shared region syscalls + +dyld-79.2 + @rpath does not work with -rpath @executable_path/... + +dyld-79.1 (Leopard9A396) +fix use of LC_REEXPORTED_DYLIB + + +dyld-79 (Leopard9A392) + Support Apple PIE (address space randomization) + update_dyld_shared_cache should warning and not quit if a specified root is missing + DOF registration needs to switch from /dev/helper to /dev/dtracehelper + don't error out when a cache line crossing i386 stub cannot be bound + + +dyld-78.2 (Leopard9A387) + 9A385: Mail (anything using Message.framework) takes a ridiculous amount of time to launch + + +dyld-78.1 (Leopard9A385) +Fix override of _malloc_lock to be data instead of code + + +dyld-78 + when loading a bundle, dyld is not making a copy of name + 9A343 KidPix 3 : SpellChecker bundle is not loaded + dyld cache does not recognize dynamic load of library via symbolic link + + +dyld-77.1 +Back out 4892382 until B&I build fleet has fixed kernel + + +dyld-77 + Use _dyld_initializer + Look at reduction/elimination of per-framework cost (don't touch __dyld section) + libdyldapis.a: make initialization as lean as possible + dyld should malloc never-to-be-freed blocks from its own pool + Libraries feeding into Libsystem should contain version numbers (libdyldapis) +Install update_prebinding symlink +Addend warnings to end of shared cache .map files +Conditionalize away update_prebinding support +dladdr() should not remove 's' from "start" + + +dyld-76.2 + hang at boot, Libc changes + +dyld-76.1 + x86_64: dyld share cache does not work for AppKit + + +dyld-76 + Rosetta apps crash after update_dyld_shared_cache + Long-standing typo for "file to short" error from dlopen() / dlerror() + + +dyld-75.1 +Enable ppc shared cache generation on intel machines + + +dyld-75 + 64-byte crossing fast stubs should be bound early to avoid threading issues + support new intel stub segment that is marked not-writable + + +dyld-74 + register dtrace DOF sections with kernel + 10.4.9 Regression: Math Kernel crash with TiNovi 8P114 + + +dyld-73.2 + Leopard 9A921: Dyld error "lazy pointer not found" loading/running java + + +dyld-73.1 (Leopard9A328) + 9A326: update_prebinding crashes at end of install + + +dyld-73 (Leopard9A326) + REGR: 9A322 All Java apps crashing at dyld's misaligned_stack_error + + +dyld-72 (Leopard9A322) + Maya 8 crashes on launch on Leopard9A309 + ProTools 7.1.1cs1 for Intel hangs on launch on Leopard9A309 + x86 crashes in the binding helper do not have proper backtrace + + +dyld-71 (Leopard9A320) + inform rosetta of each library in the dyld shared cache + 9A316: Dreamweaver MX 2004 crashes on launch in dyld's addImage + + +dyld-70 (Leopard9A315) +support split-seg dylibs built with LC_SEGMENT_SPLIT_INFO +support --progress option in update_dyld_shared_cache so that installer can run it + + +dyld-69.1 (Leopard9A309) + 9A305: Firefox 2.0 crashes on launch + httpd is dying in dyld after libobjc.dylib is unloaded + + +dyld-69 (Leopard9A305) + ER: dlclose() should be able to unload dylibs + runtime support for RPATH + + +dyld-68 (Leopard9A296) + rosetta doesn't work when shared cache is present + shared cache for 64-bit archs does not work when some dylibs are invalid + + +dyld-67.1 (Leopard9A292) + support 64-bit programs built with new 10.5 subtype + + +dyld-67 + CrashReporter needs a new way to distinguish fatal dyld errors + support dlopen(NULL, RTLD_FIRST) +Move base address of ppc64 dyld to match new memory layout +Move base address of 64-bit shared caches to match new memory layout +Move location of shared cache file to /var/db/dyld +Add support for LC_REEXPORT_DYLIB +Use shared cache if it exists +Requires ld64-63.1 or later built dylibs for shared cache generation + + +dyld-66.3 (Leopard9A276) + dyld fails to build with Libc changes in 4632326 + support 64-bit programs built with new 10.5 subtype + + +dyld-66.2 (Leopard9A260) + Leopard9A259: Backtraces in crash reports are not getting symbolicated + dyld should get rosetta process name from standard location + + +dyld-66.1 (Leopard9A259) +Fix for build breakage with Libc-436 + + +dyld-66 +Preliminary shared cache support + is in Darwin but not Dev Tools package + export shared range regions for gdb + __pthread_tsd_first appears to be initialized too late in dyld + don't use @executable_path or fallback searching in setuid programs + + +dyld-65.1 (Leopard9A252) +fix B&I build failure with Libc-435 + + +dyld-65 (Leopard9A247) + jump table entry at end of segment can crash + Mathematica 5.2 (64-bit MathKernel) always crashes on 9A229 + dlsym man page needs to be more specific about RTLD_DEFAULT + Change wording in SEARCHING section in dlopen man page + Man page for dyld has misspelling: cheep + dyld(3) man page should point to Mach-O Programming Topics URL + + +dyld-64 (Leopard9A224) + No man page for dlopen_preflight(3) + dyld lazy binding of fast stubs is not completely thread safe + remove use of load_shared_file() + + +dyld-63 (Leopard9A215) + Would like way to quiet dynamic loader + deprecated old APIs for Leopard + NSCreateObjectFileImageFromMemory crashes when image is a MH_EXECUTABLE + + +dyld-62.1 (Leopard9A206) + prebound old stubs failure prevents uTest from running on i386 + + +dyld-62 (Leopard9A202) + an image with an open non-zero base address does not load more efficiently + Leopard9A190: NSAddImage() crashes when called from x86_64 process + /usr/lib/libAPSimple.dylib: mmap error + need to force interposing for rosetta processes + + +dyld-61 (Leopard9A179) + dyld calls close(-1) + _stub_binding_helper_interface isn't using movdqa + dyld tries to mmap 0 size segments and fails with conforming mmap + Load dyld above 4GB for x86-64 +Move apple parameters on stack when DYLD_ variables are removed + + +dyld-60 (Leopard9A160) + Suresec #203: dyld environment with suid binaries + SureSec si#187 linker: environment variables + print warning message if DYLD_INSERT_LIBRARIES is set (then ignored) for a setuid/setgid program + dyld's disableIfBadUser() routine can fail for constant strings + + +dyld-59 (Leopard9A156) + ER: dlopen_preflight() + + +dyld-58 (Leopard9A154) + implement RTLD_SELF + better handling of open() errors + would like dlopen(RTLD_FIRST) so that dlsym variant that does not search dependent libraries + dyld needs to adopt to Unix conformance changes + Crash on Leopard9A146 when GC rejects loading a library on Intel + + +dyld-57 (Leopard9A144) + pthread tsd entries doubly owned by DYLD and Libsystem... (Leopard) + dyld should automatically stop using the shared region if unavailable + If instantiateFromLoadedImage fails, dyld crashes + isCompatibleMachO needs to know about x86-64 + + +dyld-56 (Leopard9A140) + 64-bit dyld should load at around the last 4G - not in the first 4G + 64 bit: app crashes immediately if -pagezero_size >= 0x0ffffffff + dyld needs to build for x86-64 + dyld_debug API shim has leak + dyld does not slide properly on Intel + + +dyld-55 (Leopard9A138) + dlopen() should fail if bundle has objc code incompatible with runtime environment + libdyld: make non-POSIX header definitions visible when _POSIX_C_SOURCE is defined + A flat_namespace image with a reference to an internal private_extern should resolve immediately + dlopen() man page is missing RTLD_NOLOAD and RTLD_NODELETE + _CFExecutableLinkedOnOrAfter() fails on 64 bit + dyld needs to support x86-64 + + +dyld-54 (Leopard9A80) + remove ppc64 workarounds for fixed bugs + Memory error in removePathWithPrefix() + dyld does not properly swap CPU subtype from fat file header + dyld does not compile for open source + ADOBE XCODE 2.2: ZeroLink can cause wrong typeinfos to be used +Sync with Chardonnay (except for 4313830 and 4215516) + + +dyld-53 (Leopard9A42) + Add -fno-exceptions to Release target of libdyld + Wrong number of seconds reported by DYLD_PRINT_STATISTICS on Intel + + +dyld-52 (Leopard9Axxx) + dyld changes for new libstdc++ project + + +dyld-51 (Clueless) + STD:VSX: dlclose() should return non-zero value on failure. + STD:BUILD: dyld references non-conforming member name for PPC64 + The gdb list of images should be updated before dyld bases and binds each image + interposing does not handle stacking/nesting of interposing functions + use of DYLD_INTERPOSE() causes compiler warnings with -std=c99 -pedantic + SWB: dyld-32 fails to link using 4.0 compiler (gcc-4042) on Tiger8A371 + +dyld-50 (Leopard9Axxx) +Convert to build with gcc-4.0 + SWB: dyld-32 fails to link using 4.0 compiler (gcc-4042) on Tiger8A371 + +---------------------------------- + + + +dyld-46.16 (Mac OS X 10.4.11) + raise bail out limit in update_prebinding to 100 errors + +dyld-46.15 + [SUIncaSoho] update_prebinding can only handle 2MB of ppc unprebound dylibs on intel + update_prebinding fails if a prebound dylib depends on a non-prebound dylib + +dyld-46.14 + prebinding zeroes out files if an error occurs (such as no vm space left) + Rare & unknown root cause: Corruption of Kerberos framework during SU + +dyld-46.13 + dyld crashes starting threaded program + 10.4.9 Regression: SuTiNovi8P132: update_prebinding never reaches steady state + 10.4.9 Regression: SuTiNovi8P132: update_prebinding never reaches steady state + [SUTiSoHo] update_prebinding crashes when a weak linked dylib is missing + [SUIncaSoHo] update_prebinding crashes when a weak linked dylib is missing + +dyld-46.12 + 10.4.9 Regression: Math Kernel crash with TiNovi 8P114 + +dyld-46.11 + dyld's x86_64 support should be open source + 10.4.x: an image with an open non-zero base address does not load more efficiently + [SUTiNovi] A flat_namespace image with a reference to an internal private_extern should resolve immediately + [SUTiNovi] 10.4.8 regression: ppc X program segmentation fault in 10.4.8, worked in 10.4.7 + [SUIncaNovi] 10.4.8 regression: ppc X program segmentation fault in 10.4.8, worked in 10.4.7 + [SUIncaNovi] A flat_namespace image with a reference to an internal private_extern should resolve immediately + +dyld-46.10 + dyld-46.9 fails to build in Nicoya + +dyld-46.9 (Inca8K...) + jump table entry at end of segment can crash + +dyld-46.8 (Inca8K1073) + Mathematica 5.2 (64-bit MathKernel) always crashes on Inca + +dyld-46.7 (Inca8K1072) + dyld lazy binding of fast stubs is not completely thread safe + +dyld-46.6 (Inca8K1061) + Inca: don't write warning to stderr for setuid binaries + +dyld-46.5 (Inca8K1059) + prebound old stubs failure prevents uTest from running on i386 + +dyld-46.4 (Inca8K1057) + NSAddImage() crashes when called from x86_64 process + an image with an open non-zero base address does not load more efficiently + +dyld-46.3 (Inca8K1054) + need to force interposing for rosetta processes +re-enable setuid security fixes for 4525062 and 4525053 + +dyld-46.2 (Inca8K1046) + Adobe CS2 no longer launches + Load dyld above 4GB for x86-64 + +dyld-46.1 (Inca8K1040) +rdar://problem/4538177 dyld does not slide on x86_64 + +dyld-46 (Inca...) +re-enable x86_64 + + +dyld-45.3 (SUSecurity ) + *SecUpd: Chardonnay* don't write warning to stderr for setuid binaries + *SecUpd: Tiger* don't write warning to stderr for setuid binaries + +dyld-45.2 (SUSecurity ) + *SecUpd: Chardonnay* SureSec si#187 remove all DYLD_ env vars for setuid binaries + *SecUpd: Tiger* SureSec si#187 remove all DYLD_ env vars for setuid binaries + *SecUpd: Chardonnay* Suresec #203: don't use $HOME with suid binaries + *SecUpd: Tiger* Suresec #203: don't use $HOME with suid binaries + + +dyld-45.1 (SUTiLondon...) +back out Suresec #203: dyld environment with suid binaries [SUTi] + + +dyld-45 (SUTiLondon...) + sync all 10.4.x dyld trains + dyld fix for gcc-3.3 C++ needs to get in SU + 64-bit dlopen crashes when opening fat bundle + SureSec si#187 linker: environment variables [SUTi] + Suresec #203: dyld environment with suid binaries [SUTi] + SureSec si#187 linker: environment variables [SUChard] + Suresec #203: dyld environment with suid binaries [SUChard] + +dyld-44.23 (Inca8...) + Crash using Core Image under Rosetta running InDesign CS2 w/ Magma Effects + +dyld-44.22 (Inca8K1030) + Stub binding helper changes for FP args for x86-64 + +dyld-44.21 (Inca8K1030) + printf doesn't work for x86-64 + +dyld-44.20 (Inca8K1029) + isCompatibleMachO needs to know about x86-64 + +dyld-44.19 (Inca8J1028) +two small x86_64 fixes + +dyld-44.18 (Inca8J1027) + dyld needs to support x86-64 + +dyld-44.17 (Chardonnay8G1152) + prebound fast stubs not ignored for flat_namespace dylibs + +dyld-44.16 (Chardonnay8G1141) + Sherlock often crashes in dyld::bindLazySymbol on launch + +dyld-44.15 (Chardonnay8G1137) + no apps can launch with /usr/lib/libMallocDebug.A.dylib on 8G1133 + +dyld-44.14 (Chardonnay8F1110) + System Integrity: changes needed for dyld + +dyld-44.13 (Chardonnay8F1108) + pthread tsd entries doubly owned by DYLD and Libsystem... (Chardonnay) + +dyld-44.12 (Chardonnay8F1108) + never pass shared_region_map_file_np() a zero-length region + +dyld-44.11 (Chardonnay8F1104) + dyld launch code should special case if there is only one prebound image with weak exports + +dyld-44.10 (Chardonnay8F1100) + dyld fails to link with Libc-391.1.13 + +dyld-44.9 (Chardonnay8F1093) + XCode2.1 + gcc 3.3 + C++ exception + Bundle Bug + low disk space code path executed with 44GB free + +dyld-44.8 (Chardonnay8F1079) + dyld lazy binding code should use movdqa + +dyld-44.7 (Chardonnay8B1072) + fix for rosetta crashes + +dyld-44.6 (Chardonnay8B1072) + Optimizing system should only progress once + +dyld-44.5 (Chardonnay8B1052) + New intel stub support + +dyld-44.4 (Chardonnay8B1051) + Leopard 9A14: Finder crashes burning a CD + dyld lazy binding code needs to save/restore all possible register parameters + use new libstdc++-static + +dyld-44.3 (Chardonnay8B1051) + dyld should recognize byte-swapped Mach-O files + dyld should only update prebound external relocations if they change + +dyld-44.2 (SUTiDenver8F10) [Mac OS X 10.4.3] + Tiger breaks NSCreateObjectFileImageFromMemory with fat Mach-O + dyld does not load libraries correctly if matching install_name in /usr/lib + CrashTracer: ..269 crashes at com.adobe.Acrobat.framework: RunAcrobat + 424 + +dyld-44.1 (Chardonnay) + __attribute__ ((regparm (3), stdcall)) doesn't work for inter-image calls + dyld should automatically set DYLD_SHARED_REGION to avoid + +dyld-44 (Chardonnay) + merge SUTiCambridge dyld and Karma dyld into Chardonnay + + +dyld-43.1 (SUTiCambridge8C20) [Mac OS X 10.4.2] +Update open source APSL headers + update_prebinding should gracefully handle low disk space + prebinding should not change n_value of .obj_class_name symbols + dyld gets into infinite loop with dlsym if dylib dependencies contain loops + FilesBuster crashed in dyld + DYLD_ROOT_PATH crashes program if any component does not start with / + dyld writes past end of buffer when checking environment variables + dyld_image_removing notification never sent + ANN: DR020 + + +dyld-43 (Tiger8A428) [Mac OS X 10.4.0] +rdar://problem/4067311 PACE protected Mach-O apps crash under Tiger 8a420 + + +dyld-42 (Tiger8A420) +rdar://problem/4058724 FileMaker JDBC extension does not work in Tiger + + +dyld-41 (Tiger8A417) +rdar://problem/4047633 Adobe Photoshop CS2: Scripting Save for Web runs out of memory unexpectedly when compared to 10.3.8. + + +dyld-40 (Tiger8A413) +rdar://problem/4047391 dyld no longer follow dependent libraries using dlopen + + +dyld-39 (Tiger8A406) +rdar://problem/4034570 DT P1: MATLAB R14sp2 does not run on Tiger +rdar://problem/4028274 DT P1: GLSLShowpieces crashes when built and run + + +dyld-38 (Tiger8A402) +rdar://problem/3820219 Tiger + MOTU Digital Performer + hit play = Digital Performer crashes +rdar://problem/3978682 If an excutable & a framework with the same base name are in the same folder, you get a dyld error if you use full path +rdar://problem/3987135 MATLAB unresolved symbol __XEditResPutWidgetInfo on Tiger +rdar://problem/4027813 dlsym man page documents unsupported RTDL_SELF + + +dyld-37 (Tiger8A399) +rdar://problem/4001668 Safari hang inside CFBundleDYLDGetSymbolByNameWithSearch in Flash plug-in upon opening www.espn.com +rdar://problem/3853454 dyld needs to call sys_icache_invalidate() after instruction fix-ups +rdar://problem/3984074 Soldier of Fortune II crashes at start of gameplay on Tiger8A36 +rdar://problem/4008399 Malicious user can set Tiger dyld fallback paths in setuid process +rdar://problem/4021002 Aliens vs Predator II with latest update does not run + + +dyld-36 (Tiger8A393) +rdar://problem/3970385 [Tiger] 8A323: Aliens VS Predator 2 Freezes On Launch +rdar://problem/3839120 _dyld_get_image_header_containing_address returning NULL when it shouldn't +rdar://problem/3925105 update_prebinding crashes on /usr/lib/libnetsnmptrapd.5.dylib + + +dyld-35 (Tiger8A384) +rdar://problem/3984074 Soldier of Fortune II crashes at start of gameplay on Tiger8A367 +rdar://problem/4003637 Typo blocks support of -init for 64-bits +rdar://problem/4003891 improve mmap() failure error msg + + +dyld-34 (Tiger8A381) +rdar://problem/3976215 dlopen() should look for a library with matching name and architecture +rdar://problem/3978682 executable and dylibs can be confused +rdar://problem/3819111 dlopen() ignores LD_LIBRARY_PATH +rdar://problem/3956709 Insufficient documentation for dlopen + + +dyld-33 (Tiger8A379) +rdar://problem/3941826 8A340: Tron 2.0 does not launch +rdar://problem/3848965 James Bond 007 Nightfire fails to launch +rdar://problem/3947513 No One Lives Forever 2 crashes on Quit on Tiger8A347 +rdar://problem/3779999 Spyhunter crashes upon launch on Tiger8A244 + + +dyld-32 (Tiger8A367) +rdar://problem/3974486 PowerMail launch fails: weak linking of library but not symbols +rdar://problem/3974797 can update_prebinding only complain about stuff I actually have installed? +rdar://problem/3979715 synchronizing attribute name with section name + + +dyld-31 (Tiger8A362) +rdar://problem/3958479 Third party "crash reporter" app backtraces have no symbols for our libraries/APIs +rdar://problem/3966025 add DYLD_INTERPOSE macro to an apple internal header +rdar://problem/3968283 For interposing support section-by-type and section-by-name + + +dyld-30 (Tiger8A357) +rdar://problem/3947090 objc runtime / two-level namespace crash when using new interposing libMallocDebug or libgmalloc + + +dyld-29 (Tiger8A356) +rdar://problem/3960729 update_prebinding needs to fflush() every output line +rdar://problem/3960657 Why are my crashreporter logs all useless? + + +dyld-28 (Tiger8A354) +rdar://problem/3510780 _dyld_get_image_header_containing_address man page is wrong +rdar://problem/3798074 STD: dlclose() does not unload bundles +rdar://problem/3882857 dyld no longer finds framework file if not in Framework hierarchy +rdar://problem/3898206 dlopen() does not support library searching as linkage at launch does +rdar://problem/3921479 dyld(1) should not document unimplemented env vars +rdar://problem/3935714 Support RTLD_NODELETE +rdar://problem/3942919 can't use _debug libSystem + + +dyld-27 (Tiger8A350) +rdar://problem/3902088 update_prebinding needs to provide progress information + + +dyld-26 (Tiger8A348) +rdar://problem/3943033 hang upon boot, permissions and startup thrash + + +dyld-25 (Tiger8A347) +rdar://problem/3916220 Main executable unmapped +rdar://problem/3812732 Microsoft Error Reporting crashes on Tiger +rdar://problem/3943349 dyld project should install mach-o/dyld_debug.h +rdar://problem/3943546 mach-o/dyld_debug.h should be deprecated +rdar://problem/3933738 use getsectdatafromheader_64 + + +dyld-24 (Tiger8A345) +rdar://problem/3941688 update_prebinding needs a --dry-run option + + +dyld-23 +rdar://problem/3799069 Need coalescing across dylibs +rdar://problem/3859973 problem with weaklink using darwine +rdar://problem/3938167 dyld does not work with XLF generated binaries and provided libraries +rdar://problem/3935377 STD: dyld needs to include sys/time.h and sys/types.h +rdar://problem/3930361 STD: dyld must create a stub for ___fegetfltrounds +rdar://problem/3934712 STD:VSX: more dlfcn.h namespace issues + + +dyld-22.5 (Tiger8A340) +rdar://problem/3788633 MatLab 7.0 fails to launch on Tiger8A246 +rdar://problem/3919674 prebound flat-namespace libraries are bound wrong + + +dyld-22.4 +rdar://problem/3915914 make update_prebinding more robust + + +dyld-22.3 (Tiger8A333) +rdar://problem/3920720 dyld missing symbols because Xcode not add -fvisibility=hidden + + +dyld-22.2 (Tiger8A330) +rdar://problem/3909873 Transmit.app crashes with symbol not found + + +dyld-22.1 +rdar://problem/3908248 Crash in apps when launched from MallocDebug (BlockMove symbol not found if DYLD_FORCE_FLAT_NAMESPACE is set) + + +dyld-22 (Tiger8A325) +rdar://problem/3903866 reduce LINKEDIT page usage by better use of two-level hints +rdar://problem/3884004 Libraries can be half-baked if an error occurs during their first use +rdar://problem/3899047 interposing doesn't work on 8A317 with dyld-21 +rdar://problem/3514720 Adobe bundle kernel panics system + + +dyld-21 (Tiger8A322) +rdar://problem/3745562 Support two-level interposing with insert libraries (malloc-debug) +rdar://problem/3892061 8A316: Selecting any application in Finder in the column view crashes finder +rdar://problem/3894540 DYLD_PRINT_SEGMENTS no longer works on 8A317 + + +dyld-20 (Tiger8A317) +rdar://problem/32957877 Support @loader_path in dylib commands +rdar://problem/33837173 need SPI to set fallback symbol lookup +rdar://problem/33891778 dyld (__DATA,__dyld) function pointers should drift as little as possible +Fix issue with new prebinding overwriting symlinks + + +dyld-19 (Tiger8A315) +rdar://problem/3823664 dyld reports corrupt executable +rdar://problem/3847571 dyld submission number should be embedded in dyld +rdar://problem/3865141 dyld_all_image_infos should contain bit that says if process is using the shared region +rdar://problem/3884103 dyld needs to use -msoft-float for ppc64 +rdar://problem/3886337 PPC_RELOC_PB_LA_PTR not recognized by ppc64 +Clean up/document dyld-gdb interface + + +dyld-18.3 (prebinding sideworld build) +prebinding enhancements + +dyld-18.2 (Tiger8A306) +rdar://problem/3782982 Fix for Macromedia apps (Flash 7.2) + +dyld-18.1 +rdar://problem/3864644 DVD player crash (loading memory based bundle) + +dyld-18 (Tiger8A303) +rdar://problem/3866877 STD:VSX dlsym() faults when passed a bad handle. +rdar://problem/3862043 typo in dyld function name +rdar://problem/3857000 dyld should limit its exported symbols +rdar://problem/3835208 want better error message for incompatible libraries +dyld now built with dead-code-stripping +better launch performance when hundreds of libraries are used +more bug fixes in update_prebinding in dyld + + +dyld-17.1 (Tiger8A301) +rdar://problem/3672757 initial implementation of update_prebinding in dyld +rdar://problem/3847376 dyld does not slide itself properly (partial fix) +rdar://problem/3843028 STD: make dlfcn.h posix compliant +rdar://problem/3856182 STD: add includes +rdar://problem/3782982 infrastructure work on compatibility for Macromedia apps +Other 64-bit tweaks + + +dyld-16.1 (Tiger8A296) +rdar://problem/3768530 dyld should adopt shared_region_map_file_np() + + +dyld-15 (Gordian 64-bit side world build only) +rdar://problem/3834744 64-bit dyld needs to handle mh->cputype != host_info cputype +Other 64-bit clean ups + + +dyld-14 (Tiger8A280) +rdar://problem/3811777 Deadlock in dyld code, seen in Merlin and Mail +rdar://problem/3793075 Update to fix: DVD Player crashes at launch with disc in drive +rdar://problem/3830560 Don't let gcc-3.5 optimize away send_event +rdar://problem/3826169 Update dlfcn.h header to Apple header and acknowledge Peter and Jorge +rdar://problem/3793861 Update dlopen(3) man page to reflect new implementation +rdar://problem/3559013 libdyld.a needs to build for ppc64 +Added DYLD_PRINT_OPTS +Added DYLD_PRINT_ENV +Deprecated 10 APIs + + +dyld-13 (Gordian 64-bit side world build only) +rdar://problem/3560664 Add ppc64 support +rdar://problem/3819144 Opening PDF in Safari crashes +rdar://problem/3793861 Update dlopen man pages +rdar://problem/3765271 Build with gcc-3.5 +rdar://problem/3762685 Make own prototype for task_self_trap() +rdar://problem/3799467 Mozilla tries to open a dylib using a bundle API +rdar://problem/3812263 Add dyld SPI so Shark can interpose lazy pointer lookup +Added emacs major-mode and tab-width directives + + +dyld-12.4 (Tiger8A271) +tweak so dyld can use libc.a built with long double support + + +dyld-12.3 (no-build) +rdar://problem/3765271 switch dyld to build with gcc-3.5 + + +dyld-12.2 (Tiger8A265) +rdar://problem/3793075 DVD Player crashes at launch with disc in drive + + +dyld-12.1 (Tiger8A255) +rdar://problem/3749360 a flat lookup needs to search everywhere and not short-circuit to own image + + +dyld-12 (Tiger8A231) +rdar://problem/3751226 make dyld thread safe (libdyld.a should come from new dyld project) +rdar://problem/3654650 integrate dlopen() and friends into dyld + + +dyld-11.3 (Tiger8A225) +rdar://problem/3751226 NSAddImage() with leaf name doesn't use fallback path + + +dyld-11.1 (Tiger8A223) +rdar://problem/3749251 NSADDIMAGE_OPTION_RETURN_ONLY_IF_LOADED implies NSADDIMAGE_OPTION_RETURN_ON_ERROR" + + +dyld-11 (Tiger8A220) +rdar://problem/3684168 prevent DYLD_LIBRARY_PATH from causing circular reference when two dylib have same leaf name +rdar://problem/3698212 implement _dyld_launched_prebound() +rdar://problem/3696924 suppport frameworks without symlink to current version +rdar://problem/3692136 Support DYLD_PRINT_LIBRARIES_POST_LAUNCH +rdar://problem/3685517 make find-image-for-address faster via caching +rdar://problem/3661976 -force_flat_namespace should disable prebinding +rdar://problem/3702311 make dyld's __TEXT read-only +rdar://problem/3669059 do cpu sub-type checking on thin files +rdar://problem/3696002 Support C++ vague linkage that resolves internally +rdar://problem/3725847 run initializers in inserted libraries +rdar://problem/3731063 pass arc/argv to initializers +rdar://problem/3731100 don't write on __dyld section unless necessary +rdar://problem/3731633 use mmap() instead of map_fd() +rdar://problem/3676934 don't double read first page of images, use pread() +rdar://problem/3725372 ignore environment variables for setuid binaries + + +dyld-10.2 (Tiger8A157) +rdar://problem/3668765 Macromedia apps don't launch + + +dyld-10.1 (Tiger8A156) +rdar://problem/3691952 Fix-n-continue broke in Objective-C + + +dyld-10 (Tiger8A148) +rdar://problem/3686995 implement dyld_call_module_initializers_for_dylib and do parameter sanity checking of module parameters +rdar://problem/3674139 Make @executable_path work when main executable is a symlink +rdar://problem/3688719 Support CW built binaries built with -data_before + + +dyld-9 +rdar://problem/3682744 GoLive crash (NSIsSymbolDefinedWithHint should try harder) +rdar://problem/3660691 iTunes crash with partial plugin +rdar://problem/3684167 DYLD_IMAGE_SUFFIX broke +rdar://problem/3672670 Fix uninitialized variable in objc hook + + +dyld-8 (Tiger8A144) +rdar://problem/3680627 handle weaklib when prebound +rdar://problem/3672670 new hook for objc + + +dyld-7 (Tiger8A141) +rdar://problem/3669059 Support cpu-sub-type selection +rdar://problem/3675131 allow text relocations +rdar://problem/3675614 fix lazy pointer usage in termination routines +rdar://problem/3673658 Allow NSUnLinkModule to be called with NULL + + +dyld-6 (Tiger8A139) +rdar://problem/3649313 clean up +rdar://problem/3661976 support MH_FORCE_FLAT +rdar://problem/3659666 DYLD_FORCE_FLAT_NAMESPACE should disable prebinding +Better error reporting +Faster flat namespace lookups +Better implementation of NSLinkEditError + + +dyld-5 (Tiger8A135) +rdar://problem/3665738 fix binding of private_extern within multi-module libs +rdar://problem/3667763 use hints supplied in APIs +Set error_string for CrashReporter +Better error message when library can't be found +Properly use ZeroLink bundle notifier + + +dyld-4 (Tiger8A133) +rdar://problem/3663433 allmemory shows 8000 pages are missing + + +dyld-3 +rdar://problem/3659408 Proper searching in umbrellas +rdar://problem/3657040 Better error messages when symbols not found +rdar://problem/3657197 Add ~/Library/Frameworks to fallback path +Properly use DYLD_IMAG_SUFFIX +Initial CrashReporter support + + +dyld-2 (Tiger8A132) +Support: NSNameOfSymbol, NSModuleForSymbol, NSLibraryNameForModule, and NSMakePrivateModulePublic +Add more functionality to DYLD_IGNORE_PREBINDING environment variable + + +dyld-1 +Initial submission of dyld rewrite. diff --git a/src/dyld-apple/doc/man/man1/dyld.1 b/src/dyld-apple/doc/man/man1/dyld.1 new file mode 100644 index 000000000..4c9e61daf --- /dev/null +++ b/src/dyld-apple/doc/man/man1/dyld.1 @@ -0,0 +1,309 @@ +.TH DYLD 1 "December 14, 2009" "Apple Inc." +.SH NAME +dyld \- the dynamic link editor +.SH SYNOPSIS +DYLD_FRAMEWORK_PATH +.br +DYLD_FALLBACK_FRAMEWORK_PATH +.br +DYLD_VERSIONED_FRAMEWORK_PATH +.br +DYLD_LIBRARY_PATH +.br +DYLD_FALLBACK_LIBRARY_PATH +.br +DYLD_VERSIONED_LIBRARY_PATH +.br +DYLD_PRINT_TO_FILE +.br +DYLD_ROOT_PATH +.br +DYLD_SHARED_REGION +.br +DYLD_INSERT_LIBRARIES +.br +DYLD_FORCE_FLAT_NAMESPACE +.br +DYLD_IMAGE_SUFFIX +.br +DYLD_PRINT_OPTS +.br +DYLD_PRINT_ENV +.br +DYLD_PRINT_LIBRARIES +.br +DYLD_PRINT_LIBRARIES_POST_LAUNCH +.br +DYLD_BIND_AT_LAUNCH +.br +DYLD_DISABLE_DOFS +.br +DYLD_PRINT_APIS +.br +DYLD_PRINT_BINDINGS +.br +DYLD_PRINT_INITIALIZERS +.br +DYLD_PRINT_REBASINGS +.br +DYLD_PRINT_SEGMENTS +.br +DYLD_PRINT_STATISTICS +.br +DYLD_PRINT_DOFS +.br +DYLD_PRINT_RPATHS +.br +DYLD_SHARED_CACHE_DIR +.br +DYLD_SHARED_CACHE_DONT_VALIDATE +.SH DESCRIPTION +The dynamic linker uses the following environment variables. +They affect any program that uses the dynamic linker. +.TP +.B DYLD_FRAMEWORK_PATH +This is a colon separated list of directories that contain frameworks. +The dynamic linker searches these directories before it searches for the +framework by its install name. +It allows you to test new versions of existing +frameworks. (A framework is a library install name that ends in the form +XXX.framework/Versions/YYY/XXX or XXX.framework/XXX, where XXX and YYY are any +name.) +.IP +For each framework that a program uses, the dynamic linker looks for the +framework in each directory in +.SM DYLD_FRAMEWORK_PATH +in turn. If it looks in all the directories and can't find the framework, it +searches the directories in +.SM DYLD_LIBRARY_PATH +in turn. If it still can't find the framework, it then searches +.SM DYLD_FALLBACK_FRAMEWORK_PATH +and +.SM DYLD_FALLBACK_LIBRARY_PATH +in turn. +.IP +Use the +.B \-L +option to +.IR otool (1). +to discover the frameworks and shared libraries that the executable +is linked against. +.TP +.B DYLD_FALLBACK_FRAMEWORK_PATH +This is a colon separated list of directories that contain frameworks. +It is used as the default location for frameworks not found in their install +path. + +By default, it is set to +/Library/Frameworks:/Network/Library/Frameworks:/System/Library/Frameworks +.TP +.B DYLD_VERSIONED_FRAMEWORK_PATH +This is a colon separated list of directories that contain potential override frameworks. +The dynamic linker searches these directories for frameworks. For +each framework found dyld looks at its LC_ID_DYLIB and gets the current_version +and install name. Dyld then looks for the framework at the install name path. +Whichever has the larger current_version value will be used in the process whenever +a framework with that install name is required. This is similar to DYLD_FRAMEWORK_PATH +except instead of always overriding, it only overrides is the supplied framework is newer. +Note: dyld does not check the framework's Info.plist to find its version. Dyld only +checks the -currrent_version number supplied when the framework was created. +.TP +.B DYLD_LIBRARY_PATH +This is a colon separated list of directories that contain libraries. The +dynamic linker searches these directories before it searches the default +locations for libraries. It allows you to test new versions of existing +libraries. +.IP +For each library that a program uses, the dynamic linker looks for it in each +directory in +.SM DYLD_LIBRARY_PATH +in turn. If it still can't find the library, it then searches +.SM DYLD_FALLBACK_FRAMEWORK_PATH +and +.SM DYLD_FALLBACK_LIBRARY_PATH +in turn. +.IP +Use the +.B \-L +option to +.IR otool (1). +to discover the frameworks and shared libraries that the executable +is linked against. +.TP +.B DYLD_FALLBACK_LIBRARY_PATH +This is a colon separated list of directories that contain libraries. +It is used as the default location for libraries not found in their install +path. +By default, it is set +to $(HOME)/lib:/usr/local/lib:/lib:/usr/lib. +.TP +.B DYLD_VERSIONED_LIBRARY_PATH +This is a colon separated list of directories that contain potential override libraries. +The dynamic linker searches these directories for dynamic libraries. For +each library found dyld looks at its LC_ID_DYLIB and gets the current_version +and install name. Dyld then looks for the library at the install name path. +Whichever has the larger current_version value will be used in the process whenever +a dylib with that install name is required. This is similar to DYLD_LIBRARY_PATH +except instead of always overriding, it only overrides is the supplied library is newer. +.TP +.B DYLD_PRINT_TO_FILE +This is a path to a (writable) file. Normally, the dynamic linker writes all +logging output (triggered by DYLD_PRINT_* settings) to file descriptor 2 +(which is usually stderr). But this setting causes the dynamic linker to +write logging output to the specified file. +.TP +.B DYLD_ROOT_PATH +This is a colon separated list of directories. The dynamic linker will prepend each of +this directory paths to every image access until a file is found. +.TP +.B DYLD_SHARED_REGION +This can be "use" (the default), "avoid", or "private". Setting it to +"avoid" tells dyld to not use the shared cache. All OS dylibs are loaded +dynamically just like every other dylib. Setting it to "private" tells +dyld to remove the shared region from the process address space and mmap() +back in a private copy of the dyld shared cache in the shared region address +range. This is only useful if the shared cache on disk has been updated +and is different than the shared cache in use. +.TP +.B DYLD_INSERT_LIBRARIES +This is a colon separated list of dynamic libraries to load before the ones +specified in the program. This lets you test new modules of existing dynamic +shared libraries that are used in flat-namespace images by loading a temporary +dynamic shared library with just the new modules. Note that this has no +effect on images built a two-level namespace images using a dynamic shared +library unless +.SM DYLD_FORCE_FLAT_NAMESPACE +is also used. +.TP +.B DYLD_FORCE_FLAT_NAMESPACE +Force all images in the program to be linked as flat-namespace images and ignore +any two-level namespace bindings. This may cause programs to fail to execute +with a multiply defined symbol error if two-level namespace images are used to +allow the images to have multiply defined symbols. +.TP +.B DYLD_IMAGE_SUFFIX +This is set to a string of a suffix to try to be used for all shared libraries +used by the program. For libraries ending in ".dylib" the suffix is applied +just before the ".dylib". For all other libraries the suffix is appended to the +library name. This is useful for using conventional "_profile" and "_debug" +libraries and frameworks. +.TP +.B DYLD_PRINT_OPTS +When this is set, the dynamic linker writes to file descriptor 2 (normally +standard error) the command line options. +.TP +.B DYLD_PRINT_ENV +When this is set, the dynamic linker writes to file descriptor 2 (normally +standard error) the environment variables. +.TP +.B DYLD_PRINT_LIBRARIES +When this is set, the dynamic linker writes to file descriptor 2 (normally +standard error) the filenames of the libraries the program is using. +This is useful to make sure that the use of +.SM DYLD_LIBRARY_PATH +is getting what you want. +.TP +.B DYLD_PRINT_LIBRARIES_POST_LAUNCH +This does the same as +.SM DYLD_PRINT_LIBRARIES +but the printing starts after the program gets to its entry point. +.TP +.B DYLD_BIND_AT_LAUNCH +When this is set, the dynamic linker binds all undefined symbols +the program needs at launch time. This includes function symbols that can are normally +lazily bound at the time of their first call. +.TP +.B DYLD_PRINT_STATISTICS +Right before the process's main() is called, dyld prints out information about how +dyld spent its time. Useful for analyzing launch performance. +.TP +.B DYLD_PRINT_STATISTICS_DETAILS +Right before the process's main() is called, dyld prints out detailed information about how +dyld spent its time. Useful for analyzing launch performance. +.TP +.B DYLD_DISABLE_DOFS +Causes dyld not register dtrace static probes with the kernel. +.TP +.B DYLD_PRINT_INITIALIZERS +Causes dyld to print out a line when running each initializers in every image. Initializers +run by dyld included constructors for C++ statically allocated objects, functions marked with +__attribute__((constructor)), and -init functions. +.TP +.B DYLD_PRINT_APIS +Causes dyld to print a line whenever a dyld API is called (e.g. NSAddImage()). +.TP +.B DYLD_PRINT_SEGMENTS +Causes dyld to print out a line containing the name and address range of each mach-o segment +that dyld maps. In addition it prints information about if the image was from the dyld +shared cache. +.TP +.B DYLD_PRINT_BINDINGS +Causes dyld to print a line each time a symbolic name is bound. +.TP +.B DYLD_PRINT_DOFS +Causes dyld to print out information about dtrace static probes registered with the kernel. +.TP +.B DYLD_PRINT_RPATHS +Cause dyld to print a line each time it expands an @rpath variable and whether +that expansion was successful or not. +.TP +.B DYLD_SHARED_CACHE_DIR +This is a directory containing dyld shared cache files. This variable can be used in +conjunction with DYLD_SHARED_REGION=private and DYLD_SHARED_CACHE_DONT_VALIDATE +to run a process with an alternate shared cache. +.TP +.B DYLD_SHARED_CACHE_DONT_VALIDATE +Causes dyld to not check that the inode and mod-time of files in the shared cache match +the requested dylib on disk. Thus a program can be made to run with the dylib in the +shared cache even though the real dylib has been updated on disk. +.TP +.SH DYNAMIC LIBRARY LOADING +Unlike many other operating systems, Darwin does not locate dependent dynamic libraries +via their leaf file name. Instead the full path to each dylib is used (e.g. /usr/lib/libSystem.B.dylib). +But there are times when a full path is not appropriate; for instance, may want your +binaries to be installable in anywhere on the disk. +To support that, there are three @xxx/ variables that can be used as a path prefix. At runtime dyld +substitutes a dynamically generated path for the @xxx/ prefix. +.TP +.B @executable_path/ +This variable is replaced with the path to the directory containing the main executable for +the process. This is useful for loading dylibs/frameworks embedded in a .app directory. +If the main executable file is at /some/path/My.app/Contents/MacOS/My and a framework dylib +file is at /some/path/My.app/Contents/Frameworks/Foo.framework/Versions/A/Foo, then +the framework load path could be encoded as +@executable_path/../Frameworks/Foo.framework/Versions/A/Foo and the .app directory could be +moved around in the file system and dyld will still be able to load the embedded framework. +.TP +.B @loader_path/ +This variable is replaced with the path to the directory containing the mach-o binary which +contains the load command using @loader_path. Thus, in every binary, @loader_path resolves to +a different path, whereas @executable_path always resolves to the same path. @loader_path is +useful as the load path for a framework/dylib embedded in a plug-in, if the final file +system location of the plugin-in unknown (so absolute paths cannot be used) or if the plug-in +is used by multiple applications (so @executable_path cannot be used). If the plug-in mach-o +file is at /some/path/Myfilter.plugin/Contents/MacOS/Myfilter and a framework dylib +file is at /some/path/Myfilter.plugin/Contents/Frameworks/Foo.framework/Versions/A/Foo, then +the framework load path could be encoded as +@loader_path/../Frameworks/Foo.framework/Versions/A/Foo and the Myfilter.plugin directory could +be moved around in the file system and dyld will still be able to load the embedded framework. +.TP +.B @rpath/ +Dyld maintains a current stack of paths called the run path list. When @rpath is encountered +it is substituted with each path in the run path list until a loadable dylib if found. +The run path stack is built from the LC_RPATH load commands in the depencency chain +that lead to the current dylib load. +You can add an LC_RPATH load command to an image with the -rpath option to ld(1). You can +even add a LC_RPATH load command path that starts with @loader_path/, and it will push a path +on the run path stack that relative to the image containing the LC_RPATH. +The use of @rpath is most useful when you have a complex directory structure of programs and +dylibs which can be installed anywhere, but keep their relative positions. This scenario +could be implemented using @loader_path, but every client of a dylib could need a different +load path because its relative position in the file system is different. The use of @rpath +introduces a level of indirection that simplies things. You pick a location in your directory +structure as an anchor point. Each dylib then gets an install path that starts with @rpath +and is the path to the dylib relative to the anchor point. Each main executable is linked +with -rpath @loader_path/zzz, where zzz is the path from the executable to the anchor point. +At runtime dyld sets it run path to be the anchor point, then each dylib is found relative +to the anchor point. +.SH "SEE ALSO" +libtool(1), ld(1), otool(1) diff --git a/src/dyld-apple/doc/man/man1/update_dyld_shared_cache.1 b/src/dyld-apple/doc/man/man1/update_dyld_shared_cache.1 new file mode 100644 index 000000000..7161fc5c0 --- /dev/null +++ b/src/dyld-apple/doc/man/man1/update_dyld_shared_cache.1 @@ -0,0 +1,90 @@ +.Dd June 23, 2016 +.Dt update_dyld_shared_cache 1 +.Os Darwin +.Sh NAME +.Nm update_dyld_shared_cache +.Nd "Updates dyld's shared cache" +.Sh SYNOPSIS +.Nm +.Op Fl root Ar directory +.Op Fl overlay Ar directory +.Op Fl arch Ar arch +.Op Fl force +.Op Fl debug +.Op Fl universal_boot +.Op Fl verify +.Sh DESCRIPTION +.Nm update_dyld_shared_cache +ensures that dyld's shared cache is up-to-date. This tool is normally +only run by Apple's Installer and Software Update, as they are the only +official ways for OS dylibs to be updated. But if for some reason you +used another mechanism to alter an OS dylib, you should manually run +.Nm update_dyld_shared_cache . +.Pp +Note that the new cache does not take effect until the OS is rebooted. +.Pp +If a safe-boot is +done (booting with shift key held down) the cache is deleted. +.Pp +The dyld shared cache +is mapped by dyld into a process at launch time. Later, when loading +any mach-o image, dyld will first check if is in the share cache, and if +it is will use that pre-bound version instead of opening, mapping, and binding +the original file. This results in significant performance improvements to +launch time. +.Pp +.Nm update_dyld_shared_cache +scans the directory /var/db/dyld/shared_region_roots for text files containing paths to +mach-o executables. The full dependencies of all dylibs required by those executables is +used to determine which libraries are commonly used and should be placed in the +shared cache. If one of the text files contains a path to a dylib, that dylib and its +dependents will be forced into the cache. +.Pp +.Nm update_dyld_shared_cache +builds a separate cache file for each architecture. The cache files and a readable text +map of the cached are generated to /var/db/dyld. +.Pp +You must be root to run this tool. +.Pp +The options are as follows: +.Bl -tag +.It Fl root Ar directory +This option specifies the root of an OS installation for which dyld's +shared cache should be updated. This is used by the Installer to update the +dyld shared cache in a partition other than the one you into which you are currently +booted. The cache files are created in the var/db/dyld directory of the specified directory. +Note: if you are manually doing this, be sure to run the update_dyld_shared_cache tool +that is in the partition being updated. This assures the cache format created will +match that expected when booting off that partition. +.It Fl overlay Ar directory +This option specifies the root of a sparse directory tree. When building +the dyld shared cache, any corresponding mach-o files in the sparse directory +will override those in the boot partition. This is used by Software +Update to build a dyld shared cache for the update that is about to be +installed. The cache files +are created in the var/db/dyld directory of the specified directory. +.It Fl arch Ar arch +By default +.Nm update_dyld_shared_cache +generates cache files for all architecture that the current machine +can execute. You can override this behavior by specifying one or more -arch options and list +exactly which architectures should have their shared caches updated. +.It Fl force +This option will cause +.Nm update_dyld_shared_cache +to regenerated the shared cache files even if they appear to be already up-to-date. +.It Fl debug +This option prints out additional information about the work being done. +.It Fl universal_boot +This option builds caches for all machines. +.It Fl verify +Will regenerate a shared cache in-memory that matches the randomization of the existing shared +cache file. Then instead of writing the cache file, it compares the in-memory cache file to +the on disk version and reports any differences. +.El +.Sh FILES +.Tp +/var/db/dyld/shared_region_roots +directory of text files with paths to mach-o images used to determine what should be in shared cache. +.Sh SEE ALSO +.Xr dyld 1 diff --git a/src/dyld-apple/doc/man/man3/dladdr.3 b/src/dyld-apple/doc/man/man3/dladdr.3 new file mode 100644 index 000000000..bca446298 --- /dev/null +++ b/src/dyld-apple/doc/man/man3/dladdr.3 @@ -0,0 +1,73 @@ +.Dd September 24, 2004 +.Os +.Dt DLADDR 3 +.Sh NAME +.Nm dladdr +.Nd find the image containing a given address +.Sh SYNOPSIS +.In dlfcn.h +.Ft int +.Fn dladdr "const void* addr" "Dl_info* info" +.Sh DESCRIPTION +The +.Fn dladdr +function +queries dyld (the dynamic linker) for information about the image +containing the address +.Fa addr . +The information is returned in the structure specified by +.Fa info . +The structure contains at least the following members: +.Bl -tag -width "XXXconst char *dli_fname" +.It Li "const char* dli_fname" +The pathname of the shared object containing the address. +.It Li "void* dli_fbase" +The base address (mach_header) at which the image is mapped into the +address space of the calling process. +.It Li "const char* dli_sname" +The name of the nearest run-time symbol with a value less than or +equal to +.Fa addr . +.It Li "void* dli_saddr" +The value of the symbol returned in +.Li dli_sname . +.El +.Pp +The +.Fn dladdr +function +is available only in dynamically linked programs. +.Sh ERRORS +If an image containing +.Fa addr +cannot be found, +.Fn dladdr +returns 0. +On success, a non-zero value is returned. +.Pp +If the image containing +.Fa addr +is found, but no nearest symbol was found, +the dli_sname and dli_saddr fields are set to NULL. +.Sh SEE ALSO +.Xr dyld 3 , +.Xr dlopen 3 +.Sh HISTORY +The +.Fn dladdr +function first appeared in the Solaris operating system. +.Sh AUTHORS +Mac OS X 10.3 incorporated the dlcompat package written by Jorge Acereda +and Peter O'Gorman . +.Pp +In Mac OS X 10.4, dlopen was rewritten to be a native part of dyld. +.Pp +This man page was borrowed from FreeBSD and modified. +.Sh BUGS +This implementation is almost bug-compatible with the Solaris +implementation. The following bugs are present: +.Bl -bullet +.It +Returning 0 as an indication of failure goes against long-standing +Unix tradition. +.El diff --git a/src/dyld-apple/doc/man/man3/dlclose.3 b/src/dyld-apple/doc/man/man3/dlclose.3 new file mode 100644 index 000000000..1200e8086 --- /dev/null +++ b/src/dyld-apple/doc/man/man3/dlclose.3 @@ -0,0 +1,43 @@ +.Dd Nov 6, 2006 +.Dt DLCLOSE 3 +.Sh NAME +.Nm dlclose +.Nd close a dynamic library or bundle +.Sh SYNOPSIS +.In dlfcn.h +.Ft int +.Fn dlclose "void* handle" +.Sh DESCRIPTION +.Fn dlclose +releases a reference to the dynamic library or bundle referenced by +.Fa handle . +If the reference count drops to 0, the bundle is removed from the +address space, and +.Fa handle +is rendered invalid. +Just before removing a dynamic library or bundle in this way, any +termination routines in it are called. +.Fa handle +is the value returned by a previous call to dlopen. +.Pp +Prior to Mac OS X 10.5, only bundles could be unloaded. Starting in Mac OS X 10.5, +dynamic libraries may also be unloaded. There are a couple of cases in which a +dynamic library will never be unloaded: 1) the main executable links against it, +2) An API that does not supoort unloading (e.g. NSAddImage()) was used to load +it or some other dynnamic library that depends on it, 3) the dynamic library +is in dyld's shared cache. +.Sh RETURN VALUES +If +.Fn dlclose +is successful, it returns a value of 0. +Otherwise it returns -1, and sets an error string that can be +retrived with +.Fn dlerror . +.Pp +.Sh SEE ALSO +.Xr dlopen 3 +.Xr dlsym 3 +.Xr dlerror 3 +.Xr dyld 3 +.Xr ld 1 +.Xr cc 1 diff --git a/src/dyld-apple/doc/man/man3/dlerror.3 b/src/dyld-apple/doc/man/man3/dlerror.3 new file mode 100644 index 000000000..758cc1830 --- /dev/null +++ b/src/dyld-apple/doc/man/man3/dlerror.3 @@ -0,0 +1,34 @@ +.Dd April 17, 2006 +.Dt DLERROR 3 +.Sh NAME +.Nm dlerror +.Nd get diagnostic information +.Sh SYNOPSIS +.In dlfcn.h +.Ft const char* +.Fn dlerror "void" +.Sh DESCRIPTION +.Fn dlerror +returns a null-terminated character string describing the last error that +occurred on this thread during a call to +.Fn dlopen , +.Fn dlopen_preflight , +.Fn dlsym , +or +.Fn dlclose . +If no such error has occurred, +.Fn dlerror +returns a null pointer. +At each call to +.Fn dlerror , +the error indication is reset. Thus in the case of two calls +to +.Fn dlerror , +where the second call follows the first immediately, the second call +will always return a null pointer. +.Sh SEE ALSO +.Xr dlopen 3 +.Xr dlopen_preflight 3 +.Xr dlclose 3 +.Xr dlsym 3 +.Xr dyld 3 diff --git a/src/dyld-apple/doc/man/man3/dlopen.3 b/src/dyld-apple/doc/man/man3/dlopen.3 new file mode 100644 index 000000000..1da4927ae --- /dev/null +++ b/src/dyld-apple/doc/man/man3/dlopen.3 @@ -0,0 +1,187 @@ +.Dd Aug 7, 2012 +.Os +.Dt DLOPEN 3 +.Sh NAME +.Nm dlopen +.Nd load and link a dynamic library or bundle +.Sh SYNOPSIS +.In dlfcn.h +.Ft void* +.Fn dlopen "const char* path" "int mode" +.Sh DESCRIPTION +.Fn dlopen +examines the mach-o file specified by +.Fa path . +If the file is compatible with the current process and has not already been +loaded into the current process, it is loaded and linked. After being linked, +if it contains any initializer functions, they are called, before +.Fn dlopen +returns. +.Fn dlopen +can load dynamic libraries and bundles. It returns a handle that can +be used with +.Fn dlsym +and +.Fn dlclose . +A second call to +.Fn dlopen +with the same path will return the same handle, but the internal reference +count for the handle will be incremented. Therefore all +.Fn dlopen +calls should be balanced with a +.Fn dlclose +call. +.Pp +If a null pointer is passed in +.Fa path , +.Fn dlopen +returns a handle equivalent to RTLD_DEFAULT. +.Pp +.Fa mode +contains options to +.Fn dlopen . +It must contain one or more of the following values, possibly ORed together: +.Pp +.Bl -tag -width RTLD_LAZYX +.It Dv RTLD_LAZY +Each external function reference is bound the first time the function is called. +.It Dv RTLD_NOW +All external function references are bound immediately during the call to +.Fn dlopen . +.El +.Pp +.Dv RTLD_LAZY +is normally preferred, for reasons of efficiency. +However, +.Dv RTLD_NOW +is useful to ensure that any undefined symbols are discovered during the +call to +.Fn dlopen . +If neither +RTLD_LAZY nor RTLD_NOW is specified, the default is RTLD_LAZY. +.Pp +One of the following flags may be ORed into the +.Fa mode +argument: +.Bl -tag -width RTLD_LOCALX +.It Dv RTLD_GLOBAL +Symbols exported from this image (dynamic library or bundle) will be available to any +images build with -flat_namespace option to +.Xr ld 1 +or to calls to +.Fn dlsym +when using a special handle. +.It Dv RTLD_LOCAL +Symbols exported from this image (dynamic library or bundle) are generally hidden +and only availble to +.Fn dlsym +when directly using the handle returned by this call to +.Fn dlopen . +.Pp +.El +If neither +RTLD_GLOBAL nor RTLD_LOCAL is specified, the default is RTLD_GLOBAL. +.Pp +One of the following may be ORed into the +.Fa mode +argument: +.Bl -tag -width RTLD_NODELETEX +.It Dv RTLD_NOLOAD +The specified image is not loaded. However, a valid +.Fa handle +is returned if the image already exists in the process. This provides a way +to query if an image is already loaded. The +.Fa handle +returned is ref-counted, so you eventually need a corresponding call to +.Fn dlclose +.It Dv RTLD_NODELETE +The specified image is tagged so that will never be removed from the address space, +even after all clients have released it via +.Fn dlclose +.El +.Pp +Additionally, the following may be ORed into the +.Fa mode +argument: +.Bl -tag -width RTLD_FIRSTX +.It Dv RTLD_FIRST +The retuned +.Fa handle +is tagged so that any +.Fn dlsym +calls on the +.Fa handle +will only search the image specified, and not subsequent images. If +.Fa path +is NULL and the option RTLD_FIRST is used, the +.Fa handle +returned will only search the main executable. +.El +.Sh SEARCHING +.Fn dlopen +searches for a compatible Mach-O file in the directories specified by a set of environment variables and +the process's current working directory. +When set, the environment variables contain a colon-separated list of directory paths, +which can be absolute or relative to the current working directory. +.Pp +When +.Fa path +does not contain a slash character (i.e. it is just a leaf name), +.Fn dlopen +searches the following until it finds a compatible Mach-O file: $LD_LIBRARY_PATH, +$DYLD_LIBRARY_PATH, current working directory, $DYLD_FALLBACK_LIBRARY_PATH. +.Pp +When +.Fa path +looks like a framework path (e.g. /stuff/foo.framework/foo), +.Fn dlopen +searches the following until it finds a compatible Mach-O file: +$DYLD_FRAMEWORK_PATH (with framework partial path from +.Fa path +), then the supplied +.Fa path +(using current working directory for relative paths), then +$DYLD_FALLBACK_FRAMEWORK_PATH (with framework partial path from +.Fa path +). +.Pp +When +.Fa path +contains a slash but is not a framework path (i.e. a full path or a partial path to a dylib), +.Fn dlopen +searches the following until it finds a compatible Mach-O file: +$DYLD_LIBRARY_PATH (with leaf name from +.Fa path +), then the supplied +.Fa path +(using current working directory for relative paths), then +$DYLD_FALLBACK_LIBRARY_PATH (with leaf name from +.Fa path +). +.Pp +Note: If DYLD_FALLBACK_LIBRARY_PATH is not set, dlopen operates as if +DYLD_FALLBACK_LIBRARY_PATH was set to $HOME/lib:/usr/local/lib:/usr/lib. +.Pp +Note: If DYLD_FALLBACK_FRAMEWORK_PATH is not set, dlopen operates as if +DYLD_FALLBACK_FRAMEWORK_PATH was set to $HOME/Library/Frameworks:/Library/Frameworks:/Network/Library/Frameworks:/System/Library/Frameworks. +.Pp +Note: There are no configuration files to control dlopen searching. +.Pp +Note: If the main executable is a set[ug]id binary or codesigned with entitlements, +then all environment variables are ignored, and only a full path can be used. +.Pp +Note: Mac OS X uses "universal" files to combine 32-bit and 64-bit libraries. This means there are no separate 32-bit and 64-bit search paths. +.Pp +.Sh RETURN VALUES +If +.Fn dlopen +fails, it returns a null pointer, and sets an error condition which may be interrogated with +.Fn dlerror . +.Pp +.Sh SEE ALSO +.Xr dlopen_preflight 3 +.Xr dlclose 3 +.Xr dlsym 3 +.Xr dlerror 3 +.Xr dyld 1 +.Xr ld 1 diff --git a/src/dyld-apple/doc/man/man3/dlopen_preflight.3 b/src/dyld-apple/doc/man/man3/dlopen_preflight.3 new file mode 100644 index 000000000..aa27dbfc7 --- /dev/null +++ b/src/dyld-apple/doc/man/man3/dlopen_preflight.3 @@ -0,0 +1,33 @@ +.Dd April 17, 2006 +.Os +.Dt DLOPEN_PREFLIGHT 3 +.Sh NAME +.Nm dlopen_preflight +.Nd preflight the load of a dynamic library or bundle +.Sh SYNOPSIS +.In dlfcn.h +.Ft bool +.Fn dlopen_preflight "const char* path" +.Sh DESCRIPTION +.Fn dlopen_preflight +examines the mach-o file specified by +.Fa path . +It checks if the file and libraries it depends on are all compatible with the current process. +That is, they contain the correct architecture and are not otherwise ABI incompatible. +.Pp +.Fn dlopen_preflight +was first available in Mac OS X 10.5. +.Sh SEARCHING +.Fn dlopen_preflight +uses the same steps as +.Fn dlopen +to find a compatible mach-o file. +.Sh RETURN VALUES +.Fn dlopen_preflight +returns true on if the mach-o file is compatible. If the file is not compatible, it returns false +and sets an error string that can be examined with +.Fn dlerror . +.Pp +.Sh SEE ALSO +.Xr dlopen 3 +.Xr dlerror 3 diff --git a/src/dyld-apple/doc/man/man3/dlsym.3 b/src/dyld-apple/doc/man/man3/dlsym.3 new file mode 100644 index 000000000..5f7c4ed26 --- /dev/null +++ b/src/dyld-apple/doc/man/man3/dlsym.3 @@ -0,0 +1,94 @@ +.Dd August 28, 2008 +.Dt DLSYM 3 +.Sh NAME +.Nm dlsym +.Nd get address of a symbol +.Sh SYNOPSIS +.In dlfcn.h +.Ft void* +.Fn dlsym "void* handle" "const char* symbol" +.Sh DESCRIPTION +.Fn dlsym +returns the address of the code or data location +specified by the null-terminated character string +.Fa symbol . +Which libraries and bundles are searched depends on the +.Fa handle +parameter. +.Pp +If +.Fn dlsym +is called with a +.Fa handle , +returned by +.Fn dlopen +then only that image and any libraries it depends on are searched for +.Fa symbol . +.Pp +If +.Fn dlsym +is called with the special +.Fa handle +.Dv RTLD_DEFAULT , +then all mach-o images in the process (except those loaded with dlopen(xxx, RTLD_LOCAL)) +are searched in the order they were loaded. +This can be a costly search and should be avoided. +.Pp +If +.Fn dlsym +is called with the special +.Fa handle +.Dv RTLD_NEXT , +then dyld searches for the symbol in the dylibs the calling image +linked against when built. It is usually used when +you intentionally have multiply defined symbol across images +and want to find the "next" definition. It searches other images +for the definition that the caller would be using if it did not +have a definition. The exact search algorithm depends on whether +the caller's image was linked -flat_namespace or -twolevel_namespace. +For flat linked images, the search starts in the load ordered list +of all images, in the image right after the caller's image. +For two-level images, the search simulates how the static linker +would have searched for the symbol when linking the caller's +image. +.Pp +If +.Fn dlsym +is called with the special +.Fa handle +.Dv RTLD_SELF , +then the search for the symbol starts with the image that called +.Fn dlsym . +If it is not found, the search continues as if RTLD_NEXT was used. +.Pp +If +.Fn dlsym +is called with the special +.Fa handle +.Dv RTLD_MAIN_ONLY , +then it only searches for +.Fa symbol +in the main executable. +.Pp +.Sh RETURN VALUES +The +.Fn dlsym +function +returns a null pointer if the symbol cannot be found, and sets an error +condition which may be queried with +.Fn dlerror . +.Pp +.Sh NOTES +The symbol name passed to +.Fn dlsym +is the name used in C source code. For example to find the address +of function foo(), you would pass "foo" as the symbol name. This +is unlike the older dyld APIs which required a leading underscore. +If you looking up a C++ symbol, you need to use the mangled C++ symbol +name. +.Sh SEE ALSO +.Xr dlopen 3 +.Xr dlerror 3 +.Xr dyld 3 +.Xr ld 1 +.Xr cc 1 diff --git a/src/dyld-apple/doc/man/man3/dyld.3 b/src/dyld-apple/doc/man/man3/dyld.3 new file mode 100644 index 000000000..cb9f6e4ba --- /dev/null +++ b/src/dyld-apple/doc/man/man3/dyld.3 @@ -0,0 +1,126 @@ +.Dd November 29, 2010 +.Dt dyld 3 +.Sh NAME +.Nm _dyld_image_count, +.Nm _dyld_get_image_header, +.Nm _dyld_get_image_vmaddr_slide, +.Nm _dyld_get_image_name, +.Nm _dyld_register_func_for_add_image, +.Nm _dyld_register_func_for_remove_image, +.Nm NSVersionOfRunTimeLibrary, +.Nm NSVersionOfLinkTimeLibrary +.Nm _NSGetExecutablePath +.Sh SYNOPSIS +.In mach-o/dyld.h +.Ft uint32_t +.Fo _dyld_image_count +.Fa "void" +.Fc +.Ft const struct mach_header* +.Fo _dyld_get_image_header +.Fa "uint32_t image_index" +.Fc +.Ft intptr_t +.Fo _dyld_get_image_vmaddr_slide +.Fa "uint32_t image_index" +.Fc +.Ft const char* +.Fo _dyld_get_image_name +.Fa "uint32_t image_index" +.Fc +.Ft void +.Fo _dyld_register_func_for_add_image +.Fa "void \*[lp]*func\*[rp]\*[lp]const struct mach_header* mh, intptr_t vmaddr_slide\*[rp]" +.Fc +.Ft void +.Fo _dyld_register_func_for_remove_image +.Fa "void \*[lp]*func\*[rp]\*[lp]const struct mach_header* mh, intptr_t vmaddr_slide\*[rp]" +.Fc +.Ft int32_t +.Fo NSVersionOfRunTimeLibrary +.Fa "const char* libraryName" +.Fc +.Ft int32_t +.Fo NSVersionOfLinkTimeLibrary +.Fa "const char* libraryName" +.Fc +.Ft int +.Fo _NSGetExecutablePath +.Fa "char* buf" +.Fa "uint32_t* bufsize" +.Fc +.Sh DESCRIPTION +These routines provide additional introspection of dyld beyond that provided by +.Fn dlopen +and +.Fn dladdr +. +.Pp +.Fn _dyld_image_count +returns the current number of images mapped in by dyld. Note that using this +count to iterate all images is not thread safe, because another thread +may be adding or removing images during the iteration. +.Pp +.Fn _dyld_get_image_header +returns a pointer to the mach header of the image indexed by image_index. If +.Fa image_index +is out of range, NULL is returned. +.Pp +.Fn _dyld_get_image_vmaddr_slide +returns the virtural memory address slide amount of the image indexed by +.Fa image_index. +If +.Fa image_index +is out of range zero is returned. +.Pp +.Fn _dyld_get_image_name +returns the name of the image indexed by +.Fa image_index. +The C-string continues to be owned by dyld and should not deleted. +If +.Fa image_index +is out of range NULL is returned. +.Pp +.Fn _dyld_register_func_for_add_image +registers the specified function to be called when a new image is added +(a bundle or a dynamic shared library) to the program. When this function is +first registered it is called for once for each image that is currently part of +the process. +.Pp +.Fn _dyld_register_func_for_remove_image +registers the specified function to be called when an image is removed +(a bundle or a dynamic shared library) from the process. +.Pp +.Fn NSVersionOfRunTimeLibrary +returns the current_version number of the currently loaded dylib +specifed by the libraryName. The libraryName parameter would be "bar" for /path/libbar.3.dylib and +"Foo" for /path/Foo.framework/Versions/A/Foo. This function returns -1 if no such library is loaded. +.Pp +.Fn NSVersionOfLinkTimeLibrary +returns the current_version number that the main executable was linked +against at build time. The libraryName parameter would be "bar" for /path/libbar.3.dylib and +"Foo" for /path/Foo.framework/Versions/A/Foo. This function returns -1 if the main executable did not link +against the specified library. +.Pp +.Fn _NSGetExecutablePath +copies the path of the main executable into the buffer +.Fa buf . +The +.Fa bufsize +parameter should initially be the size of the buffer. This function returns 0 if the path was successfully copied, +and * +.Fa bufsize +is left unchanged. +It returns -1 if the buffer is not large enough, and * +.Fa bufsize +is set to the size required. +Note that +.Fn _NSGetExecutablePath +will return "a path" to the executable not a "real path" to the executable. +That is, the path may be a symbolic link and not the real file. With deep directories the total bufsize +needed could be more than MAXPATHLEN. +.Sh SEE ALSO +.Xr dlopen 3 +.Xr dladdr 3 +.Xr dyld 1 +http://developer.apple.com/documentation/DeveloperTools/Conceptual/MachOTopics/index.html \ No newline at end of file diff --git a/src/dyld-apple/dyld.xcodeproj/project.pbxproj b/src/dyld-apple/dyld.xcodeproj/project.pbxproj new file mode 100644 index 000000000..89a7c1f77 --- /dev/null +++ b/src/dyld-apple/dyld.xcodeproj/project.pbxproj @@ -0,0 +1,2330 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXAggregateTarget section */ + 37A0AD0A1C15FFF500731E50 /* update_dyld_shared_cache */ = { + isa = PBXAggregateTarget; + buildConfigurationList = 37A0AD0B1C15FFF500731E50 /* Build configuration list for PBXAggregateTarget "update_dyld_shared_cache" */; + buildPhases = ( + ); + dependencies = ( + 37A0AD1B1C16004600731E50 /* PBXTargetDependency */, + 37A0AD131C16003600731E50 /* PBXTargetDependency */, + 37A0AD151C16003600731E50 /* PBXTargetDependency */, + 37A0AD171C16003600731E50 /* PBXTargetDependency */, + 37A0AD191C16003600731E50 /* PBXTargetDependency */, + 37A0AD111C16003600731E50 /* PBXTargetDependency */, + ); + name = update_dyld_shared_cache; + productName = update_dyld_shared_cache; + }; + F908134211D3ED0B00626CC1 /* libdyld */ = { + isa = PBXAggregateTarget; + buildConfigurationList = F908135211D3ED9000626CC1 /* Build configuration list for PBXAggregateTarget "libdyld" */; + buildPhases = ( + F908135111D3ED9000626CC1 /* usr|include|mach-o */, + F908137011D3FB5000626CC1 /* usr|include */, + F9C69EFC14EC8AB8009CAE2E /* usr|local|include */, + F908137111D3FB5000626CC1 /* usr|local|include|mach-o */, + F908137211D3FB5000626CC1 /* usr|share|man|man1 */, + F908137311D3FB5000626CC1 /* usr|share|man|man3 */, + ); + dependencies = ( + F9B4D78012AD9736000605A6 /* PBXTargetDependency */, + F908134811D3ED1A00626CC1 /* PBXTargetDependency */, + ); + name = libdyld; + productName = libdyld; + }; + F9ED4C920630A73900DF4E74 /* all */ = { + isa = PBXAggregateTarget; + buildConfigurationList = F9D8C7E5087B087300E93EFB /* Build configuration list for PBXAggregateTarget "all" */; + buildPhases = ( + ); + dependencies = ( + F9ED4CA70630A78A00DF4E74 /* PBXTargetDependency */, + F9ED4CA90630A78A00DF4E74 /* PBXTargetDependency */, + 37A0AD0F1C16000F00731E50 /* PBXTargetDependency */, + ); + name = all; + productName = all; + }; + F9F6F4271C1FB0A700BD8FED /* dyld_tests */ = { + isa = PBXAggregateTarget; + buildConfigurationList = F9F6F42A1C1FB0A700BD8FED /* Build configuration list for PBXAggregateTarget "dyld_tests" */; + buildPhases = ( + F9F6F42B1C1FB0AE00BD8FED /* build */, + ); + dependencies = ( + F97FF3661C237F97000ACDD2 /* PBXTargetDependency */, + ); + name = dyld_tests; + productName = dyld_tests; + }; +/* End PBXAggregateTarget section */ + +/* Begin PBXBuildFile section */ + 3703A1141B38C1B300ADBA7F /* AdjustForNewSegmentLocation.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9D0FE6D1A367093001E839B /* AdjustForNewSegmentLocation.cpp */; }; + 3703A1161B38C1B300ADBA7F /* BindAllImages.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9D0FE6E1A367093001E839B /* BindAllImages.cpp */; }; + 3703A1171B38C1B300ADBA7F /* OptimizerObjC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98E19031A3FC1A9008DB73F /* OptimizerObjC.cpp */; }; + 3703A1181B38C1B300ADBA7F /* OptimizerBranches.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98E18FF1A3FC1A9008DB73F /* OptimizerBranches.cpp */; }; + 3703A1191B38C1B300ADBA7F /* OptimizerLinkedit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98E19011A3FC1A9008DB73F /* OptimizerLinkedit.cpp */; }; + 3703A11A1B38C1B300ADBA7F /* MultiCacheBuilder.mm in Sources */ = {isa = PBXBuildFile; fileRef = 371D29811B2F53C8000BBE48 /* MultiCacheBuilder.mm */; }; + 3703A11B1B38C1B300ADBA7F /* FileCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9D0FE6F1A367093001E839B /* FileCache.cpp */; }; + 3703A11C1B38C1B300ADBA7F /* SharedCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9D0FE701A367093001E839B /* SharedCache.cpp */; }; + 3703A1261B38C22900ADBA7F /* dyld_shared_cache_builder.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3703A1251B38C22900ADBA7F /* dyld_shared_cache_builder.mm */; }; + 370C6E541BDEF08000387223 /* libspindump.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 370C6E531BDEF08000387223 /* libspindump.dylib */; settings = {ATTRIBUTES = (Required, ); }; }; + 370E5F421CC06CF8000158F2 /* Logging.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 370E5F401CC06CF8000158F2 /* Logging.cpp */; }; + 370E5F431CC06CF8000158F2 /* Logging.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 370E5F401CC06CF8000158F2 /* Logging.cpp */; }; + 370E5F441CC06CF8000158F2 /* Logging.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 370E5F401CC06CF8000158F2 /* Logging.cpp */; }; + 371D29821B2F53C8000BBE48 /* MultiCacheBuilder.mm in Sources */ = {isa = PBXBuildFile; fileRef = 371D29811B2F53C8000BBE48 /* MultiCacheBuilder.mm */; }; + 3733C9071BD98F6800420392 /* dsc_iterator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9F2A56E0F7AEEE300B7C9EB /* dsc_iterator.cpp */; }; + 3733C9081BD98F6900420392 /* dsc_iterator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9F2A56E0F7AEEE300B7C9EB /* dsc_iterator.cpp */; }; + 3733C9091BD98F6A00420392 /* dsc_iterator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9F2A56E0F7AEEE300B7C9EB /* dsc_iterator.cpp */; }; + 375E6F441C59DEFF001BB760 /* MachOProxy.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 376ABDB71C5930E7009F0011 /* MachOProxy.cpp */; }; + 376ABDB61C592CC0009F0011 /* Metabom.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 376ED1D71C46F2710051DD54 /* Metabom.framework */; }; + 376ABDBA1C5930E7009F0011 /* MachOProxy.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 376ABDB71C5930E7009F0011 /* MachOProxy.cpp */; }; + 376ABDBB1C5930E7009F0011 /* MachOProxy.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 376ABDB71C5930E7009F0011 /* MachOProxy.cpp */; }; + 376ED1D81C46F2710051DD54 /* Metabom.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 376ED1D71C46F2710051DD54 /* Metabom.framework */; }; + 377685F51AC4B27D00026E6C /* AdjustForNewSegmentLocation.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9D0FE6D1A367093001E839B /* AdjustForNewSegmentLocation.cpp */; }; + 377685F61AC4B27D00026E6C /* BindAllImages.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9D0FE6E1A367093001E839B /* BindAllImages.cpp */; }; + 377685F71AC4B27D00026E6C /* OptimizerObjC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98E19031A3FC1A9008DB73F /* OptimizerObjC.cpp */; }; + 377685F81AC4B27D00026E6C /* OptimizerBranches.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98E18FF1A3FC1A9008DB73F /* OptimizerBranches.cpp */; }; + 377685F91AC4B27D00026E6C /* OptimizerLinkedit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98E19011A3FC1A9008DB73F /* OptimizerLinkedit.cpp */; }; + 377685FA1AC4B27D00026E6C /* FileCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9D0FE6F1A367093001E839B /* FileCache.cpp */; }; + 377685FB1AC4B27D00026E6C /* SharedCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9D0FE701A367093001E839B /* SharedCache.cpp */; }; + 377686041AC4B2ED00026E6C /* multi_dyld_shared_cache_builder.mm in Sources */ = {isa = PBXBuildFile; fileRef = 377686031AC4B2ED00026E6C /* multi_dyld_shared_cache_builder.mm */; }; + 378EE3B11BE88C47001C99FB /* Bom.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 37F7A5961BB363820039043A /* Bom.framework */; }; + 378EE3B21BE88C4A001C99FB /* Bom.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 37F7A5961BB363820039043A /* Bom.framework */; }; + 37BF1D761B6168150048BC27 /* Manifest.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37BF1D731B6168150048BC27 /* Manifest.mm */; }; + 37BF1D771B6168150048BC27 /* Manifest.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37BF1D731B6168150048BC27 /* Manifest.mm */; }; + 37F7A5951BB362CA0039043A /* update_dyld_shared_cache.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37E93E291AAFB0B10080E640 /* update_dyld_shared_cache.mm */; }; + 37F7A5981BB364130039043A /* Manifest.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37BF1D731B6168150048BC27 /* Manifest.mm */; }; + 37F7A5991BB364130039043A /* MultiCacheBuilder.mm in Sources */ = {isa = PBXBuildFile; fileRef = 371D29811B2F53C8000BBE48 /* MultiCacheBuilder.mm */; }; + 37F7A59A1BB3642F0039043A /* FileCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9D0FE6F1A367093001E839B /* FileCache.cpp */; }; + 37F7A59B1BB3642F0039043A /* SharedCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9D0FE701A367093001E839B /* SharedCache.cpp */; }; + 37F7A59C1BB364530039043A /* OptimizerBranches.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98E18FF1A3FC1A9008DB73F /* OptimizerBranches.cpp */; }; + 37F7A59D1BB364530039043A /* OptimizerLinkedit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98E19011A3FC1A9008DB73F /* OptimizerLinkedit.cpp */; }; + 37F7A59E1BB364530039043A /* OptimizerObjC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98E19031A3FC1A9008DB73F /* OptimizerObjC.cpp */; }; + 37F7A59F1BB364530039043A /* AdjustForNewSegmentLocation.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9D0FE6D1A367093001E839B /* AdjustForNewSegmentLocation.cpp */; }; + 37F7A5A01BB364530039043A /* BindAllImages.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9D0FE6E1A367093001E839B /* BindAllImages.cpp */; }; + F908134C11D3ED6200626CC1 /* dyld.h in usr|include|mach-o */ = {isa = PBXBuildFile; fileRef = F9ED4CEA0630A80600DF4E74 /* dyld.h */; }; + F908134D11D3ED6200626CC1 /* dyld_images.h in usr|include|mach-o */ = {isa = PBXBuildFile; fileRef = F98D274C0AA79D7400416316 /* dyld_images.h */; }; + F908135911D3FA8700626CC1 /* dlfcn.h in usr|include */ = {isa = PBXBuildFile; fileRef = F99EE6AE06B48D4200BF1992 /* dlfcn.h */; }; + F908135D11D3FACD00626CC1 /* dyld-interposing.h in usr|local|include|mach-o */ = {isa = PBXBuildFile; fileRef = F918691408B16D2500E0F9DB /* dyld-interposing.h */; }; + F908135E11D3FACD00626CC1 /* dyld_cache_format.h in usr|local|include|mach-o */ = {isa = PBXBuildFile; fileRef = F93937400A94FC4700070A07 /* dyld_cache_format.h */; }; + F908135F11D3FACD00626CC1 /* dyld_gdb.h in usr|local|include|mach-o */ = {isa = PBXBuildFile; fileRef = F9ED4CE80630A80600DF4E74 /* dyld_gdb.h */; }; + F908136011D3FACD00626CC1 /* dyld_priv.h in usr|local|include|mach-o */ = {isa = PBXBuildFile; fileRef = F9ED4CE90630A80600DF4E74 /* dyld_priv.h */; }; + F908136411D3FB0300626CC1 /* dyld.1 in usr|share|man|man1 */ = {isa = PBXBuildFile; fileRef = EF799FE9070D27BB00F78484 /* dyld.1 */; }; + F908136811D3FB3A00626CC1 /* dladdr.3 in usr|share|man|man3 */ = {isa = PBXBuildFile; fileRef = EF799FEB070D27BB00F78484 /* dladdr.3 */; }; + F908136911D3FB3A00626CC1 /* dlclose.3 in usr|share|man|man3 */ = {isa = PBXBuildFile; fileRef = EF799FEC070D27BB00F78484 /* dlclose.3 */; }; + F908136A11D3FB3A00626CC1 /* dlerror.3 in usr|share|man|man3 */ = {isa = PBXBuildFile; fileRef = EF799FED070D27BB00F78484 /* dlerror.3 */; }; + F908136B11D3FB3A00626CC1 /* dlopen.3 in usr|share|man|man3 */ = {isa = PBXBuildFile; fileRef = EF799FEE070D27BB00F78484 /* dlopen.3 */; }; + F908136C11D3FB3A00626CC1 /* dlsym.3 in usr|share|man|man3 */ = {isa = PBXBuildFile; fileRef = EF799FEF070D27BB00F78484 /* dlsym.3 */; }; + F908136D11D3FB3A00626CC1 /* dyld.3 in usr|share|man|man3 */ = {isa = PBXBuildFile; fileRef = EF799FF0070D27BB00F78484 /* dyld.3 */; }; + F908136E11D3FB3A00626CC1 /* dlopen_preflight.3 in usr|share|man|man3 */ = {isa = PBXBuildFile; fileRef = F9E572000A66EF41007D9BE9 /* dlopen_preflight.3 */; }; + F913FADA0630A8AE00B7AE9D /* dyldAPIsInLibSystem.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F913FAD90630A8AE00B7AE9D /* dyldAPIsInLibSystem.cpp */; }; + F9280B7B1AB9DCA000B18AEC /* ImageLoaderMegaDylib.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9280B791AB9DCA000B18AEC /* ImageLoaderMegaDylib.cpp */; }; + F94DB9040F0A9B1700323715 /* ImageLoaderMachOClassic.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F94DB9000F0A9B1700323715 /* ImageLoaderMachOClassic.cpp */; }; + F94DB9050F0A9B1700323715 /* ImageLoaderMachOCompressed.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F94DB9020F0A9B1700323715 /* ImageLoaderMachOCompressed.cpp */; settings = {COMPILER_FLAGS = "-O3"; }; }; + F95090E51C5AD1E80031F81D /* dyld_process_info.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F95090E41C5AD1B30031F81D /* dyld_process_info.cpp */; }; + F958D4771C7FCE6700A0B199 /* dyld_process_info_notify.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F958D4761C7FCD4A00A0B199 /* dyld_process_info_notify.cpp */; }; + F97FF3601C236408000ACDD2 /* execserver.defs in Sources */ = {isa = PBXBuildFile; fileRef = F97FF35E1C236402000ACDD2 /* execserver.defs */; settings = {ATTRIBUTES = (Server, ); }; }; + F97FF3611C23640C000ACDD2 /* nocr.c in Sources */ = {isa = PBXBuildFile; fileRef = F97FF35F1C236402000ACDD2 /* nocr.c */; }; + F97FF3641C237F68000ACDD2 /* nocr.1 in install man page */ = {isa = PBXBuildFile; fileRef = F97FF3631C237F5C000ACDD2 /* nocr.1 */; }; + F98C78F00F7C02E8006257D2 /* dsc_iterator.h in usr|local|include|mach-o */ = {isa = PBXBuildFile; fileRef = F9F2A56F0F7AEEE300B7C9EB /* dsc_iterator.h */; }; + F99B8E630FEC11B400701838 /* dyld_shared_cache_util.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F99B8E620FEC11B400701838 /* dyld_shared_cache_util.cpp */; }; + F99B8EA30FEC1C4200701838 /* dsc_iterator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9F2A56E0F7AEEE300B7C9EB /* dsc_iterator.cpp */; }; + F9A221E70F3A6D7C00D15F73 /* dyldLibSystemGlue.c in Sources */ = {isa = PBXBuildFile; fileRef = F9A221E60F3A6D7C00D15F73 /* dyldLibSystemGlue.c */; }; + F9A6D6E4116F9DF20051CC16 /* threadLocalVariables.c in Sources */ = {isa = PBXBuildFile; fileRef = F9A6D6E2116F9DF20051CC16 /* threadLocalVariables.c */; }; + F9A6D70C116FBBD10051CC16 /* threadLocalHelpers.s in Sources */ = {isa = PBXBuildFile; fileRef = F9A6D70B116FBBD10051CC16 /* threadLocalHelpers.s */; }; + F9BA514B0ECE4F4200D1D62E /* dyld_stub_binder.s in Sources */ = {isa = PBXBuildFile; fileRef = F99EFC0D0EAD60E8001032B8 /* dyld_stub_binder.s */; }; + F9C69EFE14EC8AD2009CAE2E /* objc-shared-cache.h in usr|local|include */ = {isa = PBXBuildFile; fileRef = F9C69EFD14EC8ABF009CAE2E /* objc-shared-cache.h */; }; + F9CE307A1208F1B50098B590 /* dsc_extractor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9CE30781208F1B50098B590 /* dsc_extractor.cpp */; }; + F9CE307B1208F1C60098B590 /* dsc_extractor.h in usr|local|include|mach-o */ = {isa = PBXBuildFile; fileRef = F9CE30791208F1B50098B590 /* dsc_extractor.h */; }; + F9D1001814D8D13D00099D91 /* dsc_extractor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9CE30781208F1B50098B590 /* dsc_extractor.cpp */; }; + F9D1001D14D8D19500099D91 /* dsc_iterator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9F2A56E0F7AEEE300B7C9EB /* dsc_iterator.cpp */; }; + F9D238DB0A9E2FD0002B55C7 /* update_dyld_shared_cache.1 in usr|share|man|man1 */ = {isa = PBXBuildFile; fileRef = F9D238D90A9E19A0002B55C7 /* update_dyld_shared_cache.1 */; }; + F9D49CCC1458B95200F86ADD /* start_glue.s in Sources */ = {isa = PBXBuildFile; fileRef = F9D49CCB1458B95200F86ADD /* start_glue.s */; }; + F9ED4CD60630A7F100DF4E74 /* dyld_gdb.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9ED4CC60630A7F100DF4E74 /* dyld_gdb.cpp */; }; + F9ED4CD70630A7F100DF4E74 /* dyld.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9ED4CC70630A7F100DF4E74 /* dyld.cpp */; }; + F9ED4CD90630A7F100DF4E74 /* dyldAPIs.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9ED4CC90630A7F100DF4E74 /* dyldAPIs.cpp */; }; + F9ED4CDA0630A7F100DF4E74 /* dyldExceptions.c in Sources */ = {isa = PBXBuildFile; fileRef = F9ED4CCA0630A7F100DF4E74 /* dyldExceptions.c */; }; + F9ED4CDB0630A7F100DF4E74 /* dyldInitialization.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9ED4CCB0630A7F100DF4E74 /* dyldInitialization.cpp */; }; + F9ED4CDE0630A7F100DF4E74 /* dyldNew.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9ED4CCE0630A7F100DF4E74 /* dyldNew.cpp */; }; + F9ED4CDF0630A7F100DF4E74 /* dyldStartup.s in Sources */ = {isa = PBXBuildFile; fileRef = F9ED4CCF0630A7F100DF4E74 /* dyldStartup.s */; }; + F9ED4CE00630A7F100DF4E74 /* glue.c in Sources */ = {isa = PBXBuildFile; fileRef = F9ED4CD00630A7F100DF4E74 /* glue.c */; }; + F9ED4CE10630A7F100DF4E74 /* ImageLoader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9ED4CD10630A7F100DF4E74 /* ImageLoader.cpp */; }; + F9ED4CE30630A7F100DF4E74 /* ImageLoaderMachO.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9ED4CD30630A7F100DF4E74 /* ImageLoaderMachO.cpp */; }; + F9ED4CE50630A7F100DF4E74 /* stub_binding_helper.s in Sources */ = {isa = PBXBuildFile; fileRef = F9ED4CD50630A7F100DF4E74 /* stub_binding_helper.s */; }; + F9F256360639DBCC00A7427D /* dyldLock.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9ED4CCC0630A7F100DF4E74 /* dyldLock.cpp */; }; + F9F2A5700F7AEEE300B7C9EB /* dsc_iterator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9F2A56E0F7AEEE300B7C9EB /* dsc_iterator.cpp */; }; + F9FF8C161C69B080009F8A53 /* dyld_process_info.h in usr|local|include|mach-o */ = {isa = PBXBuildFile; fileRef = F95090D01C5AB89A0031F81D /* dyld_process_info.h */; }; +/* End PBXBuildFile section */ + +/* Begin PBXBuildRule section */ + F921D3160703769A000D1056 /* PBXBuildRule */ = { + isa = PBXBuildRule; + compilerSpec = com.apple.compilers.gcc; + fileType = sourcecode.cpp; + isEditable = 1; + outputFiles = ( + ); + script = ""; + }; + F921D317070376A6000D1056 /* PBXBuildRule */ = { + isa = PBXBuildRule; + compilerSpec = com.apple.compilers.gcc; + fileType = sourcecode.c; + isEditable = 1; + outputFiles = ( + ); + script = ""; + }; + F921D318070376B0000D1056 /* PBXBuildRule */ = { + isa = PBXBuildRule; + compilerSpec = com.apple.compilers.gcc; + fileType = sourcecode.asm; + isEditable = 1; + outputFiles = ( + ); + script = ""; + }; + F921D31E070376F1000D1056 /* PBXBuildRule */ = { + isa = PBXBuildRule; + compilerSpec = com.apple.compilers.gcc; + fileType = sourcecode.cpp; + isEditable = 1; + outputFiles = ( + ); + }; + F9574C4906C94DA700142BFA /* PBXBuildRule */ = { + isa = PBXBuildRule; + compilerSpec = com.apple.compilers.gcc; + fileType = sourcecode.c; + isEditable = 1; + outputFiles = ( + ); + }; +/* End PBXBuildRule section */ + +/* Begin PBXContainerItemProxy section */ + 37A0AD0E1C16000F00731E50 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 37A0AD0A1C15FFF500731E50; + remoteInfo = update_dyld_shared_cache; + }; + 37A0AD101C16003600731E50 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */; + proxyType = 1; + remoteGlobalIDString = F93937310A94FAF700070A07; + remoteInfo = update_dyld_shared_cache_tool; + }; + 37A0AD121C16003600731E50 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */; + proxyType = 1; + remoteGlobalIDString = F9F2A5580F7AEE9800B7C9EB; + remoteInfo = libdsc; + }; + 37A0AD141C16003600731E50 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */; + proxyType = 1; + remoteGlobalIDString = F9D1001114D8D0BA00099D91; + remoteInfo = dsc_extractor; + }; + 37A0AD161C16003600731E50 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 377685F21AC4B27D00026E6C; + remoteInfo = multi_dyld_shared_cache_builder; + }; + 37A0AD181C16003600731E50 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 3703A1111B38C1B300ADBA7F; + remoteInfo = dyld_shared_cache_builder; + }; + 37A0AD1A1C16004600731E50 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */; + proxyType = 1; + remoteGlobalIDString = F99B8E550FEC10F600701838; + remoteInfo = dyld_shared_cache_util; + }; + F908134711D3ED1A00626CC1 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */; + proxyType = 1; + remoteGlobalIDString = F9ED4C9E0630A76B00DF4E74; + remoteInfo = libdyld.dylib; + }; + F97FF3651C237F97000ACDD2 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */; + proxyType = 1; + remoteGlobalIDString = F97FF3551C23638F000ACDD2; + remoteInfo = nocr; + }; + F99B8EB10FEC220C00701838 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */; + proxyType = 1; + remoteGlobalIDString = F99B8E550FEC10F600701838; + remoteInfo = dyld_shared_cache_util; + }; + F9B4D77F12AD9736000605A6 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */; + proxyType = 1; + remoteGlobalIDString = F9F2A5580F7AEE9800B7C9EB; + remoteInfo = libdsc; + }; + F9ED4CA60630A78A00DF4E74 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */; + proxyType = 1; + remoteGlobalIDString = F9ED4C970630A76000DF4E74; + remoteInfo = dyld; + }; + F9ED4CA80630A78A00DF4E74 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */; + proxyType = 1; + remoteGlobalIDString = F9ED4C9E0630A76B00DF4E74; + remoteInfo = libdyld; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 3703A1201B38C1B300ADBA7F /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + ); + runOnlyForDeploymentPostprocessing = 1; + }; + 377685FE1AC4B27D00026E6C /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + ); + runOnlyForDeploymentPostprocessing = 1; + }; + F908135111D3ED9000626CC1 /* usr|include|mach-o */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 8; + dstPath = "$(INSTALL_PATH_PREFIX)/usr/include/mach-o"; + dstSubfolderSpec = 0; + files = ( + F908134C11D3ED6200626CC1 /* dyld.h in usr|include|mach-o */, + F908134D11D3ED6200626CC1 /* dyld_images.h in usr|include|mach-o */, + ); + name = "usr|include|mach-o"; + runOnlyForDeploymentPostprocessing = 1; + }; + F908137011D3FB5000626CC1 /* usr|include */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 8; + dstPath = "$(INSTALL_PATH_PREFIX)/usr/include"; + dstSubfolderSpec = 0; + files = ( + F908135911D3FA8700626CC1 /* dlfcn.h in usr|include */, + ); + name = "usr|include"; + runOnlyForDeploymentPostprocessing = 1; + }; + F908137111D3FB5000626CC1 /* usr|local|include|mach-o */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 8; + dstPath = "$(INSTALL_PATH_PREFIX)/usr/local/include/mach-o"; + dstSubfolderSpec = 0; + files = ( + F908135D11D3FACD00626CC1 /* dyld-interposing.h in usr|local|include|mach-o */, + F908135E11D3FACD00626CC1 /* dyld_cache_format.h in usr|local|include|mach-o */, + F9FF8C161C69B080009F8A53 /* dyld_process_info.h in usr|local|include|mach-o */, + F908135F11D3FACD00626CC1 /* dyld_gdb.h in usr|local|include|mach-o */, + F908136011D3FACD00626CC1 /* dyld_priv.h in usr|local|include|mach-o */, + ); + name = "usr|local|include|mach-o"; + runOnlyForDeploymentPostprocessing = 1; + }; + F908137211D3FB5000626CC1 /* usr|share|man|man1 */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 8; + dstPath = "$(INSTALL_PATH_PREFIX)/usr/share/man/man1"; + dstSubfolderSpec = 0; + files = ( + F908136411D3FB0300626CC1 /* dyld.1 in usr|share|man|man1 */, + ); + name = "usr|share|man|man1"; + runOnlyForDeploymentPostprocessing = 1; + }; + F908137311D3FB5000626CC1 /* usr|share|man|man3 */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 8; + dstPath = "$(INSTALL_PATH_PREFIX)/usr/share/man/man3"; + dstSubfolderSpec = 0; + files = ( + F908136811D3FB3A00626CC1 /* dladdr.3 in usr|share|man|man3 */, + F908136911D3FB3A00626CC1 /* dlclose.3 in usr|share|man|man3 */, + F908136A11D3FB3A00626CC1 /* dlerror.3 in usr|share|man|man3 */, + F908136B11D3FB3A00626CC1 /* dlopen.3 in usr|share|man|man3 */, + F908136C11D3FB3A00626CC1 /* dlsym.3 in usr|share|man|man3 */, + F908136D11D3FB3A00626CC1 /* dyld.3 in usr|share|man|man3 */, + F908136E11D3FB3A00626CC1 /* dlopen_preflight.3 in usr|share|man|man3 */, + ); + name = "usr|share|man|man3"; + runOnlyForDeploymentPostprocessing = 1; + }; + F97FF3541C23638F000ACDD2 /* install man page */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = "$(INSTALL_PATH_PREFIX)/usr/share/man/man1"; + dstSubfolderSpec = 0; + files = ( + F97FF3641C237F68000ACDD2 /* nocr.1 in install man page */, + ); + name = "install man page"; + runOnlyForDeploymentPostprocessing = 1; + }; + F98C78D10F7C00EA006257D2 /* usr|local|include|mach-o */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 8; + dstPath = "$(INSTALL_PATH_PREFIX)$(INSTALL_LOCATION)/usr/local/include/mach-o"; + dstSubfolderSpec = 0; + files = ( + F98C78F00F7C02E8006257D2 /* dsc_iterator.h in usr|local|include|mach-o */, + F9CE307B1208F1C60098B590 /* dsc_extractor.h in usr|local|include|mach-o */, + ); + name = "usr|local|include|mach-o"; + runOnlyForDeploymentPostprocessing = 1; + }; + F9C69EFC14EC8AB8009CAE2E /* usr|local|include */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 8; + dstPath = "$(INSTALL_PATH_PREFIX)/usr/local/include"; + dstSubfolderSpec = 0; + files = ( + F9C69EFE14EC8AD2009CAE2E /* objc-shared-cache.h in usr|local|include */, + ); + name = "usr|local|include"; + runOnlyForDeploymentPostprocessing = 1; + }; + F9D238DD0A9E2FEE002B55C7 /* usr|share|man|man1 */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 8; + dstPath = "$(INSTALL_PATH_PREFIX)$(INSTALL_LOCATION)/usr/$(LOCAL)/share/man/man1"; + dstSubfolderSpec = 0; + files = ( + F9D238DB0A9E2FD0002B55C7 /* update_dyld_shared_cache.1 in usr|share|man|man1 */, + ); + name = "usr|share|man|man1"; + runOnlyForDeploymentPostprocessing = 1; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 3703A1241B38C1B300ADBA7F /* dyld_shared_cache_builder */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = dyld_shared_cache_builder; sourceTree = BUILT_PRODUCTS_DIR; }; + 3703A1251B38C22900ADBA7F /* dyld_shared_cache_builder.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = dyld_shared_cache_builder.mm; sourceTree = ""; usesTabs = 0; }; + 370C6E531BDEF08000387223 /* libspindump.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libspindump.dylib; path = usr/lib/libspindump.dylib; sourceTree = SDKROOT; }; + 370E5F401CC06CF8000158F2 /* Logging.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Logging.cpp; sourceTree = ""; usesTabs = 0; }; + 370E5F411CC06CF8000158F2 /* Logging.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Logging.h; sourceTree = ""; usesTabs = 0; }; + 371D29811B2F53C8000BBE48 /* MultiCacheBuilder.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MultiCacheBuilder.mm; sourceTree = ""; usesTabs = 0; }; + 371D29831B30E587000BBE48 /* MultiCacheBuilder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MultiCacheBuilder.h; sourceTree = ""; usesTabs = 0; }; + 374DDAE11AC0A0F70097CFF0 /* Trie.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Trie.hpp; sourceTree = ""; }; + 376ABDB71C5930E7009F0011 /* MachOProxy.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MachOProxy.cpp; sourceTree = ""; usesTabs = 0; }; + 376ABDB81C5930E7009F0011 /* MachOProxy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MachOProxy.h; sourceTree = ""; usesTabs = 0; }; + 376ED1D71C46F2710051DD54 /* Metabom.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Metabom.framework; path = AppleInternal/Library/Frameworks/Metabom.framework; sourceTree = SDKROOT; }; + 377686021AC4B27D00026E6C /* multi_dyld_shared_cache_builder */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = multi_dyld_shared_cache_builder; sourceTree = BUILT_PRODUCTS_DIR; }; + 377686031AC4B2ED00026E6C /* multi_dyld_shared_cache_builder.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = multi_dyld_shared_cache_builder.mm; sourceTree = ""; usesTabs = 0; }; + 37BF1D731B6168150048BC27 /* Manifest.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = Manifest.mm; sourceTree = ""; usesTabs = 0; }; + 37BF1D741B6168150048BC27 /* Manifest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Manifest.h; sourceTree = ""; usesTabs = 0; }; + 37E93E291AAFB0B10080E640 /* update_dyld_shared_cache.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = update_dyld_shared_cache.mm; sourceTree = ""; usesTabs = 0; }; + 37F7A5961BB363820039043A /* Bom.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Bom.framework; path = System/Library/PrivateFrameworks/Bom.framework; sourceTree = SDKROOT; }; + EF799FE9070D27BB00F78484 /* dyld.1 */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text.man; name = dyld.1; path = doc/man/man1/dyld.1; sourceTree = SOURCE_ROOT; }; + EF799FEB070D27BB00F78484 /* dladdr.3 */ = {isa = PBXFileReference; explicitFileType = text.man; fileEncoding = 30; name = dladdr.3; path = doc/man/man3/dladdr.3; sourceTree = SOURCE_ROOT; }; + EF799FEC070D27BB00F78484 /* dlclose.3 */ = {isa = PBXFileReference; explicitFileType = text.man; fileEncoding = 30; name = dlclose.3; path = doc/man/man3/dlclose.3; sourceTree = SOURCE_ROOT; }; + EF799FED070D27BB00F78484 /* dlerror.3 */ = {isa = PBXFileReference; explicitFileType = text.man; fileEncoding = 30; name = dlerror.3; path = doc/man/man3/dlerror.3; sourceTree = SOURCE_ROOT; }; + EF799FEE070D27BB00F78484 /* dlopen.3 */ = {isa = PBXFileReference; explicitFileType = text.man; fileEncoding = 30; name = dlopen.3; path = doc/man/man3/dlopen.3; sourceTree = SOURCE_ROOT; }; + EF799FEF070D27BB00F78484 /* dlsym.3 */ = {isa = PBXFileReference; explicitFileType = text.man; fileEncoding = 30; name = dlsym.3; path = doc/man/man3/dlsym.3; sourceTree = SOURCE_ROOT; }; + EF799FF0070D27BB00F78484 /* dyld.3 */ = {isa = PBXFileReference; explicitFileType = text.man; fileEncoding = 30; name = dyld.3; path = doc/man/man3/dyld.3; sourceTree = SOURCE_ROOT; }; + F90C540A1A82D78000558E8C /* update_dyld_shared_cache_entitlements.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = update_dyld_shared_cache_entitlements.plist; sourceTree = ""; }; + F913FAD90630A8AE00B7AE9D /* dyldAPIsInLibSystem.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = dyldAPIsInLibSystem.cpp; path = src/dyldAPIsInLibSystem.cpp; sourceTree = ""; }; + F918691408B16D2500E0F9DB /* dyld-interposing.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = "dyld-interposing.h"; path = "include/mach-o/dyld-interposing.h"; sourceTree = ""; }; + F9280B791AB9DCA000B18AEC /* ImageLoaderMegaDylib.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ImageLoaderMegaDylib.cpp; path = src/ImageLoaderMegaDylib.cpp; sourceTree = ""; }; + F9280B7A1AB9DCA000B18AEC /* ImageLoaderMegaDylib.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ImageLoaderMegaDylib.h; path = src/ImageLoaderMegaDylib.h; sourceTree = ""; }; + F93937320A94FAF700070A07 /* update_dyld_shared_cache */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = update_dyld_shared_cache; sourceTree = BUILT_PRODUCTS_DIR; }; + F939373E0A94FC4700070A07 /* Architectures.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; path = Architectures.hpp; sourceTree = ""; }; + F939373F0A94FC4700070A07 /* CacheFileAbstraction.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; path = CacheFileAbstraction.hpp; sourceTree = ""; }; + F93937400A94FC4700070A07 /* dyld_cache_format.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = dyld_cache_format.h; sourceTree = ""; }; + F93937410A94FC4700070A07 /* FileAbstraction.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; path = FileAbstraction.hpp; sourceTree = ""; }; + F93937430A94FC4700070A07 /* MachOFileAbstraction.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; path = MachOFileAbstraction.hpp; sourceTree = ""; }; + F93937440A94FC4700070A07 /* MachOLayout.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; path = MachOLayout.hpp; sourceTree = ""; }; + F94DB9000F0A9B1700323715 /* ImageLoaderMachOClassic.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ImageLoaderMachOClassic.cpp; path = src/ImageLoaderMachOClassic.cpp; sourceTree = ""; }; + F94DB9010F0A9B1700323715 /* ImageLoaderMachOClassic.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ImageLoaderMachOClassic.h; path = src/ImageLoaderMachOClassic.h; sourceTree = ""; }; + F94DB9020F0A9B1700323715 /* ImageLoaderMachOCompressed.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ImageLoaderMachOCompressed.cpp; path = src/ImageLoaderMachOCompressed.cpp; sourceTree = ""; }; + F94DB9030F0A9B1700323715 /* ImageLoaderMachOCompressed.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ImageLoaderMachOCompressed.h; path = src/ImageLoaderMachOCompressed.h; sourceTree = ""; }; + F95090D01C5AB89A0031F81D /* dyld_process_info.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = dyld_process_info.h; path = "include/mach-o/dyld_process_info.h"; sourceTree = ""; usesTabs = 0; }; + F95090E41C5AD1B30031F81D /* dyld_process_info.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = dyld_process_info.cpp; path = src/dyld_process_info.cpp; sourceTree = ""; usesTabs = 0; }; + F958D4751C7FCD4A00A0B199 /* dyld_process_info_internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = dyld_process_info_internal.h; path = src/dyld_process_info_internal.h; sourceTree = ""; }; + F958D4761C7FCD4A00A0B199 /* dyld_process_info_notify.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = dyld_process_info_notify.cpp; path = src/dyld_process_info_notify.cpp; sourceTree = ""; }; + F95C95160E994796007B7CB8 /* MachOTrie.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = MachOTrie.hpp; sourceTree = ""; }; + F971DD131A4A0E0700BBDD52 /* base.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = base.xcconfig; sourceTree = ""; }; + F971DD141A4A0E0700BBDD52 /* dyld.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = dyld.xcconfig; sourceTree = ""; }; + F971DD151A4A0E0700BBDD52 /* libdyld.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = libdyld.xcconfig; sourceTree = ""; }; + F971DD161A4A0E0700BBDD52 /* update_dyld_shared_cache.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = update_dyld_shared_cache.xcconfig; sourceTree = ""; }; + F976F548127B90F8004BA2A5 /* dyld.order */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = dyld.order; path = src/dyld.order; sourceTree = ""; }; + F97FF3561C23638F000ACDD2 /* nocr */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = nocr; sourceTree = BUILT_PRODUCTS_DIR; }; + F97FF3581C23638F000ACDD2 /* main.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = main.c; sourceTree = ""; }; + F97FF35E1C236402000ACDD2 /* execserver.defs */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.mig; name = execserver.defs; path = testing/nocr/execserver.defs; sourceTree = ""; }; + F97FF35F1C236402000ACDD2 /* nocr.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = nocr.c; path = testing/nocr/nocr.c; sourceTree = ""; }; + F97FF3631C237F5C000ACDD2 /* nocr.1 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.man; name = nocr.1; path = ../../../testing/nocr/nocr.1; sourceTree = ""; }; + F981BB8B170FC24400A686D6 /* dyldSyscallInterface.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = dyldSyscallInterface.h; path = src/dyldSyscallInterface.h; sourceTree = ""; }; + F98935B90A9A412B00FB6228 /* MachOBinder.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; path = MachOBinder.hpp; sourceTree = ""; }; + F98935BA0A9A412B00FB6228 /* MachORebaser.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; path = MachORebaser.hpp; sourceTree = ""; }; + F98D274C0AA79D7400416316 /* dyld_images.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = dyld_images.h; path = "include/mach-o/dyld_images.h"; sourceTree = ""; }; + F98E18FF1A3FC1A9008DB73F /* OptimizerBranches.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OptimizerBranches.cpp; sourceTree = ""; usesTabs = 0; }; + F98E19001A3FC1A9008DB73F /* OptimizerBranches.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OptimizerBranches.h; sourceTree = ""; usesTabs = 0; }; + F98E19011A3FC1A9008DB73F /* OptimizerLinkedit.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OptimizerLinkedit.cpp; sourceTree = ""; usesTabs = 0; }; + F98E19031A3FC1A9008DB73F /* OptimizerObjC.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OptimizerObjC.cpp; sourceTree = ""; usesTabs = 0; }; + F98E190E1A40D167008DB73F /* ObjC1Abstraction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ObjC1Abstraction.hpp; sourceTree = ""; usesTabs = 0; }; + F98E190F1A40D167008DB73F /* ObjC2Abstraction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ObjC2Abstraction.hpp; sourceTree = ""; usesTabs = 0; }; + F99B8E620FEC11B400701838 /* dyld_shared_cache_util.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = dyld_shared_cache_util.cpp; sourceTree = ""; }; + F99B8E670FEC121100701838 /* dyld_shared_cache_util */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = dyld_shared_cache_util; sourceTree = BUILT_PRODUCTS_DIR; }; + F99DE0361AAE4F0400669496 /* libdyld_data_symbols.dirty */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = libdyld_data_symbols.dirty; path = src/libdyld_data_symbols.dirty; sourceTree = ""; }; + F99EE6AE06B48D4200BF1992 /* dlfcn.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = dlfcn.h; path = include/dlfcn.h; sourceTree = ""; }; + F99EFC0D0EAD60E8001032B8 /* dyld_stub_binder.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; name = dyld_stub_binder.s; path = src/dyld_stub_binder.s; sourceTree = ""; }; + F9A221E60F3A6D7C00D15F73 /* dyldLibSystemGlue.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = dyldLibSystemGlue.c; path = src/dyldLibSystemGlue.c; sourceTree = ""; }; + F9A6D6E2116F9DF20051CC16 /* threadLocalVariables.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = threadLocalVariables.c; path = src/threadLocalVariables.c; sourceTree = ""; }; + F9A6D70B116FBBD10051CC16 /* threadLocalHelpers.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; name = threadLocalHelpers.s; path = src/threadLocalHelpers.s; sourceTree = ""; }; + F9AB709D0BA75730002F6068 /* dyldLibSystemInterface.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = dyldLibSystemInterface.h; path = src/dyldLibSystemInterface.h; sourceTree = ""; }; + F9AC7E930B7BB67700FEB38B /* version.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; path = version.c; sourceTree = BUILT_PRODUCTS_DIR; }; + F9AFEA3216F15CE300CB5161 /* start_glue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = start_glue.h; path = src/start_glue.h; sourceTree = ""; }; + F9B01E3D0739ABDE00CF981B /* dyld.exp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.exports; name = dyld.exp; path = src/dyld.exp; sourceTree = SOURCE_ROOT; }; + F9C69EFD14EC8ABF009CAE2E /* objc-shared-cache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "objc-shared-cache.h"; path = "include/objc-shared-cache.h"; sourceTree = ""; usesTabs = 0; }; + F9CE30781208F1B50098B590 /* dsc_extractor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = dsc_extractor.cpp; sourceTree = ""; }; + F9CE30791208F1B50098B590 /* dsc_extractor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = dsc_extractor.h; sourceTree = ""; }; + F9D0FE6D1A367093001E839B /* AdjustForNewSegmentLocation.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = AdjustForNewSegmentLocation.cpp; sourceTree = ""; usesTabs = 0; }; + F9D0FE6E1A367093001E839B /* BindAllImages.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = BindAllImages.cpp; sourceTree = ""; usesTabs = 0; }; + F9D0FE6F1A367093001E839B /* FileCache.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FileCache.cpp; sourceTree = ""; usesTabs = 0; }; + F9D0FE701A367093001E839B /* SharedCache.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = SharedCache.cpp; sourceTree = ""; usesTabs = 0; }; + F9D0FE711A367093001E839B /* mega-dylib-utils.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "mega-dylib-utils.h"; sourceTree = ""; usesTabs = 0; }; + F9D1001214D8D0BA00099D91 /* dsc_extractor.bundle */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = dsc_extractor.bundle; sourceTree = BUILT_PRODUCTS_DIR; }; + F9D238D90A9E19A0002B55C7 /* update_dyld_shared_cache.1 */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text.man; path = update_dyld_shared_cache.1; sourceTree = ""; }; + F9D49CCB1458B95200F86ADD /* start_glue.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; name = start_glue.s; path = src/start_glue.s; sourceTree = ""; }; + F9E572000A66EF41007D9BE9 /* dlopen_preflight.3 */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text; path = dlopen_preflight.3; sourceTree = ""; }; + F9E7AD981BE47CE30025D311 /* CodeSigningTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CodeSigningTypes.h; sourceTree = ""; }; + F9ED4C980630A76000DF4E74 /* dyld */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = dyld; sourceTree = BUILT_PRODUCTS_DIR; }; + F9ED4C9F0630A76B00DF4E74 /* libdyld.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libdyld.dylib; sourceTree = BUILT_PRODUCTS_DIR; }; + F9ED4CC60630A7F100DF4E74 /* dyld_gdb.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = dyld_gdb.cpp; path = src/dyld_gdb.cpp; sourceTree = SOURCE_ROOT; }; + F9ED4CC70630A7F100DF4E74 /* dyld.cpp */ = {isa = PBXFileReference; fileEncoding = 30; indentWidth = 4; lastKnownFileType = sourcecode.cpp.cpp; name = dyld.cpp; path = src/dyld.cpp; sourceTree = SOURCE_ROOT; usesTabs = 1; }; + F9ED4CC80630A7F100DF4E74 /* dyld.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = dyld.h; path = src/dyld.h; sourceTree = SOURCE_ROOT; }; + F9ED4CC90630A7F100DF4E74 /* dyldAPIs.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = dyldAPIs.cpp; path = src/dyldAPIs.cpp; sourceTree = SOURCE_ROOT; }; + F9ED4CCA0630A7F100DF4E74 /* dyldExceptions.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = dyldExceptions.c; path = src/dyldExceptions.c; sourceTree = SOURCE_ROOT; }; + F9ED4CCB0630A7F100DF4E74 /* dyldInitialization.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = dyldInitialization.cpp; path = src/dyldInitialization.cpp; sourceTree = SOURCE_ROOT; }; + F9ED4CCC0630A7F100DF4E74 /* dyldLock.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = dyldLock.cpp; path = src/dyldLock.cpp; sourceTree = SOURCE_ROOT; }; + F9ED4CCD0630A7F100DF4E74 /* dyldLock.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = dyldLock.h; path = src/dyldLock.h; sourceTree = SOURCE_ROOT; }; + F9ED4CCE0630A7F100DF4E74 /* dyldNew.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = dyldNew.cpp; path = src/dyldNew.cpp; sourceTree = SOURCE_ROOT; }; + F9ED4CCF0630A7F100DF4E74 /* dyldStartup.s */ = {isa = PBXFileReference; fileEncoding = 30; indentWidth = 4; lastKnownFileType = sourcecode.asm; name = dyldStartup.s; path = src/dyldStartup.s; sourceTree = SOURCE_ROOT; tabWidth = 8; usesTabs = 1; }; + F9ED4CD00630A7F100DF4E74 /* glue.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = glue.c; path = src/glue.c; sourceTree = SOURCE_ROOT; }; + F9ED4CD10630A7F100DF4E74 /* ImageLoader.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = ImageLoader.cpp; path = src/ImageLoader.cpp; sourceTree = SOURCE_ROOT; }; + F9ED4CD20630A7F100DF4E74 /* ImageLoader.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = ImageLoader.h; path = src/ImageLoader.h; sourceTree = SOURCE_ROOT; }; + F9ED4CD30630A7F100DF4E74 /* ImageLoaderMachO.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = ImageLoaderMachO.cpp; path = src/ImageLoaderMachO.cpp; sourceTree = SOURCE_ROOT; }; + F9ED4CD40630A7F100DF4E74 /* ImageLoaderMachO.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = ImageLoaderMachO.h; path = src/ImageLoaderMachO.h; sourceTree = SOURCE_ROOT; }; + F9ED4CD50630A7F100DF4E74 /* stub_binding_helper.s */ = {isa = PBXFileReference; fileEncoding = 30; indentWidth = 4; lastKnownFileType = sourcecode.asm; name = stub_binding_helper.s; path = src/stub_binding_helper.s; sourceTree = SOURCE_ROOT; tabWidth = 8; usesTabs = 1; }; + F9ED4CE80630A80600DF4E74 /* dyld_gdb.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = dyld_gdb.h; path = "include/mach-o/dyld_gdb.h"; sourceTree = SOURCE_ROOT; }; + F9ED4CE90630A80600DF4E74 /* dyld_priv.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = dyld_priv.h; path = "include/mach-o/dyld_priv.h"; sourceTree = SOURCE_ROOT; }; + F9ED4CEA0630A80600DF4E74 /* dyld.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = dyld.h; path = "include/mach-o/dyld.h"; sourceTree = SOURCE_ROOT; }; + F9F2A5590F7AEE9800B7C9EB /* libdsc.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libdsc.a; sourceTree = BUILT_PRODUCTS_DIR; }; + F9F2A56E0F7AEEE300B7C9EB /* dsc_iterator.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = dsc_iterator.cpp; sourceTree = ""; }; + F9F2A56F0F7AEEE300B7C9EB /* dsc_iterator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = dsc_iterator.h; sourceTree = ""; }; + F9F6F4261C1FAF8000BD8FED /* testing */ = {isa = PBXFileReference; lastKnownFileType = folder; path = testing; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 3703A11D1B38C1B300ADBA7F /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 376ABDB61C592CC0009F0011 /* Metabom.framework in Frameworks */, + 378EE3B21BE88C4A001C99FB /* Bom.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 377685FD1AC4B27D00026E6C /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 376ED1D81C46F2710051DD54 /* Metabom.framework in Frameworks */, + 378EE3B11BE88C47001C99FB /* Bom.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F93937300A94FAF700070A07 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 370C6E541BDEF08000387223 /* libspindump.dylib in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F97FF3531C23638F000ACDD2 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F99B8E540FEC10F600701838 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F9D1001014D8D0BA00099D91 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F9F2A5570F7AEE9800B7C9EB /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + EF799FE7070D27BB00F78484 /* man */ = { + isa = PBXGroup; + children = ( + EF799FE8070D27BB00F78484 /* man1 */, + EF799FEA070D27BB00F78484 /* man3 */, + ); + name = man; + path = doc/man; + sourceTree = SOURCE_ROOT; + }; + EF799FE8070D27BB00F78484 /* man1 */ = { + isa = PBXGroup; + children = ( + EF799FE9070D27BB00F78484 /* dyld.1 */, + F97FF3631C237F5C000ACDD2 /* nocr.1 */, + F9D238D90A9E19A0002B55C7 /* update_dyld_shared_cache.1 */, + ); + name = man1; + path = doc/man/man1; + sourceTree = SOURCE_ROOT; + }; + EF799FEA070D27BB00F78484 /* man3 */ = { + isa = PBXGroup; + children = ( + EF799FEB070D27BB00F78484 /* dladdr.3 */, + EF799FEC070D27BB00F78484 /* dlclose.3 */, + EF799FED070D27BB00F78484 /* dlerror.3 */, + EF799FEE070D27BB00F78484 /* dlopen.3 */, + EF799FEF070D27BB00F78484 /* dlsym.3 */, + F9E572000A66EF41007D9BE9 /* dlopen_preflight.3 */, + EF799FF0070D27BB00F78484 /* dyld.3 */, + ); + name = man3; + path = doc/man/man3; + sourceTree = SOURCE_ROOT; + }; + F939373D0A94FC4700070A07 /* launch-cache */ = { + isa = PBXGroup; + children = ( + F90C540A1A82D78000558E8C /* update_dyld_shared_cache_entitlements.plist */, + F939373E0A94FC4700070A07 /* Architectures.hpp */, + F939373F0A94FC4700070A07 /* CacheFileAbstraction.hpp */, + F93937400A94FC4700070A07 /* dyld_cache_format.h */, + F93937410A94FC4700070A07 /* FileAbstraction.hpp */, + F93937430A94FC4700070A07 /* MachOFileAbstraction.hpp */, + F93937440A94FC4700070A07 /* MachOLayout.hpp */, + F98935BA0A9A412B00FB6228 /* MachORebaser.hpp */, + F98935B90A9A412B00FB6228 /* MachOBinder.hpp */, + F95C95160E994796007B7CB8 /* MachOTrie.hpp */, + F9F2A56E0F7AEEE300B7C9EB /* dsc_iterator.cpp */, + F9F2A56F0F7AEEE300B7C9EB /* dsc_iterator.h */, + F9CE30781208F1B50098B590 /* dsc_extractor.cpp */, + F9CE30791208F1B50098B590 /* dsc_extractor.h */, + F99B8E620FEC11B400701838 /* dyld_shared_cache_util.cpp */, + ); + path = "launch-cache"; + sourceTree = ""; + }; + F971DD121A4A0E0700BBDD52 /* configs */ = { + isa = PBXGroup; + children = ( + F971DD131A4A0E0700BBDD52 /* base.xcconfig */, + F971DD141A4A0E0700BBDD52 /* dyld.xcconfig */, + F971DD151A4A0E0700BBDD52 /* libdyld.xcconfig */, + F971DD161A4A0E0700BBDD52 /* update_dyld_shared_cache.xcconfig */, + ); + path = configs; + sourceTree = SOURCE_ROOT; + }; + F97FF3571C23638F000ACDD2 /* nocr */ = { + isa = PBXGroup; + children = ( + F97FF3581C23638F000ACDD2 /* main.c */, + ); + path = nocr; + sourceTree = ""; + }; + F9D0FE6C1A367093001E839B /* interlinked-dylibs */ = { + isa = PBXGroup; + children = ( + 370C6E531BDEF08000387223 /* libspindump.dylib */, + 376ED1D71C46F2710051DD54 /* Metabom.framework */, + 37F7A5961BB363820039043A /* Bom.framework */, + 374DDAE11AC0A0F70097CFF0 /* Trie.hpp */, + F9E7AD981BE47CE30025D311 /* CodeSigningTypes.h */, + F98E18FF1A3FC1A9008DB73F /* OptimizerBranches.cpp */, + F98E19001A3FC1A9008DB73F /* OptimizerBranches.h */, + F98E19011A3FC1A9008DB73F /* OptimizerLinkedit.cpp */, + F98E19031A3FC1A9008DB73F /* OptimizerObjC.cpp */, + F98E190F1A40D167008DB73F /* ObjC2Abstraction.hpp */, + F9D0FE6D1A367093001E839B /* AdjustForNewSegmentLocation.cpp */, + F98E190E1A40D167008DB73F /* ObjC1Abstraction.hpp */, + F9D0FE6E1A367093001E839B /* BindAllImages.cpp */, + F9D0FE6F1A367093001E839B /* FileCache.cpp */, + F9D0FE701A367093001E839B /* SharedCache.cpp */, + 376ABDB81C5930E7009F0011 /* MachOProxy.h */, + 376ABDB71C5930E7009F0011 /* MachOProxy.cpp */, + 37BF1D741B6168150048BC27 /* Manifest.h */, + 37BF1D731B6168150048BC27 /* Manifest.mm */, + 371D29831B30E587000BBE48 /* MultiCacheBuilder.h */, + 371D29811B2F53C8000BBE48 /* MultiCacheBuilder.mm */, + 37E93E291AAFB0B10080E640 /* update_dyld_shared_cache.mm */, + 377686031AC4B2ED00026E6C /* multi_dyld_shared_cache_builder.mm */, + 3703A1251B38C22900ADBA7F /* dyld_shared_cache_builder.mm */, + F9D0FE711A367093001E839B /* mega-dylib-utils.h */, + 370E5F411CC06CF8000158F2 /* Logging.h */, + 370E5F401CC06CF8000158F2 /* Logging.cpp */, + ); + path = "interlinked-dylibs"; + sourceTree = SOURCE_ROOT; + }; + F9ED4C870630A72200DF4E74 = { + isa = PBXGroup; + children = ( + F9F6F4261C1FAF8000BD8FED /* testing */, + F971DD121A4A0E0700BBDD52 /* configs */, + F9ED4CBB0630A7AA00DF4E74 /* src */, + F9ED4CC30630A7BE00DF4E74 /* doc */, + F9ED4CBE0630A7B100DF4E74 /* include */, + F97FF3571C23638F000ACDD2 /* nocr */, + F9ED4C990630A76000DF4E74 /* Products */, + F9D0FE6C1A367093001E839B /* interlinked-dylibs */, + F939373D0A94FC4700070A07 /* launch-cache */, + ); + indentWidth = 4; + sourceTree = ""; + tabWidth = 4; + usesTabs = 1; + }; + F9ED4C990630A76000DF4E74 /* Products */ = { + isa = PBXGroup; + children = ( + F9ED4C980630A76000DF4E74 /* dyld */, + F9ED4C9F0630A76B00DF4E74 /* libdyld.dylib */, + F93937320A94FAF700070A07 /* update_dyld_shared_cache */, + F9F2A5590F7AEE9800B7C9EB /* libdsc.a */, + F99B8E670FEC121100701838 /* dyld_shared_cache_util */, + F9D1001214D8D0BA00099D91 /* dsc_extractor.bundle */, + 377686021AC4B27D00026E6C /* multi_dyld_shared_cache_builder */, + 3703A1241B38C1B300ADBA7F /* dyld_shared_cache_builder */, + F97FF3561C23638F000ACDD2 /* nocr */, + ); + name = Products; + sourceTree = ""; + }; + F9ED4CBB0630A7AA00DF4E74 /* src */ = { + isa = PBXGroup; + children = ( + F97FF35E1C236402000ACDD2 /* execserver.defs */, + F97FF35F1C236402000ACDD2 /* nocr.c */, + F9ED4CC60630A7F100DF4E74 /* dyld_gdb.cpp */, + F9ED4CC70630A7F100DF4E74 /* dyld.cpp */, + F9ED4CC80630A7F100DF4E74 /* dyld.h */, + F9ED4CC90630A7F100DF4E74 /* dyldAPIs.cpp */, + F9ED4CCA0630A7F100DF4E74 /* dyldExceptions.c */, + F9AB709D0BA75730002F6068 /* dyldLibSystemInterface.h */, + F913FAD90630A8AE00B7AE9D /* dyldAPIsInLibSystem.cpp */, + F9ED4CCB0630A7F100DF4E74 /* dyldInitialization.cpp */, + F9ED4CCC0630A7F100DF4E74 /* dyldLock.cpp */, + F9ED4CCD0630A7F100DF4E74 /* dyldLock.h */, + F9ED4CCE0630A7F100DF4E74 /* dyldNew.cpp */, + F9ED4CCF0630A7F100DF4E74 /* dyldStartup.s */, + F9D49CCB1458B95200F86ADD /* start_glue.s */, + F9AFEA3216F15CE300CB5161 /* start_glue.h */, + F99EFC0D0EAD60E8001032B8 /* dyld_stub_binder.s */, + F9ED4CD00630A7F100DF4E74 /* glue.c */, + F981BB8B170FC24400A686D6 /* dyldSyscallInterface.h */, + F9ED4CD10630A7F100DF4E74 /* ImageLoader.cpp */, + F9ED4CD20630A7F100DF4E74 /* ImageLoader.h */, + F9ED4CD30630A7F100DF4E74 /* ImageLoaderMachO.cpp */, + F9ED4CD40630A7F100DF4E74 /* ImageLoaderMachO.h */, + F94DB9000F0A9B1700323715 /* ImageLoaderMachOClassic.cpp */, + F94DB9010F0A9B1700323715 /* ImageLoaderMachOClassic.h */, + F94DB9020F0A9B1700323715 /* ImageLoaderMachOCompressed.cpp */, + F94DB9030F0A9B1700323715 /* ImageLoaderMachOCompressed.h */, + F9280B791AB9DCA000B18AEC /* ImageLoaderMegaDylib.cpp */, + F9280B7A1AB9DCA000B18AEC /* ImageLoaderMegaDylib.h */, + F99DE0361AAE4F0400669496 /* libdyld_data_symbols.dirty */, + F9ED4CD50630A7F100DF4E74 /* stub_binding_helper.s */, + F9B01E3D0739ABDE00CF981B /* dyld.exp */, + F976F548127B90F8004BA2A5 /* dyld.order */, + F9AC7E930B7BB67700FEB38B /* version.c */, + F9A221E60F3A6D7C00D15F73 /* dyldLibSystemGlue.c */, + F9A6D6E2116F9DF20051CC16 /* threadLocalVariables.c */, + F9A6D70B116FBBD10051CC16 /* threadLocalHelpers.s */, + F958D4751C7FCD4A00A0B199 /* dyld_process_info_internal.h */, + F958D4761C7FCD4A00A0B199 /* dyld_process_info_notify.cpp */, + F95090E41C5AD1B30031F81D /* dyld_process_info.cpp */, + ); + name = src; + sourceTree = ""; + }; + F9ED4CBE0630A7B100DF4E74 /* include */ = { + isa = PBXGroup; + children = ( + F95090D01C5AB89A0031F81D /* dyld_process_info.h */, + F918691408B16D2500E0F9DB /* dyld-interposing.h */, + F98D274C0AA79D7400416316 /* dyld_images.h */, + F9ED4CE80630A80600DF4E74 /* dyld_gdb.h */, + F9ED4CE90630A80600DF4E74 /* dyld_priv.h */, + F9ED4CEA0630A80600DF4E74 /* dyld.h */, + F99EE6AE06B48D4200BF1992 /* dlfcn.h */, + F9C69EFD14EC8ABF009CAE2E /* objc-shared-cache.h */, + ); + name = include; + sourceTree = ""; + }; + F9ED4CC30630A7BE00DF4E74 /* doc */ = { + isa = PBXGroup; + children = ( + EF799FE7070D27BB00F78484 /* man */, + ); + name = doc; + sourceTree = SOURCE_ROOT; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 3703A1111B38C1B300ADBA7F /* dyld_shared_cache_builder */ = { + isa = PBXNativeTarget; + buildConfigurationList = 3703A1211B38C1B300ADBA7F /* Build configuration list for PBXNativeTarget "dyld_shared_cache_builder" */; + buildPhases = ( + 3703A1121B38C1B300ADBA7F /* make dyld_cache_config.h */, + 3703A1131B38C1B300ADBA7F /* Sources */, + 3703A11D1B38C1B300ADBA7F /* Frameworks */, + 3703A1201B38C1B300ADBA7F /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = dyld_shared_cache_builder; + productName = update_os_interlinked_dylib; + productReference = 3703A1241B38C1B300ADBA7F /* dyld_shared_cache_builder */; + productType = "com.apple.product-type.tool"; + }; + 377685F21AC4B27D00026E6C /* multi_dyld_shared_cache_builder */ = { + isa = PBXNativeTarget; + buildConfigurationList = 377685FF1AC4B27D00026E6C /* Build configuration list for PBXNativeTarget "multi_dyld_shared_cache_builder" */; + buildPhases = ( + 377685F31AC4B27D00026E6C /* make dyld_cache_config.h */, + 377685F41AC4B27D00026E6C /* Sources */, + 377685FD1AC4B27D00026E6C /* Frameworks */, + 377685FE1AC4B27D00026E6C /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = multi_dyld_shared_cache_builder; + productName = update_os_interlinked_dylib; + productReference = 377686021AC4B27D00026E6C /* multi_dyld_shared_cache_builder */; + productType = "com.apple.product-type.tool"; + }; + F93937310A94FAF700070A07 /* update_dyld_shared_cache_tool */ = { + isa = PBXNativeTarget; + buildConfigurationList = F93937340A94FB2900070A07 /* Build configuration list for PBXNativeTarget "update_dyld_shared_cache_tool" */; + buildPhases = ( + F91083C91702592700831889 /* create dyld_cache_config.h */, + F939372F0A94FAF700070A07 /* Sources */, + F93937300A94FAF700070A07 /* Frameworks */, + F9D238DD0A9E2FEE002B55C7 /* usr|share|man|man1 */, + F991E3030FF1A4EC0082CCC9 /* do not install duplicates */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = update_dyld_shared_cache_tool; + productName = update_dyld_shared_cache; + productReference = F93937320A94FAF700070A07 /* update_dyld_shared_cache */; + productType = "com.apple.product-type.tool"; + }; + F97FF3551C23638F000ACDD2 /* nocr */ = { + isa = PBXNativeTarget; + buildConfigurationList = F97FF35C1C23638F000ACDD2 /* Build configuration list for PBXNativeTarget "nocr" */; + buildPhases = ( + F97FF3521C23638F000ACDD2 /* Sources */, + F97FF3531C23638F000ACDD2 /* Frameworks */, + F97FF3541C23638F000ACDD2 /* install man page */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = nocr; + productName = nocr; + productReference = F97FF3561C23638F000ACDD2 /* nocr */; + productType = "com.apple.product-type.tool"; + }; + F99B8E550FEC10F600701838 /* dyld_shared_cache_util */ = { + isa = PBXNativeTarget; + buildConfigurationList = F99B8E5D0FEC10F800701838 /* Build configuration list for PBXNativeTarget "dyld_shared_cache_util" */; + buildPhases = ( + F99B8E530FEC10F600701838 /* Sources */, + F99B8E540FEC10F600701838 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = dyld_shared_cache_util; + productName = dscutil; + productReference = F99B8E670FEC121100701838 /* dyld_shared_cache_util */; + productType = "com.apple.product-type.tool"; + }; + F9D1001114D8D0BA00099D91 /* dsc_extractor */ = { + isa = PBXNativeTarget; + buildConfigurationList = F9D1001714D8D0F100099D91 /* Build configuration list for PBXNativeTarget "dsc_extractor" */; + buildPhases = ( + F9D1000F14D8D0BA00099D91 /* Sources */, + F9D1001014D8D0BA00099D91 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = dsc_extractor; + productName = dsc_extractor; + productReference = F9D1001214D8D0BA00099D91 /* dsc_extractor.bundle */; + productType = "com.apple.product-type.library.dynamic"; + }; + F9ED4C970630A76000DF4E74 /* dyld */ = { + isa = PBXNativeTarget; + buildConfigurationList = F9D8C7DD087B087300E93EFB /* Build configuration list for PBXNativeTarget "dyld" */; + buildPhases = ( + F9D050C811DD701A00FB0A29 /* configure archives */, + F9ED4C950630A76000DF4E74 /* Sources */, + F907E2490FA6469000BFEDBD /* install iPhone file */, + F9213B3F18BFC9CB001CB6E8 /* simulator entitlement */, + F99B8EB60FEC236500701838 /* suppress macosx dyld_shared_cache_util */, + ); + buildRules = ( + F921D318070376B0000D1056 /* PBXBuildRule */, + F921D317070376A6000D1056 /* PBXBuildRule */, + F921D3160703769A000D1056 /* PBXBuildRule */, + ); + dependencies = ( + F99B8EB20FEC220C00701838 /* PBXTargetDependency */, + ); + name = dyld; + productName = dyld; + productReference = F9ED4C980630A76000DF4E74 /* dyld */; + productType = "com.apple.product-type.tool"; + }; + F9ED4C9E0630A76B00DF4E74 /* libdyld.dylib */ = { + isa = PBXNativeTarget; + buildConfigurationList = F9D8C7E1087B087300E93EFB /* Build configuration list for PBXNativeTarget "libdyld.dylib" */; + buildPhases = ( + F9ED4C9C0630A76B00DF4E74 /* Sources */, + F959621018849DF20003E4D4 /* add dyld symlink */, + ); + buildRules = ( + F921D31E070376F1000D1056 /* PBXBuildRule */, + F9574C4906C94DA700142BFA /* PBXBuildRule */, + ); + dependencies = ( + ); + name = libdyld.dylib; + productName = libdyld; + productReference = F9ED4C9F0630A76B00DF4E74 /* libdyld.dylib */; + productType = "com.apple.product-type.library.dynamic"; + }; + F9F2A5580F7AEE9800B7C9EB /* libdsc */ = { + isa = PBXNativeTarget; + buildConfigurationList = F9F2A56B0F7AEEB100B7C9EB /* Build configuration list for PBXNativeTarget "libdsc" */; + buildPhases = ( + F9F2A5560F7AEE9800B7C9EB /* Sources */, + F9F2A5570F7AEE9800B7C9EB /* Frameworks */, + F98C78D10F7C00EA006257D2 /* usr|local|include|mach-o */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = libdsc; + productName = dsc; + productReference = F9F2A5590F7AEE9800B7C9EB /* libdsc.a */; + productType = "com.apple.product-type.library.static"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + F9ED4C8B0630A72300DF4E74 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0630; + TargetAttributes = { + 37A0AD0A1C15FFF500731E50 = { + CreatedOnToolsVersion = 7.0; + }; + F97FF3551C23638F000ACDD2 = { + CreatedOnToolsVersion = 7.1; + }; + F9F6F4271C1FB0A700BD8FED = { + CreatedOnToolsVersion = 7.1; + }; + }; + }; + buildConfigurationList = F9D8C7E9087B087300E93EFB /* Build configuration list for PBXProject "dyld" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 1; + knownRegions = ( + English, + Japanese, + French, + German, + ); + mainGroup = F9ED4C870630A72200DF4E74; + productRefGroup = F9ED4C990630A76000DF4E74 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + F9ED4C920630A73900DF4E74 /* all */, + 37A0AD0A1C15FFF500731E50 /* update_dyld_shared_cache */, + F9ED4C970630A76000DF4E74 /* dyld */, + F908134211D3ED0B00626CC1 /* libdyld */, + F9ED4C9E0630A76B00DF4E74 /* libdyld.dylib */, + F93937310A94FAF700070A07 /* update_dyld_shared_cache_tool */, + F9F2A5580F7AEE9800B7C9EB /* libdsc */, + F99B8E550FEC10F600701838 /* dyld_shared_cache_util */, + F9D1001114D8D0BA00099D91 /* dsc_extractor */, + 377685F21AC4B27D00026E6C /* multi_dyld_shared_cache_builder */, + 3703A1111B38C1B300ADBA7F /* dyld_shared_cache_builder */, + F9F6F4271C1FB0A700BD8FED /* dyld_tests */, + F97FF3551C23638F000ACDD2 /* nocr */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXShellScriptBuildPhase section */ + 3703A1121B38C1B300ADBA7F /* make dyld_cache_config.h */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "make dyld_cache_config.h"; + outputPaths = ( + "$(DERIVED_FILE_DIR)/dyld_cache_config.h", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/bash; + shellScript = "echo \"\" > ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\nif [ -z \"${ARM_SDK}\" ]; then\n # if iOS SDK not available, use MacOSX SDK\n ARM_SDK=`xcodebuild -sdk macosx.internal -version Path`\nfi\n\nSHARED_REGION_FILE=\"${ARM_SDK}/usr/include/mach/shared_region.h\"\n\n\nif [ -r \"${SHARED_REGION_FILE}\" ]; then\n echo -n \"#define ARM_SHARED_REGION_START \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n awk '/define SHARED_REGION_BASE_ARM[ \\t]/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n echo \"\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\n echo -n \"#define ARM_SHARED_REGION_SIZE \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n awk '/define SHARED_REGION_SIZE_ARM[ \\t]/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n echo \"\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\n echo -n \"#define ARM64_SHARED_REGION_START \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n awk '/define SHARED_REGION_BASE_ARM64/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n echo \"\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\n echo -n \"#define ARM64_SHARED_REGION_SIZE \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n awk '/define SHARED_REGION_SIZE_ARM64/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n echo \"\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\nelse\n echo \"ERROR: File needed to configure update_dyld_shared_cache does not exist '${SHARED_REGION_FILE}'\"\n exit 1\nfi\n\nif [ -r \"${ARM_SDK}/AppleInternal/DirtyDataFiles/dirty-data-segments-order.txt\" ]; then\n mkdir -p \"${DSTROOT}/${INSTALL_LOCATION}/usr/local/bin\"\n cp \"${ARM_SDK}/AppleInternal/DirtyDataFiles/dirty-data-segments-order.txt\" \"${DSTROOT}/${INSTALL_LOCATION}/usr/local/bin\"\nfi\nif [ -r \"${ARM_SDK}/AppleInternal/OrderFiles/dylib-order.txt\" ]; then\n mkdir -p \"${DSTROOT}/${INSTALL_LOCATION}/usr/local/bin\"\n cp \"${ARM_SDK}/AppleInternal/OrderFiles/dylib-order.txt\" \"${DSTROOT}/${INSTALL_LOCATION}/usr/local/bin\"\nfi\n\n"; + showEnvVarsInLog = 0; + }; + 377685F31AC4B27D00026E6C /* make dyld_cache_config.h */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "make dyld_cache_config.h"; + outputPaths = ( + "$(DERIVED_FILE_DIR)/dyld_cache_config.h", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/bash; + shellScript = "echo \"\" > ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\nif [ -z \"${ARM_SDK}\" ]; then\n # if iOS SDK not available, use MacOSX SDK\n ARM_SDK=`xcodebuild -sdk macosx.internal -version Path`\nfi\n\nSHARED_REGION_FILE=\"${ARM_SDK}/usr/include/mach/shared_region.h\"\n\n\nif [ -r \"${SHARED_REGION_FILE}\" ]; then\n echo -n \"#define ARM_SHARED_REGION_START \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n awk '/define SHARED_REGION_BASE_ARM[ \\t]/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n echo \"\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\n echo -n \"#define ARM_SHARED_REGION_SIZE \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n awk '/define SHARED_REGION_SIZE_ARM[ \\t]/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n echo \"\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\n echo -n \"#define ARM64_SHARED_REGION_START \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n awk '/define SHARED_REGION_BASE_ARM64/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n echo \"\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\n echo -n \"#define ARM64_SHARED_REGION_SIZE \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n awk '/define SHARED_REGION_SIZE_ARM64/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n echo \"\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\nelse\n echo \"ERROR: File needed to configure update_dyld_shared_cache does not exist '${SHARED_REGION_FILE}'\"\n exit 1\nfi\n\nif [ -r \"${ARM_SDK}/AppleInternal/DirtyDataFiles/dirty-data-segments-order.txt\" ]; then\n mkdir -p \"${DSTROOT}/${INSTALL_LOCATION}/usr/local/bin\"\n cp \"${ARM_SDK}/AppleInternal/DirtyDataFiles/dirty-data-segments-order.txt\" \"${DSTROOT}/${INSTALL_LOCATION}/usr/local/bin\"\nfi\nif [ -r \"${ARM_SDK}/AppleInternal/OrderFiles/dylib-order.txt\" ]; then\n mkdir -p \"${DSTROOT}/${INSTALL_LOCATION}/usr/local/bin\"\n cp \"${ARM_SDK}/AppleInternal/OrderFiles/dylib-order.txt\" \"${DSTROOT}/${INSTALL_LOCATION}/usr/local/bin\"\nfi\n\n"; + showEnvVarsInLog = 0; + }; + F907E2490FA6469000BFEDBD /* install iPhone file */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 8; + files = ( + ); + inputPaths = ( + ); + name = "install iPhone file"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 1; + shellPath = /bin/sh; + shellScript = "if [ \"${RC_PURPLE}\" = \"YES\" ]\nthen\n\tmkdir -p ${DSTROOT}//System/Library/Caches/com.apple.dyld\n\techo \"existence of this file enables dyld to have dylibs override shared cache\" > ${DSTROOT}//System/Library/Caches/com.apple.dyld/enable-dylibs-to-override-cache\nfi\n"; + showEnvVarsInLog = 0; + }; + F91083C91702592700831889 /* create dyld_cache_config.h */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "create dyld_cache_config.h"; + outputPaths = ( + "$(DERIVED_FILE_DIR)/dyld_cache_config.h", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/bash; + shellScript = "echo \"\" > ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\nif [ -z \"${ARM_SDK}\" ]; then\n # if iOS SDK not available, use MacOSX SDK\n ARM_SDK=`xcodebuild -sdk macosx.internal -version Path`\nfi\n\nSHARED_REGION_FILE=\"${ARM_SDK}/usr/include/mach/shared_region.h\"\n\n\nif [ -r \"${SHARED_REGION_FILE}\" ]; then\n echo -n \"#define ARM_SHARED_REGION_START \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n awk '/define SHARED_REGION_BASE_ARM[ \\t]/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n echo \"\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\n echo -n \"#define ARM_SHARED_REGION_SIZE \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n awk '/define SHARED_REGION_SIZE_ARM[ \\t]/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n echo \"\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\n echo -n \"#define ARM64_SHARED_REGION_START \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n awk '/define SHARED_REGION_BASE_ARM64/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n echo \"\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\n echo -n \"#define ARM64_SHARED_REGION_SIZE \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n awk '/define SHARED_REGION_SIZE_ARM64/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n echo \"\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\nelse\n echo \"ERROR: File needed to configure update_dyld_shared_cache does not exist '${SHARED_REGION_FILE}'\"\n exit 1\nfi\n\n"; + showEnvVarsInLog = 0; + }; + F9213B3F18BFC9CB001CB6E8 /* simulator entitlement */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 8; + files = ( + ); + inputPaths = ( + ); + name = "simulator entitlement"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 1; + shellPath = /bin/sh; + shellScript = "if [ \"${PRODUCT_NAME}\" = \"dyld_sim\" ]\nthen\n /usr/bin/codesign --force --sign - --entitlements ${SRCROOT}/dyld_sim-entitlements.plist ${INSTALL_DIR}/dyld_sim\nfi\n"; + showEnvVarsInLog = 0; + }; + F959621018849DF20003E4D4 /* add dyld symlink */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 8; + files = ( + ); + inputPaths = ( + ); + name = "add dyld symlink"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 1; + shellPath = /bin/bash; + shellScript = "if [[ \"${PLATFORM_NAME}\" == *simulator* ]]\nthen\n\tcd ${DSTROOT}/${INSTALL_PATH}\n\tln -s libdyld.dylib libdyld_sim.dylib\nfi\n"; + showEnvVarsInLog = 0; + }; + F991E3030FF1A4EC0082CCC9 /* do not install duplicates */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 8; + files = ( + ); + inputPaths = ( + ); + name = "do not install duplicates"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 1; + shellPath = /bin/sh; + shellScript = "if [ \"${INSTALL_LOCATION}\" = \"\" ] \nthen\n # on iOS, libdyld builds arm libdsc.a and u_d_s_c builds intel libdsc.a\n # on MacOSX, to avoid collision, u_d_s_c does not install libdsc.a\n\trm -rf ${DSTROOT}/usr/local/include\n\trm -rf ${DSTROOT}/usr/local/lib\n\trm -rf ${DSTROOT}/usr/lib\nfi\n"; + showEnvVarsInLog = 0; + }; + F99B8EB60FEC236500701838 /* suppress macosx dyld_shared_cache_util */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 8; + files = ( + ); + inputPaths = ( + ); + name = "suppress macosx dyld_shared_cache_util"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 1; + shellPath = /bin/sh; + shellScript = "# iPhone wants a copy of dyld_shared_cache_util on the device\n# MacOSX does not need a copy because update_dyld_shared_cache target already installed a copy\nif [ \"${PLATFORM_NAME}\" = \"macosx\" ] \nthen\n\trm -rf ${DSTROOT}/usr/local\nfi\n"; + showEnvVarsInLog = 0; + }; + F9D050C811DD701A00FB0A29 /* configure archives */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 12; + files = ( + ); + inputPaths = ( + ); + name = "configure archives"; + outputPaths = ( + "$(DERIVED_SOURCES_DIR)/archives.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "# link with all .a files in /usr/local/lib/dyld\nls -1 ${SDKROOT}/usr/local/lib/dyld/*.a > ${DERIVED_SOURCES_DIR}/archives.txt \n\n# link with crash report archive if it exists\nif [ -f ${SDKROOT}/usr/local/lib/libCrashReporterClient.a ]\nthen\n echo \\\"${SDKROOT}/usr/local/lib/libCrashReporterClient.a\\\" >> ${DERIVED_SOURCES_DIR}/archives.txt \nfi\n\n"; + showEnvVarsInLog = 0; + }; + F9F6F42B1C1FB0AE00BD8FED /* build */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 12; + files = ( + ); + inputPaths = ( + ); + name = build; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "${SRCROOT}/testing/build_tests.py && cp ${SRCROOT}/testing/run_all_dyld_tests.py ${DSTROOT}/AppleInternal/CoreOS/tests/dyld/\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 3703A1131B38C1B300ADBA7F /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 3703A1141B38C1B300ADBA7F /* AdjustForNewSegmentLocation.cpp in Sources */, + 370E5F441CC06CF8000158F2 /* Logging.cpp in Sources */, + 376ABDBB1C5930E7009F0011 /* MachOProxy.cpp in Sources */, + 3703A1161B38C1B300ADBA7F /* BindAllImages.cpp in Sources */, + 3703A1171B38C1B300ADBA7F /* OptimizerObjC.cpp in Sources */, + 3703A1181B38C1B300ADBA7F /* OptimizerBranches.cpp in Sources */, + 3703A1191B38C1B300ADBA7F /* OptimizerLinkedit.cpp in Sources */, + 3733C9081BD98F6900420392 /* dsc_iterator.cpp in Sources */, + 3703A11A1B38C1B300ADBA7F /* MultiCacheBuilder.mm in Sources */, + 3703A1261B38C22900ADBA7F /* dyld_shared_cache_builder.mm in Sources */, + 3703A11B1B38C1B300ADBA7F /* FileCache.cpp in Sources */, + 3703A11C1B38C1B300ADBA7F /* SharedCache.cpp in Sources */, + 37BF1D771B6168150048BC27 /* Manifest.mm in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 377685F41AC4B27D00026E6C /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 377685F51AC4B27D00026E6C /* AdjustForNewSegmentLocation.cpp in Sources */, + 370E5F431CC06CF8000158F2 /* Logging.cpp in Sources */, + 376ABDBA1C5930E7009F0011 /* MachOProxy.cpp in Sources */, + 377686041AC4B2ED00026E6C /* multi_dyld_shared_cache_builder.mm in Sources */, + 377685F61AC4B27D00026E6C /* BindAllImages.cpp in Sources */, + 377685F71AC4B27D00026E6C /* OptimizerObjC.cpp in Sources */, + 377685F81AC4B27D00026E6C /* OptimizerBranches.cpp in Sources */, + 3733C9071BD98F6800420392 /* dsc_iterator.cpp in Sources */, + 377685F91AC4B27D00026E6C /* OptimizerLinkedit.cpp in Sources */, + 371D29821B2F53C8000BBE48 /* MultiCacheBuilder.mm in Sources */, + 377685FA1AC4B27D00026E6C /* FileCache.cpp in Sources */, + 377685FB1AC4B27D00026E6C /* SharedCache.cpp in Sources */, + 37BF1D761B6168150048BC27 /* Manifest.mm in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F939372F0A94FAF700070A07 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 37F7A59C1BB364530039043A /* OptimizerBranches.cpp in Sources */, + 370E5F421CC06CF8000158F2 /* Logging.cpp in Sources */, + 375E6F441C59DEFF001BB760 /* MachOProxy.cpp in Sources */, + 37F7A59D1BB364530039043A /* OptimizerLinkedit.cpp in Sources */, + 37F7A59E1BB364530039043A /* OptimizerObjC.cpp in Sources */, + 37F7A59F1BB364530039043A /* AdjustForNewSegmentLocation.cpp in Sources */, + 37F7A5A01BB364530039043A /* BindAllImages.cpp in Sources */, + 3733C9091BD98F6A00420392 /* dsc_iterator.cpp in Sources */, + 37F7A59A1BB3642F0039043A /* FileCache.cpp in Sources */, + 37F7A59B1BB3642F0039043A /* SharedCache.cpp in Sources */, + 37F7A5981BB364130039043A /* Manifest.mm in Sources */, + 37F7A5991BB364130039043A /* MultiCacheBuilder.mm in Sources */, + 37F7A5951BB362CA0039043A /* update_dyld_shared_cache.mm in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F97FF3521C23638F000ACDD2 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F97FF3601C236408000ACDD2 /* execserver.defs in Sources */, + F97FF3611C23640C000ACDD2 /* nocr.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F99B8E530FEC10F600701838 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F99B8EA30FEC1C4200701838 /* dsc_iterator.cpp in Sources */, + F99B8E630FEC11B400701838 /* dyld_shared_cache_util.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F9D1000F14D8D0BA00099D91 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F9D1001814D8D13D00099D91 /* dsc_extractor.cpp in Sources */, + F9D1001D14D8D19500099D91 /* dsc_iterator.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F9ED4C950630A76000DF4E74 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F9ED4CDF0630A7F100DF4E74 /* dyldStartup.s in Sources */, + F9ED4CDB0630A7F100DF4E74 /* dyldInitialization.cpp in Sources */, + F9ED4CD70630A7F100DF4E74 /* dyld.cpp in Sources */, + F9ED4CD90630A7F100DF4E74 /* dyldAPIs.cpp in Sources */, + F9ED4CDA0630A7F100DF4E74 /* dyldExceptions.c in Sources */, + F9ED4CD60630A7F100DF4E74 /* dyld_gdb.cpp in Sources */, + F9ED4CE00630A7F100DF4E74 /* glue.c in Sources */, + F9280B7B1AB9DCA000B18AEC /* ImageLoaderMegaDylib.cpp in Sources */, + F9ED4CE10630A7F100DF4E74 /* ImageLoader.cpp in Sources */, + F9ED4CE30630A7F100DF4E74 /* ImageLoaderMachO.cpp in Sources */, + F9ED4CE50630A7F100DF4E74 /* stub_binding_helper.s in Sources */, + F9ED4CDE0630A7F100DF4E74 /* dyldNew.cpp in Sources */, + F94DB9040F0A9B1700323715 /* ImageLoaderMachOClassic.cpp in Sources */, + F94DB9050F0A9B1700323715 /* ImageLoaderMachOCompressed.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F9ED4C9C0630A76B00DF4E74 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F9F256360639DBCC00A7427D /* dyldLock.cpp in Sources */, + F9BA514B0ECE4F4200D1D62E /* dyld_stub_binder.s in Sources */, + F9A221E70F3A6D7C00D15F73 /* dyldLibSystemGlue.c in Sources */, + F913FADA0630A8AE00B7AE9D /* dyldAPIsInLibSystem.cpp in Sources */, + F9A6D6E4116F9DF20051CC16 /* threadLocalVariables.c in Sources */, + F9A6D70C116FBBD10051CC16 /* threadLocalHelpers.s in Sources */, + F95090E51C5AD1E80031F81D /* dyld_process_info.cpp in Sources */, + F958D4771C7FCE6700A0B199 /* dyld_process_info_notify.cpp in Sources */, + F9D49CCC1458B95200F86ADD /* start_glue.s in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F9F2A5560F7AEE9800B7C9EB /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F9F2A5700F7AEEE300B7C9EB /* dsc_iterator.cpp in Sources */, + F9CE307A1208F1B50098B590 /* dsc_extractor.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 37A0AD0F1C16000F00731E50 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 37A0AD0A1C15FFF500731E50 /* update_dyld_shared_cache */; + targetProxy = 37A0AD0E1C16000F00731E50 /* PBXContainerItemProxy */; + }; + 37A0AD111C16003600731E50 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = F93937310A94FAF700070A07 /* update_dyld_shared_cache_tool */; + targetProxy = 37A0AD101C16003600731E50 /* PBXContainerItemProxy */; + }; + 37A0AD131C16003600731E50 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = F9F2A5580F7AEE9800B7C9EB /* libdsc */; + targetProxy = 37A0AD121C16003600731E50 /* PBXContainerItemProxy */; + }; + 37A0AD151C16003600731E50 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = F9D1001114D8D0BA00099D91 /* dsc_extractor */; + targetProxy = 37A0AD141C16003600731E50 /* PBXContainerItemProxy */; + }; + 37A0AD171C16003600731E50 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 377685F21AC4B27D00026E6C /* multi_dyld_shared_cache_builder */; + targetProxy = 37A0AD161C16003600731E50 /* PBXContainerItemProxy */; + }; + 37A0AD191C16003600731E50 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 3703A1111B38C1B300ADBA7F /* dyld_shared_cache_builder */; + targetProxy = 37A0AD181C16003600731E50 /* PBXContainerItemProxy */; + }; + 37A0AD1B1C16004600731E50 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = F99B8E550FEC10F600701838 /* dyld_shared_cache_util */; + targetProxy = 37A0AD1A1C16004600731E50 /* PBXContainerItemProxy */; + }; + F908134811D3ED1A00626CC1 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = F9ED4C9E0630A76B00DF4E74 /* libdyld.dylib */; + targetProxy = F908134711D3ED1A00626CC1 /* PBXContainerItemProxy */; + }; + F97FF3661C237F97000ACDD2 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = F97FF3551C23638F000ACDD2 /* nocr */; + targetProxy = F97FF3651C237F97000ACDD2 /* PBXContainerItemProxy */; + }; + F99B8EB20FEC220C00701838 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = F99B8E550FEC10F600701838 /* dyld_shared_cache_util */; + targetProxy = F99B8EB10FEC220C00701838 /* PBXContainerItemProxy */; + }; + F9B4D78012AD9736000605A6 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = F9F2A5580F7AEE9800B7C9EB /* libdsc */; + targetProxy = F9B4D77F12AD9736000605A6 /* PBXContainerItemProxy */; + }; + F9ED4CA70630A78A00DF4E74 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = F9ED4C970630A76000DF4E74 /* dyld */; + targetProxy = F9ED4CA60630A78A00DF4E74 /* PBXContainerItemProxy */; + }; + F9ED4CA90630A78A00DF4E74 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = F9ED4C9E0630A76B00DF4E74 /* libdyld.dylib */; + targetProxy = F9ED4CA80630A78A00DF4E74 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + 3703A1221B38C1B300ADBA7F /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "c++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = NO; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)"; + ENABLE_STRICT_OBJC_MSGSEND = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks", + "$(SDKROOT)$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks", + "$(SDKROOT)$(APPLE_INTERNAL_LIBRARY_DIR)/Frameworks", + ); + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + INSTALL_PATH = "$(INSTALL_LOCATION)/usr/local/bin"; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + OTHER_CFLAGS = "-DBOM_SUPPORT=1"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx.internal; + USER_HEADER_SEARCH_PATHS = "../launch-cache/"; + VALID_ARCHS = "armv6 armv7 x86_64 x86_64h"; + }; + name = Debug; + }; + 3703A1231B38C1B300ADBA7F /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "c++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = NO; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)"; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks", + "$(SDKROOT)$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks", + "$(SDKROOT)$(APPLE_INTERNAL_LIBRARY_DIR)/Frameworks", + ); + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + INSTALL_PATH = "$(INSTALL_LOCATION)/usr/local/bin"; + MTL_ENABLE_DEBUG_INFO = NO; + OTHER_CFLAGS = "-DBOM_SUPPORT=1"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx.internal; + USER_HEADER_SEARCH_PATHS = "../launch-cache/"; + VALID_ARCHS = "armv6 armv7 x86_64 x86_64h"; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Release; + }; + 377686001AC4B27D00026E6C /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "c++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = NO; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)"; + ENABLE_STRICT_OBJC_MSGSEND = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks", + "$(SDKROOT)$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks", + "$(SDKROOT)/AppleInternal/Library/Frameworks", + ); + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + INSTALL_PATH = "$(INSTALL_LOCATION)/usr/local/bin"; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + OTHER_CFLAGS = "-DBOM_SUPPORT=1"; + PRODUCT_NAME = multi_dyld_shared_cache_builder; + SDKROOT = macosx.internal; + STRIP_INSTALLED_PRODUCT = NO; + USER_HEADER_SEARCH_PATHS = "../launch-cache/"; + VALID_ARCHS = "armv6 armv7 x86_64 x86_64h"; + }; + name = Debug; + }; + 377686011AC4B27D00026E6C /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "c++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = NO; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)"; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks", + "$(SDKROOT)$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks", + "$(SDKROOT)/AppleInternal/Library/Frameworks", + ); + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + INSTALL_PATH = "$(INSTALL_LOCATION)/usr/local/bin"; + MTL_ENABLE_DEBUG_INFO = NO; + OTHER_CFLAGS = "-DBOM_SUPPORT=1"; + PRODUCT_NAME = multi_dyld_shared_cache_builder; + SDKROOT = macosx.internal; + STRIP_INSTALLED_PRODUCT = NO; + USER_HEADER_SEARCH_PATHS = "../launch-cache/"; + VALID_ARCHS = "armv6 armv7 x86_64 x86_64h"; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Release; + }; + 37A0AD0C1C15FFF500731E50 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 37A0AD0D1C15FFF500731E50 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + F908134311D3ED0C00626CC1 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = NO; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + INSTALLHDRS_COPY_PHASE = YES; + PRODUCT_NAME = libdyld; + }; + name = Debug; + }; + F908134411D3ED0C00626CC1 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + INSTALLHDRS_COPY_PHASE = YES; + PRODUCT_NAME = libdyld; + ZERO_LINK = NO; + }; + name = Release; + }; + F93937350A94FB2900070A07 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_CXX_LANGUAGE_STANDARD = "c++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_OBJC_ARC = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(SDKROOT)$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks", + ); + GCC_DYNAMIC_NO_PIC = NO; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_THREADSAFE_STATICS = NO; + GCC_VERSION = com.apple.compilers.llvm.clang.1_0; + GCC_WARN_CHECK_SWITCH_STATEMENTS = YES; + GCC_WARN_EFFECTIVE_CPLUSPLUS_VIOLATIONS = NO; + GCC_WARN_MISSING_PARENTHESES = YES; + GCC_WARN_SHADOW = YES; + GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = "$(SRCROOT)/include"; + OTHER_CPLUSPLUSFLAGS = "$(OTHER_CFLAGS)"; + OTHER_LDFLAGS = "-stdlib=libc++"; + PRODUCT_NAME = update_dyld_shared_cache; + SDKROOT = macosx.internal; + VALID_ARCHS = x86_64; + }; + name = Debug; + }; + F93937360A94FB2900070A07 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = F971DD161A4A0E0700BBDD52 /* update_dyld_shared_cache.xcconfig */; + buildSettings = { + CLANG_CXX_LANGUAGE_STANDARD = "c++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_OBJC_ARC = YES; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)"; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(SDKROOT)$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks", + ); + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = s; + GCC_THREADSAFE_STATICS = NO; + GCC_VERSION = com.apple.compilers.llvm.clang.1_0; + GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES; + HEADER_SEARCH_PATHS = "$(SRCROOT)/include"; + OTHER_CPLUSPLUSFLAGS = "$(OTHER_CFLAGS)"; + OTHER_LDFLAGS = "-stdlib=libc++"; + PRODUCT_NAME = update_dyld_shared_cache; + SDKROOT = macosx.internal; + STRIP_INSTALLED_PRODUCT = YES; + STRIP_STYLE = debugging; + VALID_ARCHS = x86_64; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Release; + }; + F97FF35A1C23638F000ACDD2 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = NO; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = ( + "$(DERIVED_SOURCES_DIR)/**", + "$(SDKROOT)/System/Library/Frameworks/System.framework/PrivateHeaders", + ); + MACOSX_DEPLOYMENT_TARGET = 10.11; + MTL_ENABLE_DEBUG_INFO = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx.internal; + }; + name = Debug; + }; + F97FF35B1C23638F000ACDD2 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = NO; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = ( + "$(DERIVED_SOURCES_DIR)/**", + "$(SDKROOT)/System/Library/Frameworks/System.framework/PrivateHeaders", + ); + MACOSX_DEPLOYMENT_TARGET = 10.11; + MTL_ENABLE_DEBUG_INFO = NO; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx.internal; + }; + name = Release; + }; + F99B8E580FEC10F600701838 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "c++14"; + COPY_PHASE_STRIP = NO; + GCC_DYNAMIC_NO_PIC = NO; + GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + /Applications/Xcode.app/Contents/Developer/Toolchains/OSX10.10.xctoolchain/usr/include, + "$(SRCROOT)/interlinked-dylibs/", + ); + INSTALL_PATH = "$(INSTALL_LOCATION)/usr/local/bin"; + PRODUCT_NAME = dyld_shared_cache_util; + }; + name = Debug; + }; + F99B8E590FEC10F600701838 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "c++14"; + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_DYNAMIC_NO_PIC = NO; + GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + /Applications/Xcode.app/Contents/Developer/Toolchains/OSX10.10.xctoolchain/usr/include, + "$(SRCROOT)/interlinked-dylibs/", + ); + INSTALL_PATH = "$(INSTALL_LOCATION)/usr/local/bin"; + PRODUCT_NAME = dyld_shared_cache_util; + SKIP_INSTALL = NO; + }; + name = Release; + }; + F9D1001314D8D0BB00099D91 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COMBINE_HIDPI_IMAGES = YES; + COPY_PHASE_STRIP = NO; + DYLIB_COMPATIBILITY_VERSION = ""; + DYLIB_CURRENT_VERSION = ""; + EXECUTABLE_EXTENSION = bundle; + GCC_DYNAMIC_NO_PIC = NO; + GCC_INLINES_ARE_PRIVATE_EXTERN = YES; + GCC_OPTIMIZATION_LEVEL = 0; + INSTALL_PATH = "$(INSTALL_PATH_PREFIX)$(INSTALL_LOCATION)/usr/lib"; + MACH_O_TYPE = mh_bundle; + OTHER_CPLUSPLUSFLAGS = ( + "-stdlib=libc++", + "$(OTHER_CFLAGS)", + ); + OTHER_LDFLAGS = ( + "-stdlib=libc++", + "-Wl,-exported_symbol,_dyld_shared_cache_extract_dylibs_progress", + ); + PRODUCT_NAME = dsc_extractor; + }; + name = Debug; + }; + F9D1001414D8D0BB00099D91 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COMBINE_HIDPI_IMAGES = YES; + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DYLIB_COMPATIBILITY_VERSION = ""; + DYLIB_CURRENT_VERSION = ""; + EXECUTABLE_EXTENSION = bundle; + GCC_INLINES_ARE_PRIVATE_EXTERN = YES; + INSTALL_PATH = "$(INSTALL_PATH_PREFIX)$(INSTALL_LOCATION)/usr/lib"; + MACH_O_TYPE = mh_bundle; + OTHER_CPLUSPLUSFLAGS = ( + "-stdlib=libc++", + "$(OTHER_CFLAGS)", + ); + OTHER_LDFLAGS = ( + "-stdlib=libc++", + "-Wl,-exported_symbol,_dyld_shared_cache_extract_dylibs_progress", + ); + PRODUCT_NAME = dsc_extractor; + ZERO_LINK = NO; + }; + name = Release; + }; + F9D8C7DE087B087300E93EFB /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = F971DD141A4A0E0700BBDD52 /* dyld.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ARCHS = "$(ARCHS_STANDARD_32_64_BIT)"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_WARN_EMPTY_BODY = YES; + CODE_SIGN_IDENTITY = "-"; + CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)"; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = dwarf; + GCC_C_LANGUAGE_STANDARD = c99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_BUILTIN_FUNCTIONS = NO; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = "DYLD_VERSION=$(RC_ProjectSourceVersion)"; + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO; + GCC_WARN_ALLOW_INCOMPLETE_PROTOCOL = NO; + GCC_WARN_MISSING_PARENTHESES = YES; + GCC_WARN_SHADOW = YES; + GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES; + GCC_WARN_UNINITIALIZED_AUTOS = NO; + HEADER_SEARCH_PATHS = ( + ./include, + "./launch-cache", + ); + LD_GENERATE_MAP_FILE = YES; + OTHER_CFLAGS = ""; + OTHER_CPLUSPLUSFLAGS = ( + "-stdlib=libc++", + "$(OTHER_CFLAGS)", + ); + OTHER_LDFLAGS = ( + "@$(DERIVED_SOURCES_DIR)/archives.txt", + "-nostdlib", + "-Wl,-dylinker", + "-Wl,-dylinker_install_name,/usr/lib/dyld", + "-stdlib=libc++", + "$(ALIGNMENT)", + "$(ENTRY)", + ); + SDKROOT = macosx.internal; + STRIPFLAGS = "-S"; + UNSTRIPPED_PRODUCT = NO; + VERSIONING_SYSTEM = "apple-generic"; + WARNING_CFLAGS = ( + "-Wmost", + "-Wno-four-char-constants", + ); + }; + name = Debug; + }; + F9D8C7E0087B087300E93EFB /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = F971DD141A4A0E0700BBDD52 /* dyld.xcconfig */; + buildSettings = { + ARCHS = "$(ARCHS_STANDARD_32_64_BIT)"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_WARN_EMPTY_BODY = YES; + CODE_SIGN_IDENTITY = "-"; + CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)"; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_C_LANGUAGE_STANDARD = c99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_CPP_RTTI = NO; + GCC_OPTIMIZATION_LEVEL = s; + GCC_PREPROCESSOR_DEFINITIONS = "DYLD_VERSION=$(RC_ProjectSourceVersion)"; + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO; + GCC_WARN_SHADOW = YES; + GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES; + HEADER_SEARCH_PATHS = ( + ./include, + "./launch-cache", + ); + LD_GENERATE_MAP_FILE = YES; + ORDER_FILE = "$(SRCROOT)/src/dyld.order"; + OTHER_CFLAGS = ""; + "OTHER_CFLAGS[arch=armv6]" = "-mthumb"; + OTHER_CPLUSPLUSFLAGS = ( + "-stdlib=libc++", + "$(OTHER_CFLAGS)", + ); + OTHER_LDFLAGS = ( + "@$(DERIVED_SOURCES_DIR)/archives.txt", + "-nostdlib", + "-Wl,-dylinker", + "-Wl,-dylinker_install_name,/usr/lib/dyld", + "-stdlib=libc++", + "$(ALIGNMENT)", + "$(ENTRY)", + "-Wl,-no_data_const", + "-Wl,-section_order,__DATA,__all_image_info:__nl_symbol_ptr:__got:__const:__crash_info:__data:__common:__bss", + ); + SDKROOT = macosx.internal; + STRIPFLAGS = "-S"; + UNSTRIPPED_PRODUCT = NO; + VERSIONING_SYSTEM = "apple-generic"; + WARNING_CFLAGS = ( + "-Wmost", + "-Wno-four-char-constants", + "-Wno-unknown-pragmas", + ); + }; + name = Release; + }; + F9D8C7E2087B087300E93EFB /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = F971DD151A4A0E0700BBDD52 /* libdyld.xcconfig */; + buildSettings = { + ARCHS = "$(ARCHS_STANDARD_32_64_BIT)"; + CLANG_CXX_LANGUAGE_STANDARD = "c++0x"; + CLANG_WARN_EMPTY_BODY = YES; + COMBINE_HIDPI_IMAGES = YES; + DEAD_CODE_STRIPPING = YES; + EXECUTABLE_PREFIX = lib; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_ENABLE_CPP_EXCEPTIONS = NO; + GCC_ENABLE_CPP_RTTI = NO; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_SHADOW = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + HEADER_SEARCH_PATHS = "$(SRCROOT)/include"; + INSTALLHDRS_COPY_PHASE = YES; + OTHER_CFLAGS = ""; + OTHER_LDFLAGS = ( + "-nostdlib", + "$(LIBSYSTEM_LIBS)", + "-umbrella", + System, + "-L$(SDKROOT)/usr/lib/system", + ); + PRODUCT_NAME = dyld; + SKIP_INSTALL = NO; + WARNING_CFLAGS = ( + "-Wmost", + "-Wno-four-char-constants", + "-Wno-unknown-pragmas", + ); + }; + name = Debug; + }; + F9D8C7E4087B087300E93EFB /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = F971DD151A4A0E0700BBDD52 /* libdyld.xcconfig */; + buildSettings = { + ARCHS = "$(ARCHS_STANDARD_32_64_BIT)"; + CLANG_CXX_LANGUAGE_STANDARD = "c++0x"; + CLANG_WARN_EMPTY_BODY = YES; + COMBINE_HIDPI_IMAGES = YES; + COPY_PHASE_STRIP = YES; + CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)"; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DYLIB_CURRENT_VERSION = "$(RC_ProjectSourceVersion)"; + EXECUTABLE_PREFIX = lib; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_ENABLE_CPP_EXCEPTIONS = NO; + GCC_ENABLE_CPP_RTTI = NO; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_SHADOW = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + HEADER_SEARCH_PATHS = "$(SRCROOT)/include"; + INSTALLHDRS_COPY_PHASE = YES; + OTHER_CFLAGS = ""; + OTHER_CPLUSPLUSFLAGS = ( + "-fno-exceptions", + "$(OTHER_CFLAGS)", + ); + OTHER_LDFLAGS = ( + "-nostdlib", + "$(LIBSYSTEM_LIBS)", + "-umbrella", + System, + "-L$(SDKROOT)/usr/lib/system", + ); + "OTHER_LDFLAGS[sdk=iphoneos*]" = ( + "-nostdlib", + "$(LIBSYSTEM_LIBS)", + "-umbrella", + System, + "-L$(SDKROOT)/usr/lib/system", + "-Wl,-dirty_data_list,$(SRCROOT)/src/libdyld_data_symbols.dirty", + ); + "OTHER_LDFLAGS[sdk=macosx*]" = ( + "-nostdlib", + "$(LIBSYSTEM_LIBS)", + "-umbrella", + System, + "-L$(SDKROOT)/usr/lib/system", + ); + PRODUCT_NAME = dyld; + SEPARATE_STRIP = YES; + SKIP_INSTALL = NO; + STRIP_INSTALLED_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + WARNING_CFLAGS = ( + "-Wmost", + "-Wno-four-char-constants", + "-Wno-unknown-pragmas", + ); + }; + name = Release; + }; + F9D8C7E6087B087300E93EFB /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = all; + }; + name = Debug; + }; + F9D8C7E8087B087300E93EFB /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = all; + }; + name = Release; + }; + F9D8C7EA087B087300E93EFB /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = F971DD131A4A0E0700BBDD52 /* base.xcconfig */; + buildSettings = { + CLANG_CXX_LIBRARY = "compiler-default"; + ONLY_ACTIVE_ARCH = NO; + SDKROOT = macosx.internal; + SUPPORTED_PLATFORMS = "iphonesimulator iphoneos macosx watchos appletvos"; + }; + name = Debug; + }; + F9D8C7EC087B087300E93EFB /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = F971DD131A4A0E0700BBDD52 /* base.xcconfig */; + buildSettings = { + CLANG_CXX_LIBRARY = "compiler-default"; + SDKROOT = macosx.internal; + SUPPORTED_PLATFORMS = "iphonesimulator iphoneos macosx watchos appletvos"; + }; + name = Release; + }; + F9F2A55A0F7AEE9900B7C9EB /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COMBINE_HIDPI_IMAGES = YES; + COPY_PHASE_STRIP = NO; + GCC_DYNAMIC_NO_PIC = NO; + GCC_INLINES_ARE_PRIVATE_EXTERN = YES; + GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_SYMBOLS_PRIVATE_EXTERN = YES; + INSTALL_PATH = "$(INSTALL_LOCATION)/usr/local/lib"; + OTHER_CPLUSPLUSFLAGS = ( + "-stdlib=libc++", + "$(OTHER_CFLAGS)", + ); + PRODUCT_NAME = dsc; + }; + name = Debug; + }; + F9F2A55B0F7AEE9900B7C9EB /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + COMBINE_HIDPI_IMAGES = YES; + COPY_PHASE_STRIP = NO; + GCC_ENABLE_CPP_EXCEPTIONS = NO; + GCC_ENABLE_CPP_RTTI = NO; + GCC_ENABLE_OBJC_EXCEPTIONS = NO; + GCC_INLINES_ARE_PRIVATE_EXTERN = YES; + GCC_MODEL_TUNING = G5; + GCC_SYMBOLS_PRIVATE_EXTERN = YES; + GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_PEDANTIC = NO; + GCC_WARN_SHADOW = NO; + GCC_WARN_SIGN_COMPARE = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + INSTALLHDRS_COPY_PHASE = YES; + INSTALL_PATH = "$(INSTALL_LOCATION)/usr/local/lib"; + OTHER_CPLUSPLUSFLAGS = ( + "-stdlib=libc++", + "$(OTHER_CFLAGS)", + ); + PRODUCT_NAME = dsc; + ZERO_LINK = NO; + }; + name = Release; + }; + F9F6F4281C1FB0A700BD8FED /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + F9F6F4291C1FB0A700BD8FED /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 3703A1211B38C1B300ADBA7F /* Build configuration list for PBXNativeTarget "dyld_shared_cache_builder" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 3703A1221B38C1B300ADBA7F /* Debug */, + 3703A1231B38C1B300ADBA7F /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 377685FF1AC4B27D00026E6C /* Build configuration list for PBXNativeTarget "multi_dyld_shared_cache_builder" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 377686001AC4B27D00026E6C /* Debug */, + 377686011AC4B27D00026E6C /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 37A0AD0B1C15FFF500731E50 /* Build configuration list for PBXAggregateTarget "update_dyld_shared_cache" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 37A0AD0C1C15FFF500731E50 /* Debug */, + 37A0AD0D1C15FFF500731E50 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F908135211D3ED9000626CC1 /* Build configuration list for PBXAggregateTarget "libdyld" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F908134311D3ED0C00626CC1 /* Debug */, + F908134411D3ED0C00626CC1 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F93937340A94FB2900070A07 /* Build configuration list for PBXNativeTarget "update_dyld_shared_cache_tool" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F93937350A94FB2900070A07 /* Debug */, + F93937360A94FB2900070A07 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F97FF35C1C23638F000ACDD2 /* Build configuration list for PBXNativeTarget "nocr" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F97FF35A1C23638F000ACDD2 /* Debug */, + F97FF35B1C23638F000ACDD2 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F99B8E5D0FEC10F800701838 /* Build configuration list for PBXNativeTarget "dyld_shared_cache_util" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F99B8E580FEC10F600701838 /* Debug */, + F99B8E590FEC10F600701838 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F9D1001714D8D0F100099D91 /* Build configuration list for PBXNativeTarget "dsc_extractor" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F9D1001314D8D0BB00099D91 /* Debug */, + F9D1001414D8D0BB00099D91 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F9D8C7DD087B087300E93EFB /* Build configuration list for PBXNativeTarget "dyld" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F9D8C7DE087B087300E93EFB /* Debug */, + F9D8C7E0087B087300E93EFB /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F9D8C7E1087B087300E93EFB /* Build configuration list for PBXNativeTarget "libdyld.dylib" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F9D8C7E2087B087300E93EFB /* Debug */, + F9D8C7E4087B087300E93EFB /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F9D8C7E5087B087300E93EFB /* Build configuration list for PBXAggregateTarget "all" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F9D8C7E6087B087300E93EFB /* Debug */, + F9D8C7E8087B087300E93EFB /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F9D8C7E9087B087300E93EFB /* Build configuration list for PBXProject "dyld" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F9D8C7EA087B087300E93EFB /* Debug */, + F9D8C7EC087B087300E93EFB /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F9F2A56B0F7AEEB100B7C9EB /* Build configuration list for PBXNativeTarget "libdsc" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F9F2A55A0F7AEE9900B7C9EB /* Debug */, + F9F2A55B0F7AEE9900B7C9EB /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F9F6F42A1C1FB0A700BD8FED /* Build configuration list for PBXAggregateTarget "dyld_tests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F9F6F4281C1FB0A700BD8FED /* Debug */, + F9F6F4291C1FB0A700BD8FED /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = F9ED4C8B0630A72300DF4E74 /* Project object */; +} diff --git a/src/dyld-apple/dyld_kernel.h b/src/dyld-apple/dyld_kernel.h new file mode 100644 index 000000000..16b93f2c0 --- /dev/null +++ b/src/dyld-apple/dyld_kernel.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2016 Apple Computer, Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_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. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * 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_OSREFERENCE_LICENSE_HEADER_END@ + */ + +#ifndef _MACH_DYLIB_INFO_H_ +#define _MACH_DYLIB_INFO_H_ + +#include +#include +#include +#include +#include + +/* These definitions must be kept in sync with the ones in + * osfmk/mach/mach_types.defs. + */ + +struct dyld_kernel_image_info { + uuid_t uuid; + fsobj_id_t fsobjid; + fsid_t fsid; + uint64_t load_addr; +}; + +struct dyld_kernel_process_info { + struct dyld_kernel_image_info cache_image_info; + uint64_t timestamp; // mach_absolute_time of last time dyld change to image list + uint32_t imageCount; // number of images currently loaded into process + uint32_t initialImageCount; // number of images statically loaded into process (before any dlopen() calls) + uint8_t dyldState; // one of dyld_process_state_* values + boolean_t no_cache; // process is running without a dyld cache + boolean_t private_cache; // process is using a private copy of its dyld cache +}; + +/* typedefs so our MIG is sane */ + +typedef struct dyld_kernel_image_info dyld_kernel_image_info_t; +typedef struct dyld_kernel_process_info dyld_kernel_process_info_t; +typedef dyld_kernel_image_info_t *dyld_kernel_image_info_array_t; + +#endif /* _MACH_DYLIB_INFO_H_ */ diff --git a/src/dyld-apple/dyld_sim-entitlements.plist b/src/dyld-apple/dyld_sim-entitlements.plist new file mode 100644 index 000000000..0509fb22f --- /dev/null +++ b/src/dyld-apple/dyld_sim-entitlements.plist @@ -0,0 +1,8 @@ + + + + + com.apple.private.dyld_sim + + + diff --git a/src/dyld-apple/include/dlfcn.h b/src/dyld-apple/include/dlfcn.h new file mode 100644 index 000000000..bcb0c098d --- /dev/null +++ b/src/dyld-apple/include/dlfcn.h @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2004-2008 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@ + */ + +/* + Based on the dlcompat work done by: + Jorge Acereda & + Peter O'Gorman +*/ + +#ifndef _DLFCN_H_ +#define _DLFCN_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#if !defined(_POSIX_C_SOURCE) || defined(_DARWIN_C_SOURCE) +#include +#include +/* + * Structure filled in by dladdr(). + */ +typedef struct dl_info { + const char *dli_fname; /* Pathname of shared object */ + void *dli_fbase; /* Base address of shared object */ + const char *dli_sname; /* Name of nearest symbol */ + void *dli_saddr; /* Address of nearest symbol */ +} Dl_info; + +extern int dladdr(const void *, Dl_info *); +#endif /* not POSIX */ + +extern int dlclose(void * __handle); +extern char * dlerror(void); +extern void * dlopen(const char * __path, int __mode); +extern void * dlsym(void * __handle, const char * __symbol); + +#if !defined(_POSIX_C_SOURCE) || defined(_DARWIN_C_SOURCE) +extern bool dlopen_preflight(const char* __path) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0); +#endif /* not POSIX */ + + +#define RTLD_LAZY 0x1 +#define RTLD_NOW 0x2 +#define RTLD_LOCAL 0x4 +#define RTLD_GLOBAL 0x8 + +#if !defined(_POSIX_C_SOURCE) || defined(_DARWIN_C_SOURCE) +#define RTLD_NOLOAD 0x10 +#define RTLD_NODELETE 0x80 +#define RTLD_FIRST 0x100 /* Mac OS X 10.5 and later */ + +/* + * Special handle arguments for dlsym(). + */ +#define RTLD_NEXT ((void *) -1) /* Search subsequent objects. */ +#define RTLD_DEFAULT ((void *) -2) /* Use default search algorithm. */ +#define RTLD_SELF ((void *) -3) /* Search this and subsequent objects (Mac OS X 10.5 and later) */ +#define RTLD_MAIN_ONLY ((void *) -5) /* Search main executable only (Mac OS X 10.5 and later) */ +#endif /* not POSIX */ + +#ifdef __cplusplus +} +#endif + +#endif /* _DLFCN_H_ */ diff --git a/src/dyld-apple/include/mach-o/dyld-interposing.h b/src/dyld-apple/include/mach-o/dyld-interposing.h new file mode 100644 index 000000000..1621a5265 --- /dev/null +++ b/src/dyld-apple/include/mach-o/dyld-interposing.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2005-2008 Apple Computer, 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@ + */ + +#if !defined(_DYLD_INTERPOSING_H_) +#define _DYLD_INTERPOSING_H_ + +/* + * Example: + * + * static + * int + * my_open(const char* path, int flags, mode_t mode) + * { + * int value; + * // do stuff before open (including changing the arguments) + * value = open(path, flags, mode); + * // do stuff after open (including changing the return value(s)) + * return value; + * } + * DYLD_INTERPOSE(my_open, open) + */ + +#define DYLD_INTERPOSE(_replacement,_replacee) \ + __attribute__((used)) static struct{ const void* replacement; const void* replacee; } _interpose_##_replacee \ + __attribute__ ((section ("__DATA,__interpose"))) = { (const void*)(unsigned long)&_replacement, (const void*)(unsigned long)&_replacee }; + +#endif diff --git a/src/dyld-apple/include/mach-o/dyld.h b/src/dyld-apple/include/mach-o/dyld.h new file mode 100644 index 000000000..ce411476b --- /dev/null +++ b/src/dyld-apple/include/mach-o/dyld.h @@ -0,0 +1,247 @@ +/* + * Copyright (c) 1999-2008 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@ + */ +#ifndef _MACH_O_DYLD_H_ +#define _MACH_O_DYLD_H_ + + +#include +#include +#include + +#include +#include + +#if __cplusplus +extern "C" { +#endif + +/* + * The following functions allow you to iterate through all loaded images. + * This is not a thread safe operation. Another thread can add or remove + * an image during the iteration. + * + * Many uses of these routines can be replace by a call to dladdr() which + * will return the mach_header and name of an image, given an address in + * the image. dladdr() is thread safe. + */ +extern uint32_t _dyld_image_count(void) __OSX_AVAILABLE_STARTING(__MAC_10_1, __IPHONE_2_0); +extern const struct mach_header* _dyld_get_image_header(uint32_t image_index) __OSX_AVAILABLE_STARTING(__MAC_10_1, __IPHONE_2_0); +extern intptr_t _dyld_get_image_vmaddr_slide(uint32_t image_index) __OSX_AVAILABLE_STARTING(__MAC_10_1, __IPHONE_2_0); +extern const char* _dyld_get_image_name(uint32_t image_index) __OSX_AVAILABLE_STARTING(__MAC_10_1, __IPHONE_2_0); + + +/* + * The following functions allow you to install callbacks which will be called + * by dyld whenever an image is loaded or unloaded. During a call to _dyld_register_func_for_add_image() + * the callback func is called for every existing image. Later, it is called as each new image + * is loaded and bound (but initializers not yet run). The callback registered with + * _dyld_register_func_for_remove_image() is called after any terminators in an image are run + * and before the image is un-memory-mapped. + */ +extern void _dyld_register_func_for_add_image(void (*func)(const struct mach_header* mh, intptr_t vmaddr_slide)) __OSX_AVAILABLE_STARTING(__MAC_10_1, __IPHONE_2_0); +extern void _dyld_register_func_for_remove_image(void (*func)(const struct mach_header* mh, intptr_t vmaddr_slide)) __OSX_AVAILABLE_STARTING(__MAC_10_1, __IPHONE_2_0); + + +/* + * NSVersionOfRunTimeLibrary() returns the current_version number of the currently dylib + * specifed by the libraryName. The libraryName parameter would be "bar" for /path/libbar.3.dylib and + * "Foo" for /path/Foo.framework/Versions/A/Foo. It returns -1 if no such library is loaded. + */ +extern int32_t NSVersionOfRunTimeLibrary(const char* libraryName) __OSX_AVAILABLE_STARTING(__MAC_10_1, __IPHONE_2_0); + + +/* + * NSVersionOfLinkTimeLibrary() returns the current_version number that the main executable was linked + * against at build time. The libraryName parameter would be "bar" for /path/libbar.3.dylib and + * "Foo" for /path/Foo.framework/Versions/A/Foo. It returns -1 if the main executable did not link + * against the specified library. + */ +extern int32_t NSVersionOfLinkTimeLibrary(const char* libraryName) __OSX_AVAILABLE_STARTING(__MAC_10_1, __IPHONE_2_0); + + +/* + * _NSGetExecutablePath() copies the path of the main executable into the buffer. The bufsize parameter + * should initially be the size of the buffer. The function returns 0 if the path was successfully copied, + * and *bufsize is left unchanged. It returns -1 if the buffer is not large enough, and *bufsize is set + * to the size required. + * + * Note that _NSGetExecutablePath will return "a path" to the executable not a "real path" to the executable. + * That is the path may be a symbolic link and not the real file. With deep directories the total bufsize + * needed could be more than MAXPATHLEN. + */ +extern int _NSGetExecutablePath(char* buf, uint32_t* bufsize) __OSX_AVAILABLE_STARTING(__MAC_10_2, __IPHONE_2_0); + + + + + + +/* + * The following dyld API's are deprecated as of Mac OS X 10.5. They are either + * no longer necessary or are superceeded by dlopen and friends in . + * dlopen/dlsym/dlclose have been available since Mac OS X 10.3 and work with + * dylibs and bundles. + * + * NSAddImage -> dlopen + * NSLookupSymbolInImage -> dlsym + * NSCreateObjectFileImageFromFile -> dlopen + * NSDestroyObjectFileImage -> dlclose + * NSLinkModule -> not needed when dlopen used + * NSUnLinkModule -> not needed when dlclose used + * NSLookupSymbolInModule -> dlsym + * _dyld_image_containing_address -> dladdr + * NSLinkEditError -> dlerror + * + */ + +#ifndef ENUM_DYLD_BOOL +#define ENUM_DYLD_BOOL + #undef FALSE + #undef TRUE + enum DYLD_BOOL { FALSE, TRUE }; +#endif /* ENUM_DYLD_BOOL */ + + +/* Object file image API */ +typedef enum { + NSObjectFileImageFailure, /* for this a message is printed on stderr */ + NSObjectFileImageSuccess, + NSObjectFileImageInappropriateFile, + NSObjectFileImageArch, + NSObjectFileImageFormat, /* for this a message is printed on stderr */ + NSObjectFileImageAccess +} NSObjectFileImageReturnCode; + +typedef struct __NSObjectFileImage* NSObjectFileImage; + +/* NSObjectFileImage can only be used with MH_BUNDLE files */ +extern NSObjectFileImageReturnCode NSCreateObjectFileImageFromFile(const char* pathName, NSObjectFileImage *objectFileImage) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_5,__IPHONE_NA,__IPHONE_NA); +extern NSObjectFileImageReturnCode NSCreateObjectFileImageFromMemory(const void *address, size_t size, NSObjectFileImage *objectFileImage) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_5,__IPHONE_NA,__IPHONE_NA); +extern bool NSDestroyObjectFileImage(NSObjectFileImage objectFileImage) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_5,__IPHONE_NA,__IPHONE_NA); + +extern uint32_t NSSymbolDefinitionCountInObjectFileImage(NSObjectFileImage objectFileImage) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_5,__IPHONE_NA,__IPHONE_NA); +extern const char* NSSymbolDefinitionNameInObjectFileImage(NSObjectFileImage objectFileImage, uint32_t ordinal) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_5,__IPHONE_NA,__IPHONE_NA); +extern uint32_t NSSymbolReferenceCountInObjectFileImage(NSObjectFileImage objectFileImage) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_5,__IPHONE_NA,__IPHONE_NA); +extern const char* NSSymbolReferenceNameInObjectFileImage(NSObjectFileImage objectFileImage, uint32_t ordinal, bool *tentative_definition) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_5,__IPHONE_NA,__IPHONE_NA); +extern bool NSIsSymbolDefinedInObjectFileImage(NSObjectFileImage objectFileImage, const char* symbolName) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_4,__IPHONE_NA,__IPHONE_NA); +extern void* NSGetSectionDataInObjectFileImage(NSObjectFileImage objectFileImage, const char* segmentName, const char* sectionName, size_t *size) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_5,__IPHONE_NA,__IPHONE_NA); +extern bool NSHasModInitObjectFileImage(NSObjectFileImage objectFileImage) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_3,__MAC_10_5,__IPHONE_NA,__IPHONE_NA); + +typedef struct __NSModule* NSModule; +extern const char* NSNameOfModule(NSModule m) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_5,__IPHONE_NA,__IPHONE_NA); +extern const char* NSLibraryNameForModule(NSModule m) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_5,__IPHONE_NA,__IPHONE_NA); + +extern NSModule NSLinkModule(NSObjectFileImage objectFileImage, const char* moduleName, uint32_t options) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_5,__IPHONE_NA,__IPHONE_NA); +#define NSLINKMODULE_OPTION_NONE 0x0 +#define NSLINKMODULE_OPTION_BINDNOW 0x1 +#define NSLINKMODULE_OPTION_PRIVATE 0x2 +#define NSLINKMODULE_OPTION_RETURN_ON_ERROR 0x4 +#define NSLINKMODULE_OPTION_DONT_CALL_MOD_INIT_ROUTINES 0x8 +#define NSLINKMODULE_OPTION_TRAILING_PHYS_NAME 0x10 + +extern bool NSUnLinkModule(NSModule module, uint32_t options) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_5,__IPHONE_NA,__IPHONE_NA); +#define NSUNLINKMODULE_OPTION_NONE 0x0 +#define NSUNLINKMODULE_OPTION_KEEP_MEMORY_MAPPED 0x1 +#define NSUNLINKMODULE_OPTION_RESET_LAZY_REFERENCES 0x2 + +/* symbol API */ +typedef struct __NSSymbol* NSSymbol; +extern bool NSIsSymbolNameDefined(const char* symbolName) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_4,__IPHONE_NA,__IPHONE_NA); +extern bool NSIsSymbolNameDefinedWithHint(const char* symbolName, const char* libraryNameHint) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_4,__IPHONE_NA,__IPHONE_NA); +extern bool NSIsSymbolNameDefinedInImage(const struct mach_header* image, const char* symbolName) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_4,__IPHONE_NA,__IPHONE_NA); +extern NSSymbol NSLookupAndBindSymbol(const char* symbolName) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_4,__IPHONE_NA,__IPHONE_NA); +extern NSSymbol NSLookupAndBindSymbolWithHint(const char* symbolName, const char* libraryNameHint) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_4,__IPHONE_NA,__IPHONE_NA); +extern NSSymbol NSLookupSymbolInModule(NSModule module, const char* symbolName) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_5,__IPHONE_NA,__IPHONE_NA); +extern NSSymbol NSLookupSymbolInImage(const struct mach_header* image, const char* symbolName, uint32_t options) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_5,__IPHONE_NA,__IPHONE_NA); +#define NSLOOKUPSYMBOLINIMAGE_OPTION_BIND 0x0 +#define NSLOOKUPSYMBOLINIMAGE_OPTION_BIND_NOW 0x1 +#define NSLOOKUPSYMBOLINIMAGE_OPTION_BIND_FULLY 0x2 +#define NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR 0x4 +extern const char* NSNameOfSymbol(NSSymbol symbol) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_5,__IPHONE_NA,__IPHONE_NA); +extern void * NSAddressOfSymbol(NSSymbol symbol) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_5,__IPHONE_NA,__IPHONE_NA); +extern NSModule NSModuleForSymbol(NSSymbol symbol) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_5,__IPHONE_NA,__IPHONE_NA); + +/* error handling API */ +typedef enum { + NSLinkEditFileAccessError, + NSLinkEditFileFormatError, + NSLinkEditMachResourceError, + NSLinkEditUnixResourceError, + NSLinkEditOtherError, + NSLinkEditWarningError, + NSLinkEditMultiplyDefinedError, + NSLinkEditUndefinedError +} NSLinkEditErrors; + +/* + * For the NSLinkEditErrors value NSLinkEditOtherError these are the values + * passed to the link edit error handler as the errorNumber (what would be an + * errno value for NSLinkEditUnixResourceError or a kern_return_t value for + * NSLinkEditMachResourceError). + */ +typedef enum { + NSOtherErrorRelocation, + NSOtherErrorLazyBind, + NSOtherErrorIndrLoop, + NSOtherErrorLazyInit, + NSOtherErrorInvalidArgs +} NSOtherErrorNumbers; + +extern void NSLinkEditError(NSLinkEditErrors *c, int *errorNumber, const char** fileName, const char** errorString) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_5,__IPHONE_NA,__IPHONE_NA); + +typedef struct { + void (*undefined)(const char* symbolName); + NSModule (*multiple)(NSSymbol s, NSModule oldModule, NSModule newModule); + void (*linkEdit)(NSLinkEditErrors errorClass, int errorNumber, + const char* fileName, const char* errorString); +} NSLinkEditErrorHandlers; + +extern void NSInstallLinkEditErrorHandlers(const NSLinkEditErrorHandlers *handlers) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_5,__IPHONE_NA,__IPHONE_NA); + +extern bool NSAddLibrary(const char* pathName) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_4,__IPHONE_NA,__IPHONE_NA); +extern bool NSAddLibraryWithSearching(const char* pathName) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_4,__IPHONE_NA,__IPHONE_NA); +extern const struct mach_header* NSAddImage(const char* image_name, uint32_t options) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_5,__IPHONE_NA,__IPHONE_NA); +#define NSADDIMAGE_OPTION_NONE 0x0 +#define NSADDIMAGE_OPTION_RETURN_ON_ERROR 0x1 +#define NSADDIMAGE_OPTION_WITH_SEARCHING 0x2 +#define NSADDIMAGE_OPTION_RETURN_ONLY_IF_LOADED 0x4 +#define NSADDIMAGE_OPTION_MATCH_FILENAME_BY_INSTALLNAME 0x8 + +extern bool _dyld_present(void) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_5,__IPHONE_NA,__IPHONE_NA); +extern bool _dyld_launched_prebound(void) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_5,__IPHONE_NA,__IPHONE_NA); +extern bool _dyld_all_twolevel_modules_prebound(void) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_3,__MAC_10_5,__IPHONE_NA,__IPHONE_NA); +extern void _dyld_bind_objc_module(const void* objc_module) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_5,__IPHONE_NA,__IPHONE_NA); +extern bool _dyld_bind_fully_image_containing_address(const void* address) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_5,__IPHONE_NA,__IPHONE_NA); +extern bool _dyld_image_containing_address(const void* address) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_3,__MAC_10_5,__IPHONE_NA,__IPHONE_NA); +extern void _dyld_lookup_and_bind(const char* symbol_name, void **address, NSModule* module) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_4,__IPHONE_NA,__IPHONE_NA); +extern void _dyld_lookup_and_bind_with_hint(const char* symbol_name, const char* library_name_hint, void** address, NSModule* module) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_4,__IPHONE_NA,__IPHONE_NA); +extern void _dyld_lookup_and_bind_fully(const char* symbol_name, void** address, NSModule* module) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_5,__IPHONE_NA,__IPHONE_NA); + +extern const struct mach_header* _dyld_get_image_header_containing_address(const void* address) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_3,__MAC_10_5,__IPHONE_NA,__IPHONE_NA); + + +#if __cplusplus +} +#endif + +#endif /* _MACH_O_DYLD_H_ */ diff --git a/src/dyld-apple/include/mach-o/dyld_gdb.h b/src/dyld-apple/include/mach-o/dyld_gdb.h new file mode 100644 index 000000000..eaadf6488 --- /dev/null +++ b/src/dyld-apple/include/mach-o/dyld_gdb.h @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2003 Apple Computer, 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@ + */ +#ifndef _DYLD_GDB_ +#define _DYLD_GDB_ + +/* + * For Mac OS X 10.4 or later, use the interface in mach-o/dylib_images.h + */ +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Prior to Mac OS 10.4, this is the interface gdb used to discover the mach-o images loaded in a process + */ +#if __i386__ +/* + * gdb_dyld_version is the version of gdb interface that dyld is currently + * exporting. For the interface described in this header file gdb_dyld_version + * is 2. As the gdb/dyld interface changes this number will be incremented and + * comments will be added as to what are the are changes for the various + * versions. + */ +extern unsigned int gdb_dyld_version; + +/* + * gdb_dyld_state_changed is the internal dyld routine called by dyld to notify + * gdb that the state of the data structures has changed. gdb is expected to + * put a break point on this routine and re-read the internal dyld data + * structures below when this break point is hit. + */ +extern void gdb_dyld_state_changed(void); + +/* + * gdb looks directly at parts of two of dyld's internal data structures. The + * list of object file images and the list of library images. The parts of + * these structures that gdb looks at will not change unless the value of + * gdb_dyld_version changes. The size of these structures and the other fields + * that gdb does not look at may change. + * + * struct object_images { + * struct object_image images[NOBJECT_IMAGES]; + * unsigned long nimages; + * struct object_images *next_images; + * ... + * }; + * + * struct library_images { + * struct library_image images[NLIBRARY_IMAGES]; + * unsigned long nimages; + * struct library_images *next_images; + * ... + * }; + * + * Both the object_image structure and the library_image structure + * start with a structure containing the following fields: + * + * struct image { + * char *physical_name; physical image name (file name) + * unsigned long vmaddr_slide; the slide from the staticly linked address + * struct mach_header *mh; address of the mach header of the image + * unsigned long valid; TRUE if this is struct is valid + * char *name; image name for reporting errors + * ... + * }; + * + * In gdb_dyld_version 1 the first field was "name". In gdb_dyld_version 2 the + * first field was changed to "physical_name" and a new fifth field "name" was + * added. These two fields are set to the same values except in the case of + * zero-link. In zero-link the NSLinkModule() option + * NSLINKMODULE_OPTION_TRAILING_PHYS_NAME is used and then the physical_name is + * the file name of the module zero-link loaded that is part of the logical + * image "name". + */ + +/* object_images is the global object_images structure */ + +/* the number of gdb_object_image structures present per bucket */ +extern unsigned int gdb_nobject_images; + +/* the size of each gdb_object_image structure */ +extern unsigned int gdb_object_image_size; + +/* library_images is the global library_images structure */ + +/* the number of gdb_library_image structures present per bucket */ +extern unsigned int gdb_nlibrary_images; + +/* the size of each gdb_library_image structure */ +extern unsigned int gdb_library_image_size; + +#endif + + + +#ifdef __cplusplus +} +#endif + +#endif /* _DYLD_GDB_ */ diff --git a/src/dyld-apple/include/mach-o/dyld_images.h b/src/dyld-apple/include/mach-o/dyld_images.h new file mode 100644 index 000000000..25e4afc60 --- /dev/null +++ b/src/dyld-apple/include/mach-o/dyld_images.h @@ -0,0 +1,169 @@ +/* + * Copyright (c) 2006-2010 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@ + */ +#ifndef _DYLD_IMAGES_ +#define _DYLD_IMAGES_ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + + + +/* + * Beginning in Mac OS X 10.4, this is how gdb discovers which mach-o images are loaded in a process. + * + * gdb looks for the symbol "_dyld_all_image_infos" in dyld. It contains the fields below. + * + * For a snashot of what images are currently loaded, the infoArray fields contain a pointer + * to an array of all images. If infoArray is NULL, it means it is being modified, come back later. + * + * To be notified of changes, gdb sets a break point on the address pointed to by the notificationn + * field. The function it points to is called by dyld with an array of information about what images + * have been added (dyld_image_adding) or are about to be removed (dyld_image_removing). + * + * The notification is called after infoArray is updated. This means that if gdb attaches to a process + * and infoArray is NULL, gdb can set a break point on notification and let the proccess continue to + * run until the break point. Then gdb can inspect the full infoArray. + * + * The dyldVersion field always points to a C string that contains the dyld version. For instance, + * in dyld-127.3, dyldVersion would contain a pointer to "127.3". + * + * The errorMessage and terminationFlags fields are normally zero. If dyld terminates a process + * (for instance because a required dylib or symbol is missing), then the errorMessage field will + * be set to point to a C string message buffer containing the reason dyld terminate the process. + * The low bit of the terminationFlags will be set if dyld terminated the process before any user + * code ran, in which case there is no need for the crash log to contain the backtrace. + * + * When dyld terminates a process because some required dylib or symbol cannot be bound, in + * addition to the errorMessage field, it now sets the errorKind field and the corresponding + * fields: errorClientOfDylibPath, errorTargetDylibPath, errorSymbol. + * + */ + +enum dyld_image_mode { dyld_image_adding=0, dyld_image_removing=1, dyld_image_info_change=2 }; + +struct dyld_image_info { + const struct mach_header* imageLoadAddress; /* base address image is mapped into */ + const char* imageFilePath; /* path dyld used to load the image */ + uintptr_t imageFileModDate; /* time_t of image file */ + /* if stat().st_mtime of imageFilePath does not match imageFileModDate, */ + /* then file has been modified since dyld loaded it */ +}; + +struct dyld_uuid_info { + const struct mach_header* imageLoadAddress; /* base address image is mapped into */ + uuid_t imageUUID; /* UUID of image */ +}; + +typedef void (*dyld_image_notifier)(enum dyld_image_mode mode, uint32_t infoCount, const struct dyld_image_info info[]); + +/* for use in dyld_all_image_infos.errorKind field */ +enum { dyld_error_kind_none=0, + dyld_error_kind_dylib_missing=1, + dyld_error_kind_dylib_wrong_arch=2, + dyld_error_kind_dylib_version=3, + dyld_error_kind_symbol_missing=4 + }; + +/* internal limit */ +#define DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT 8 + +struct dyld_all_image_infos { + uint32_t version; /* 1 in Mac OS X 10.4 and 10.5 */ + uint32_t infoArrayCount; + const struct dyld_image_info* infoArray; + dyld_image_notifier notification; + bool processDetachedFromSharedRegion; + /* the following fields are only in version 2 (Mac OS X 10.6, iPhoneOS 2.0) and later */ + bool libSystemInitialized; + const struct mach_header* dyldImageLoadAddress; + /* the following field is only in version 3 (Mac OS X 10.6, iPhoneOS 3.0) and later */ + void* jitInfo; + /* the following fields are only in version 5 (Mac OS X 10.6, iPhoneOS 3.0) and later */ + const char* dyldVersion; + const char* errorMessage; + uintptr_t terminationFlags; + /* the following field is only in version 6 (Mac OS X 10.6, iPhoneOS 3.1) and later */ + void* coreSymbolicationShmPage; + /* the following field is only in version 7 (Mac OS X 10.6, iPhoneOS 3.1) and later */ + uintptr_t systemOrderFlag; + /* the following field is only in version 8 (Mac OS X 10.7, iPhoneOS 3.1) and later */ + uintptr_t uuidArrayCount; + const struct dyld_uuid_info* uuidArray; /* only images not in dyld shared cache */ + /* the following field is only in version 9 (Mac OS X 10.7, iOS 4.0) and later */ + struct dyld_all_image_infos* dyldAllImageInfosAddress; + /* the following field is only in version 10 (Mac OS X 10.7, iOS 4.2) and later */ + uintptr_t initialImageCount; + /* the following field is only in version 11 (Mac OS X 10.7, iOS 4.2) and later */ + uintptr_t errorKind; + const char* errorClientOfDylibPath; + const char* errorTargetDylibPath; + const char* errorSymbol; + /* the following field is only in version 12 (Mac OS X 10.7, iOS 4.3) and later */ + uintptr_t sharedCacheSlide; + /* the following field is only in version 13 (Mac OS X 10.9, iOS 7.0) and later */ + uint8_t sharedCacheUUID[16]; + /* the following field is only in version 15 (macOS 10.12, iOS 10.0) and later */ + uintptr_t sharedCacheBaseAddress; + uint64_t infoArrayChangeTimestamp; + const char* dyldPath; + mach_port_t notifyPorts[DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT]; +#if __LP64__ + uintptr_t reserved[13-(DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT/2)]; +#else + uintptr_t reserved[12-DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT]; +#endif +}; + + +/* + * Beginning in Mac OS X 10.5, this is how gdb discovers where the shared cache is in a process. + * Images that are in the shared cache have their segments rearranged, so when using imageFilePath + * to load the file from disk, you have to know to adjust addresses based on how their segment + * was rearranged. + * + * gdb looks for the symbol "_dyld_shared_region_ranges" in dyld. + * + * It contains information the count of shared regions used by the process. The count is + * the number of start/length pairs. + */ +struct dyld_shared_cache_ranges { + uintptr_t sharedRegionsCount; /* how many ranges follow */ + struct { + uintptr_t start; + uintptr_t length; + } ranges[4]; /* max regions */ +}; +extern struct dyld_shared_cache_ranges dyld_shared_cache_ranges; + + + +#ifdef __cplusplus +} +#endif + +#endif /* _DYLD_IMAGES_ */ diff --git a/src/dyld-apple/include/mach-o/dyld_priv.h b/src/dyld-apple/include/mach-o/dyld_priv.h new file mode 100644 index 000000000..db108fd6e --- /dev/null +++ b/src/dyld-apple/include/mach-o/dyld_priv.h @@ -0,0 +1,425 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2003-2010 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@ + */ +#ifndef _MACH_O_DYLD_PRIV_H_ +#define _MACH_O_DYLD_PRIV_H_ + +#include +#include +#include +#include + +#if __cplusplus +extern "C" { +#endif /* __cplusplus */ + + + +// +// private interface between libSystem.dylib and dyld +// +extern int _dyld_func_lookup(const char* dyld_func_name, void **address); + +// +// private interface between libSystem.dylib and dyld +// +extern void _dyld_fork_child(); + + +// DEPRECATED +enum dyld_image_states +{ + dyld_image_state_mapped = 10, // No batch notification for this + dyld_image_state_dependents_mapped = 20, // Only batch notification for this + dyld_image_state_rebased = 30, + dyld_image_state_bound = 40, + dyld_image_state_dependents_initialized = 45, // Only single notification for this + dyld_image_state_initialized = 50, + dyld_image_state_terminated = 60 // Only single notification for this +}; + +// DEPRECATED +typedef const char* (*dyld_image_state_change_handler)(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info info[]); + + + +typedef void (*_dyld_objc_notify_mapped)(unsigned count, const char* const paths[], const struct mach_header* const mh[]); +typedef void (*_dyld_objc_notify_init)(const char* path, const struct mach_header* mh); +typedef void (*_dyld_objc_notify_unmapped)(const char* path, const struct mach_header* mh); + + +// +// Note: only for use by objc runtime +// Register handlers to be called when objc images are mapped, unmapped, and initialized. +// Dyld will call back the "mapped" function with an array of images that contain an objc-image-info section. +// Those images that are dylibs will have the ref-counts automatically bumped, so objc will no longer need to +// call dlopen() on them to keep them from being unloaded. During the call to _dyld_objc_notify_register(), +// dyld will call the "mapped" function with already loaded objc images. During any later dlopen() call, +// dyld will also call the "mapped" function. Dyld will call the "init" function when dyld would be called +// initializers in that image. This is when objc calls any +load methods in that image. +// +void _dyld_objc_notify_register(_dyld_objc_notify_mapped mapped, + _dyld_objc_notify_init init, + _dyld_objc_notify_unmapped unmapped); + + + +// +// Possible thread-local variable state changes for which you can register to be notified +// +enum dyld_tlv_states { + dyld_tlv_state_allocated = 10, // TLV range newly allocated + dyld_tlv_state_deallocated = 20 // TLV range about to be deallocated +}; + +// +// Info about thread-local variable storage. +// +typedef struct { + size_t info_size; // sizeof(dyld_tlv_info) + void * tlv_addr; // Base address of TLV storage + size_t tlv_size; // Byte size of TLV storage +} dyld_tlv_info; + +#if __BLOCKS__ + +// +// Callback that notes changes to thread-local variable storage. +// +typedef void (^dyld_tlv_state_change_handler)(enum dyld_tlv_states state, const dyld_tlv_info *info); + +// +// Register a handler to be called when a thread adds or removes storage for thread-local variables. +// The registered handler will only be called from and on behalf of the thread that owns the storage. +// The registered handler will NOT be called for any storage that was +// already allocated before dyld_register_tlv_state_change_handler() was +// called. Use dyld_enumerate_tlv_storage() to get that information. +// Exists in Mac OS X 10.7 and later +// +extern void +dyld_register_tlv_state_change_handler(enum dyld_tlv_states state, dyld_tlv_state_change_handler handler); + +// +// Enumerate the current thread-local variable storage allocated for the current thread. +// Exists in Mac OS X 10.7 and later +// +extern void +dyld_enumerate_tlv_storage(dyld_tlv_state_change_handler handler); + +#endif + + +// +// get slide for a given loaded mach_header +// Mac OS X 10.6 and later +// +extern intptr_t _dyld_get_image_slide(const struct mach_header* mh); + + +// +// get pointer to this process's dyld_all_image_infos +// Exists in Mac OS X 10.4 and later through _dyld_func_lookup() +// Exists in Mac OS X 10.6 and later through libSystem.dylib +// +const struct dyld_all_image_infos* _dyld_get_all_image_infos() __attribute__((deprecated)); + + + +struct dyld_unwind_sections +{ + const struct mach_header* mh; + const void* dwarf_section; + uintptr_t dwarf_section_length; + const void* compact_unwind_section; + uintptr_t compact_unwind_section_length; +}; + + +// +// Returns true iff some loaded mach-o image contains "addr". +// info->mh mach header of image containing addr +// info->dwarf_section pointer to start of __TEXT/__eh_frame section +// info->dwarf_section_length length of __TEXT/__eh_frame section +// info->compact_unwind_section pointer to start of __TEXT/__unwind_info section +// info->compact_unwind_section_length length of __TEXT/__unwind_info section +// +// Exists in Mac OS X 10.6 and later +extern bool _dyld_find_unwind_sections(void* addr, struct dyld_unwind_sections* info); + + +// +// This is an optimized form of dladdr() that only returns the dli_fname field. +// +// Exists in Mac OS X 10.6 and later +extern const char* dyld_image_path_containing_address(const void* addr); + + +// +// This is an optimized form of dladdr() that only returns the dli_fbase field. +// Return NULL, if address is not in any image tracked by dyld. +// +// Exists in Mac OS X 10.11 and later +extern const struct mach_header* dyld_image_header_containing_address(const void* addr); + + + +// Convienence constants for return values from dyld_get_sdk_version() and friends. +#define DYLD_MACOSX_VERSION_10_4 0x000A0400 +#define DYLD_MACOSX_VERSION_10_5 0x000A0500 +#define DYLD_MACOSX_VERSION_10_6 0x000A0600 +#define DYLD_MACOSX_VERSION_10_7 0x000A0700 +#define DYLD_MACOSX_VERSION_10_8 0x000A0800 +#define DYLD_MACOSX_VERSION_10_9 0x000A0900 +#define DYLD_MACOSX_VERSION_10_10 0x000A0A00 +#define DYLD_MACOSX_VERSION_10_11 0x000A0B00 +#define DYLD_MACOSX_VERSION_10_12 0x000A0C00 + +#define DYLD_IOS_VERSION_2_0 0x00020000 +#define DYLD_IOS_VERSION_2_1 0x00020100 +#define DYLD_IOS_VERSION_2_2 0x00020200 +#define DYLD_IOS_VERSION_3_0 0x00030000 +#define DYLD_IOS_VERSION_3_1 0x00030100 +#define DYLD_IOS_VERSION_3_2 0x00030200 +#define DYLD_IOS_VERSION_4_0 0x00040000 +#define DYLD_IOS_VERSION_4_1 0x00040100 +#define DYLD_IOS_VERSION_4_2 0x00040200 +#define DYLD_IOS_VERSION_4_3 0x00040300 +#define DYLD_IOS_VERSION_5_0 0x00050000 +#define DYLD_IOS_VERSION_5_1 0x00050100 +#define DYLD_IOS_VERSION_6_0 0x00060000 +#define DYLD_IOS_VERSION_6_1 0x00060100 +#define DYLD_IOS_VERSION_7_0 0x00070000 +#define DYLD_IOS_VERSION_7_1 0x00070100 +#define DYLD_IOS_VERSION_8_0 0x00080000 +#define DYLD_IOS_VERSION_8_1 0x00080100 +#define DYLD_IOS_VERSION_8_2 0x00080200 +#define DYLD_IOS_VERSION_8_3 0x00080300 +#define DYLD_IOS_VERSION_8_4 0x00080400 +#define DYLD_IOS_VERSION_9_0 0x00090000 +#define DYLD_IOS_VERSION_9_1 0x00090100 +#define DYLD_IOS_VERSION_9_2 0x00090200 +#define DYLD_IOS_VERSION_9_3 0x00090300 +#define DYLD_IOS_VERSION_10_0 0x000A0000 + + +#define DYLD_WATCHOS_VERSION_1_0 0x00010000 +#define DYLD_WATCHOS_VERSION_2_0 0x00020000 +#define DYLD_WATCHOS_VERSION_2_1 0x00020100 +#define DYLD_WATCHOS_VERSION_2_2 0x00020200 +#define DYLD_WATCHOS_VERSION_3_0 0x00030000 + + + +// +// This finds the SDK version a binary was built against. +// Returns zero on error, or if SDK version could not be determined. +// +// Exists in Mac OS X 10.8 and later +// Exists in iOS 6.0 and later +extern uint32_t dyld_get_sdk_version(const struct mach_header* mh); + + +// +// This finds the SDK version that the main executable was built against. +// Returns zero on error, or if SDK version could not be determined. +// +// Note on WatchOS, this returns the equivalent iOS SDK version number +// (i.e an app built against WatchOS 2.0 SDK returne 9.0). To see the +// platform specific sdk version use dyld_get_program_sdk_watch_os_version(). +// +// Exists in Mac OS X 10.8 and later +// Exists in iOS 6.0 and later +extern uint32_t dyld_get_program_sdk_version(); + + +// Watch OS only. +// This finds the Watch OS SDK version that the main executable was built against. +// Exists in Watch OS 2.0 and later +extern uint32_t dyld_get_program_sdk_watch_os_version(); // __WATCHOS_AVAILABLE(2.0); + + +// Watch OS only. +// This finds the Watch min OS version that the main executable was built to run on. +// Note: dyld_get_program_min_os_version() returns the iOS equivalent (e.g. 9.0) +// whereas this returns the raw watchOS version (e.g. 2.0). +// Exists in Watch OS 3.0 and later +extern uint32_t dyld_get_program_min_watch_os_version(); // __WATCHOS_AVAILABLE(3.0); + + +// +// This finds the min OS version a binary was built to run on. +// Returns zero on error, or if no min OS recorded in binary. +// +// Exists in Mac OS X 10.8 and later +// Exists in iOS 6.0 and later +extern uint32_t dyld_get_min_os_version(const struct mach_header* mh); + + +// +// This finds the min OS version the main executable was built to run on. +// Returns zero on error, or if no min OS recorded in binary. +// +// Exists in Mac OS X 10.8 and later +// Exists in iOS 6.0 and later +extern uint32_t dyld_get_program_min_os_version(); + + + + +// +// Returns if any OS dylib has overridden its copy in the shared cache +// +// Exists in iPhoneOS 3.1 and later +// Exists in Mac OS X 10.10 and later +extern bool dyld_shared_cache_some_image_overridden(); + + + +// +// Returns if the process is setuid or is code signed with entitlements. +// +// Exists in Mac OS X 10.9 and later +extern bool dyld_process_is_restricted(); + + + +// +// Returns path used by dyld for standard dyld shared cache file for the current arch. +// +// Exists in Mac OS X 10.11 and later +extern const char* dyld_shared_cache_file_path(); + + + +// +// for OpenGL to tell dyld it is ok to deallocate a memory based image when done. +// +// Exists in Mac OS X 10.9 and later +#define NSLINKMODULE_OPTION_CAN_UNLOAD 0x20 + + +// +// Update all bindings on specified image. +// Looks for uses of 'replacement' and changes it to 'replacee'. +// NOTE: this is less safe than using static interposing via DYLD_INSERT_LIBRARIES +// because the running program may have already copy the pointer values to other +// locations that dyld does not know about. +// +struct dyld_interpose_tuple { + const void* replacement; + const void* replacee; +}; +extern void dyld_dynamic_interpose(const struct mach_header* mh, const struct dyld_interpose_tuple array[], size_t count); + + +struct dyld_shared_cache_dylib_text_info { + uint64_t version; // current version 1 + // following fields all exist in version 1 + uint64_t loadAddressUnslid; + uint64_t textSegmentSize; + uuid_t dylibUuid; + const char* path; // pointer invalid at end of iterations +}; +typedef struct dyld_shared_cache_dylib_text_info dyld_shared_cache_dylib_text_info; + +// +// Given the UUID of a dyld shared cache file, this function will attempt to locate the cache +// file and if found iterate all images, returning info about each one. Returns 0 on success. +// +// Exists in Mac OS X 10.11 and later +// iOS 9.0 and later +extern int dyld_shared_cache_iterate_text(const uuid_t cacheUuid, void (^callback)(const dyld_shared_cache_dylib_text_info* info)); + + +// +// Given the UUID of a dyld shared cache file, and a NULL terminated array of extra directory paths to search, +// this function will scan the standard and extra directories looking for a cache file that matches the UUID +// and if found iterate all images, returning info about each one. Returns 0 on success. +// +// Exists in Mac OS X 10.12 and later +// iOS 10.0 and later +extern int dyld_shared_cache_find_iterate_text(const uuid_t cacheUuid, const char* extraSearchDirs[], void (^callback)(const dyld_shared_cache_dylib_text_info* info)); + + +// +// Returns if the specified address range is in a dyld owned memory +// that is mapped read-only and will never be unloaded. +// +// Exists in Mac OS X 10.12 and later +// iOS 10.0 and later +extern bool _dyld_is_memory_immutable(const void* addr, size_t length); + + +// +// Finds the UUID (from LC_UUID load command) of given image. +// Returns false if LC_UUID is missing or mach_header is malformed. +// +// Exists in Mac OS X 10.12 and later +// Exists in iOS 10.0 and later +extern bool _dyld_get_image_uuid(const struct mach_header* mh, uuid_t uuid); + + +// +// Gets the UUID of the dyld shared cache in the current process. +// Returns false if there is no dyld shared cache in use by the processes. +// +// Exists in Mac OS X 10.12 and later +// Exists in iOS 10.0 and later +extern bool _dyld_get_shared_cache_uuid(uuid_t uuid); + + + +// +// When dyld must terminate a process because of a required dependent dylib +// could not be loaded or a symbol is missing, dyld calls abort_with_reason() +// using one of the following error codes. +// +#define DYLD_EXIT_REASON_DYLIB_MISSING 1 +#define DYLD_EXIT_REASON_DYLIB_WRONG_ARCH 2 +#define DYLD_EXIT_REASON_DYLIB_WRONG_VERSION 3 +#define DYLD_EXIT_REASON_SYMBOL_MISSING 4 +#define DYLD_EXIT_REASON_CODE_SIGNATURE 5 +#define DYLD_EXIT_REASON_FILE_SYSTEM_SANDBOX 6 +#define DYLD_EXIT_REASON_MALFORMED_MACHO 7 +#define DYLD_EXIT_REASON_OTHER 9 + +// +// When it has more information about the termination, dyld will use abort_with_payload(). +// The payload is a dyld_abort_payload structure. The fixed fields are offsets into the +// payload for the corresponding string. If the offset is zero, that string is not available. +// +struct dyld_abort_payload { + uint32_t version; // first version is 1 + uint32_t flags; // 0x00000001 means dyld terminated at launch, backtrace not useful + uint32_t targetDylibPathOffset; // offset in payload of path string to dylib that could not be loaded + uint32_t clientPathOffset; // offset in payload of path string to image requesting dylib + uint32_t symbolOffset; // offset in payload of symbol string that could not be found + // string data +}; +typedef struct dyld_abort_payload dyld_abort_payload; + +#if __cplusplus +} +#endif /* __cplusplus */ + +#endif /* _MACH_O_DYLD_PRIV_H_ */ diff --git a/src/dyld-apple/include/mach-o/dyld_process_info.h b/src/dyld-apple/include/mach-o/dyld_process_info.h new file mode 100644 index 000000000..67bf25d9f --- /dev/null +++ b/src/dyld-apple/include/mach-o/dyld_process_info.h @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2016 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@ + */ +#ifndef _DYLD_PROCESS_INFO_ +#define _DYLD_PROCESS_INFO_ + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +// +// Beginning in iOS 10.0 and Mac OS X 10.12, this is how lldb figures out mach-o binaries are in a process: +// +// When attaching to an existing process, lldb uses _dyld_process_info_create() to get the current list of images +// in a process, then falls into the start up case. +// +// When starting a process, lldb starts the process suspended, finds the "_dyld_debugger_notification" symbol in +// dyld, sets a break point on it, then resumes the process. Dyld will call _dyld_debugger_notification() with +// a list of images that were just added or removed from the process. Dyld calls this function before running +// any initializers in the image, so the debugger will have a chance to set break points in the image. +// + +enum dyld_notify_mode { dyld_notify_adding=0, dyld_notify_removing=1, dyld_notify_remove_all=2 }; + +void _dyld_debugger_notification(enum dyld_notify_mode, unsigned long count, uint64_t machHeaders[]); + + +struct dyld_process_cache_info { + uuid_t cacheUUID; // UUID of cache used by process + uint64_t cacheBaseAddress; // load address of dyld shared cache + bool noCache; // process is running without a dyld cache + bool privateCache; // process is using a private copy of its dyld cache +}; +typedef struct dyld_process_cache_info dyld_process_cache_info; + +enum { + dyld_process_state_not_started = 0x00, // process is suspended, dyld has not started running yet + dyld_process_state_dyld_initialized = 0x10, // dyld has initialzed itself + dyld_process_state_terminated_before_inits = 0x20, // process was terminated due missing library or symbol before it got to main() + dyld_process_state_libSystem_initialized = 0x30, // dyld has run libSystem's initializer + dyld_process_state_running_initializers = 0x40, // dyld is running other initializers + dyld_process_state_program_running = 0x50, // dyld has finished and jumped into main() + dyld_process_state_dyld_terminated = 0x60 // process was terminated by dyld post-main (e.g. bad lazying binding info) +}; + +struct dyld_process_state_info { + uint64_t timestamp; // mach_absolute_time of last time dyld change to image list + uint32_t imageCount; // number of images currently loaded into process + uint32_t initialImageCount; // number of images statically loaded into process (before any dlopen() calls) + uint8_t dyldState; // one of dyld_process_state_* values +}; +typedef struct dyld_process_state_info dyld_process_state_info; + + +typedef const struct dyld_process_info_base* dyld_process_info; + +// +// Generate a dyld_process_info object for specified task. +// +// The timestamp parameter is an optimization to not spend the time to gather all the image information +// if the process image list has not changed since the last call. If timestamp is zero, this function +// always gathers the full process info. If timestamp is non-zero, this function will check if the target +// task's image list has changed since that time. If is has not changed, the function returns NULL and +// kern_return_t is KERN_SUCCESS. If it has changed, the function gathers the full image info. +// The kernelError parameter can be NULL for clients that don't care why it failed. +// +extern dyld_process_info _dyld_process_info_create(task_t task, uint64_t timestamp, kern_return_t* kernelError); + +// retain/release dyld_process_info for specified task +extern void _dyld_process_info_release(dyld_process_info info); +extern void _dyld_process_info_retain(dyld_process_info info); + +// fill in struct with basic info about dyld in the process +extern void _dyld_process_info_get_state(dyld_process_info info, dyld_process_state_info* stateInfo); + +// fill in struct with info about dyld cache in use by process +extern void _dyld_process_info_get_cache(dyld_process_info info, dyld_process_cache_info* cacheInfo); + +// iterate all images in process +extern void _dyld_process_info_for_each_image(dyld_process_info info, void (^callback)(uint64_t machHeaderAddress, const uuid_t uuid, const char* path)); + +// iterate all segments in an image +extern void _dyld_process_info_for_each_segment(dyld_process_info info, uint64_t machHeaderAddress, void (^callback)(uint64_t segmentAddress, uint64_t segmentSize, const char* segmentName)); + + + + +typedef const struct dyld_process_info_notify_base* dyld_process_info_notify; + +// +// Request notifications if image list changes in target process. Each time a load or unload happens in the target taks, +// the notify block will be called in this process. If the process exits, the notifyExit block will be called. +// If the notifications cannot be set up, this function will return NULL, and the reason in the kernError parameter. +// The kernelError parameter can be NULL for clients that don't care why it failed. +// If you want to stop receiving notifications, call _dyld_process_info_notify_release(). +// +extern dyld_process_info_notify _dyld_process_info_notify(task_t task, dispatch_queue_t queue, + void (^notify)(bool unload, uint64_t timestamp, uint64_t machHeader, const uuid_t uuid, const char* path), + void (^notifyExit)(), + kern_return_t* kernelError); + +// stop notifications and invalid dyld_process_info_notify object +extern void _dyld_process_info_notify_release(dyld_process_info_notify object); +extern void _dyld_process_info_notify_retain(dyld_process_info_notify object); + + +#ifdef __cplusplus +} +#endif + +#endif /* _DYLD_PROCESS_INFO_ */ diff --git a/src/dyld-apple/include/objc-shared-cache.h b/src/dyld-apple/include/objc-shared-cache.h new file mode 100644 index 000000000..ef5c109ea --- /dev/null +++ b/src/dyld-apple/include/objc-shared-cache.h @@ -0,0 +1,1483 @@ +/* + * Copyright (c) 2008 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@ + */ + +/* +Portions derived from: + +-------------------------------------------------------------------- +lookup8.c, by Bob Jenkins, January 4 1997, Public Domain. +hash(), hash2(), hash3, and mix() are externally useful functions. +Routines to test the hash are included if SELF_TEST is defined. +You can use this free for any purpose. It has no warranty. +-------------------------------------------------------------------- + +------------------------------------------------------------------------------ +perfect.c: code to generate code for a hash for perfect hashing. +(c) Bob Jenkins, September 1996, December 1999 +You may use this code in any way you wish, and it is free. No warranty. +I hereby place this in the public domain. +Source is http://burtleburtle.net/bob/c/perfect.c +------------------------------------------------------------------------------ +*/ + +/* + * objc-selopt.h + * Interface between libobjc and dyld + * for selector uniquing in the dyld shared cache. + * + * When building the shared cache, dyld locates all selectors and selector + * references in the cached images. It builds a perfect hash table out of + * them and writes the table into the shared cache copy of libobjc. + * libobjc then uses that table as the builtin selector list. + * + * Versioning + * The table has a version number. dyld and objc can both ignore the table + * if the other used the wrong version number. + * + * Completeness + * Not all libraries are in the shared cache. Libraries that are in the + * shared cache and were optimized are specially marked. Libraries on + * disk never include those marks. + * + * Coherency + * Libraries optimized in the shared cache can be replaced by unoptimized + * copies from disk when loaded. The copy from disk is not marked and will + * be fixed up by libobjc. The shared cache copy is still mapped into the + * process, so the table can point to cstring data in that library's part + * of the shared cache without trouble. + * + * Atomicity + * dyld writes the table itself last. If dyld marks some metadata as + * updated but then fails to write a table for some reason, libobjc + * fixes up all metadata as if it were not marked. + */ + +#ifndef _OBJC_SELOPT_H +#define _OBJC_SELOPT_H + +/* + DO NOT INCLUDE ANY objc HEADERS HERE + dyld USES THIS FILE AND CANNOT SEE THEM +*/ +#include +#include +#ifdef SELOPT_WRITE +#include +#endif +/* + DO NOT INCLUDE ANY objc HEADERS HERE + dyld USES THIS FILE AND CANNOT SEE THEM +*/ + +#ifndef STATIC_ASSERT +# define STATIC_ASSERT(x) _STATIC_ASSERT2(x, __LINE__) +# define _STATIC_ASSERT2(x, line) _STATIC_ASSERT3(x, line) +# define _STATIC_ASSERT3(x, line) \ + typedef struct { \ + int _static_assert[(x) ? 0 : -1]; \ + } _static_assert_ ## line __attribute__((unavailable)) +#endif + +#define SELOPT_DEBUG 0 + +#define S32(x) x = little_endian ? OSSwapHostToLittleInt32(x) : OSSwapHostToBigInt32(x) +#define S64(x) x = little_endian ? OSSwapHostToLittleInt64(x) : OSSwapHostToBigInt64(x) + +namespace objc_opt { + +typedef int32_t objc_stringhash_offset_t; +typedef uint8_t objc_stringhash_check_t; + +static uint64_t lookup8( uint8_t *k, size_t length, uint64_t level); + +#ifdef SELOPT_WRITE + +// Perfect hash code is at the end of this file. + +struct perfect_hash { + uint32_t capacity; + uint32_t occupied; + uint32_t shift; + uint32_t mask; + uint64_t salt; + + uint32_t scramble[256]; + uint8_t *tab; // count == mask+1; free with delete[] + + perfect_hash() : tab(0) { } + + ~perfect_hash() { if (tab) delete[] tab; } +}; + +struct eqstr { + bool operator()(const char* s1, const char* s2) const { + return strcmp(s1, s2) == 0; + } +}; + +struct hashstr { + size_t operator()(const char *s) const { + return (size_t)lookup8((uint8_t *)s, strlen(s), 0); + } +}; + +// cstring => cstring's vmaddress +// (used for selector names and class names) +typedef std::unordered_map string_map; + +// protocol name => protocol vmaddress +typedef std::unordered_map protocol_map; + +// class name => (class vmaddress, header_info vmaddress) +typedef std::unordered_multimap, hashstr, eqstr> class_map; + +static perfect_hash make_perfect(const string_map& strings); + +#endif + + +// Precomputed perfect hash table of strings. +// Base class for precomputed selector table and class table. +// Edit objc-sel-table.s if you change this structure. +struct objc_stringhash_t { + uint32_t capacity; + uint32_t occupied; + uint32_t shift; + uint32_t mask; + uint32_t unused1; // was zero + uint32_t unused2; // alignment pad + uint64_t salt; + + uint32_t scramble[256]; + uint8_t tab[0]; /* tab[mask+1] (always power-of-2) */ + // uint8_t checkbytes[capacity]; /* check byte for each string */ + // int32_t offsets[capacity]; /* offsets from &capacity to cstrings */ + + objc_stringhash_check_t *checkbytes() { return (objc_stringhash_check_t *)&tab[mask+1]; } + const objc_stringhash_check_t *checkbytes() const { return (const objc_stringhash_check_t *)&tab[mask+1]; } + + objc_stringhash_offset_t *offsets() { return (objc_stringhash_offset_t *)&checkbytes()[capacity]; } + const objc_stringhash_offset_t *offsets() const { return (const objc_stringhash_offset_t *)&checkbytes()[capacity]; } + + uint32_t hash(const char *key, size_t keylen) const + { + uint64_t val = lookup8((uint8_t*)key, keylen, salt); + uint32_t index = (uint32_t)(val>>shift) ^ scramble[tab[val&mask]]; + return index; + } + + uint32_t hash(const char *key) const + { + return hash(key, strlen(key)); + } + + // The check bytes areused to reject strings that aren't in the table + // without paging in the table's cstring data. This checkbyte calculation + // catches 4785/4815 rejects when launching Safari; a perfect checkbyte + // would catch 4796/4815. + objc_stringhash_check_t checkbyte(const char *key, size_t keylen) const + { + return + ((key[0] & 0x7) << 5) + | + ((uint8_t)keylen & 0x1f); + } + + objc_stringhash_check_t checkbyte(const char *key) const + { + return checkbyte(key, strlen(key)); + } + + +#define INDEX_NOT_FOUND (~(uint32_t)0) + + uint32_t getIndex(const char *key) const + { + size_t keylen = strlen(key); + uint32_t h = hash(key, keylen); + + // Use check byte to reject without paging in the table's cstrings + objc_stringhash_check_t h_check = checkbytes()[h]; + objc_stringhash_check_t key_check = checkbyte(key, keylen); + bool check_fail = (h_check != key_check); +#if ! SELOPT_DEBUG + if (check_fail) return INDEX_NOT_FOUND; +#endif + + objc_stringhash_offset_t offset = offsets()[h]; + if (offset == 0) return INDEX_NOT_FOUND; + const char *result = (const char *)this + offset; + if (0 != strcmp(key, result)) return INDEX_NOT_FOUND; + +#if SELOPT_DEBUG + if (check_fail) abort(); +#endif + + return h; + } + +#ifdef SELOPT_WRITE + + size_t size() + { + return sizeof(objc_stringhash_t) + + mask+1 + + capacity * sizeof(objc_stringhash_check_t) + + capacity * sizeof(objc_stringhash_offset_t); + } + + void byteswap(bool little_endian) + { + // tab and checkbytes are arrays of bytes, no swap needed + for (uint32_t i = 0; i < 256; i++) { + S32(scramble[i]); + } + objc_stringhash_offset_t *o = offsets(); + for (uint32_t i = 0; i < capacity; i++) { + S32(o[i]); + } + + S32(capacity); + S32(occupied); + S32(shift); + S32(mask); + S64(salt); + } + + const char *write(uint64_t base, size_t remaining, string_map& strings) + { + if (sizeof(objc_stringhash_t) > remaining) { + return "selector section too small (metadata not optimized)"; + } + + if (strings.size() == 0) { + bzero(this, sizeof(objc_stringhash_t)); + return NULL; + } + + perfect_hash phash = make_perfect(strings); + if (phash.capacity == 0) { + return "perfect hash failed (metadata not optimized)"; + } + + // Set header + capacity = phash.capacity; + occupied = phash.occupied; + shift = phash.shift; + mask = phash.mask; + unused1 = 0; + unused2 = 0; + salt = phash.salt; + + if (size() > remaining) { + return "selector section too small (metadata not optimized)"; + } + + // Set hash data + for (uint32_t i = 0; i < 256; i++) { + scramble[i] = phash.scramble[i]; + } + for (uint32_t i = 0; i < phash.mask+1; i++) { + tab[i] = phash.tab[i]; + } + + // Set offsets to 0 + for (uint32_t i = 0; i < phash.capacity; i++) { + offsets()[i] = 0; + } + // Set checkbytes to 0 + for (uint32_t i = 0; i < phash.capacity; i++) { + checkbytes()[i] = 0; + } + + // Set real string offsets and checkbytes +# define SHIFT (64 - 8*sizeof(objc_stringhash_offset_t)) + string_map::const_iterator s; + for (s = strings.begin(); s != strings.end(); ++s) { + int64_t offset = s->second - base; + if ((offset<>SHIFT != offset) { + return "selector offset too big (metadata not optimized)"; + } + + uint32_t h = hash(s->first); + offsets()[h] = (objc_stringhash_offset_t)offset; + checkbytes()[h] = checkbyte(s->first); + } +# undef SHIFT + + return NULL; + } + +// SELOPT_WRITE +#endif +}; + + +// Precomputed selector table. +// Edit objc-sel-table.s if you change this structure. +struct objc_selopt_t : objc_stringhash_t { + const char *get(const char *key) const + { + uint32_t h = getIndex(key); + if (h == INDEX_NOT_FOUND) return NULL; + + return (const char *)this + offsets()[h]; + } +}; + +// Precomputed class list. +// Edit objc-sel-table.s if you change these structures. + +struct objc_classheader_t { + objc_stringhash_offset_t clsOffset; + objc_stringhash_offset_t hiOffset; + + // For duplicate class names: + // clsOffset = count<<1 | 1 + // duplicated classes are duplicateOffsets[hiOffset..hiOffset+count-1] + bool isDuplicate() const { return clsOffset & 1; } + uint32_t duplicateCount() const { return clsOffset >> 1; } + uint32_t duplicateIndex() const { return hiOffset; } +}; + + +struct objc_clsopt_t : objc_stringhash_t { + // ...objc_stringhash_t fields... + // objc_classheader_t classOffsets[capacity]; /* offsets from &capacity to class_t and header_info */ + // uint32_t duplicateCount; + // objc_classheader_t duplicateOffsets[duplicatedClasses]; + + objc_classheader_t *classOffsets() { return (objc_classheader_t *)&offsets()[capacity]; } + const objc_classheader_t *classOffsets() const { return (const objc_classheader_t *)&offsets()[capacity]; } + + uint32_t& duplicateCount() { return *(uint32_t *)&classOffsets()[capacity]; } + const uint32_t& duplicateCount() const { return *(const uint32_t *)&classOffsets()[capacity]; } + + objc_classheader_t *duplicateOffsets() { return (objc_classheader_t *)(&duplicateCount()+1); } + const objc_classheader_t *duplicateOffsets() const { return (const objc_classheader_t *)(&duplicateCount()+1); } + + // 0/NULL/NULL: not found + // 1/ptr/ptr: found exactly one + // n/NULL/NULL: found N - use getClassesAndHeaders() instead + uint32_t getClassAndHeader(const char *key, void*& cls, void*& hi) const + { + uint32_t h = getIndex(key); + if (h == INDEX_NOT_FOUND) { + cls = NULL; + hi = NULL; + return 0; + } + + const objc_classheader_t& clshi = classOffsets()[h]; + if (! clshi.isDuplicate()) { + // class appears in exactly one header + cls = (void *)((const char *)this + clshi.clsOffset); + hi = (void *)((const char *)this + clshi.hiOffset); + return 1; + } + else { + // class appears in more than one header - use getClassesAndHeaders + cls = NULL; + hi = NULL; + return clshi.duplicateCount(); + } + } + + void getClassesAndHeaders(const char *key, void **cls, void **hi) const + { + uint32_t h = getIndex(key); + if (h == INDEX_NOT_FOUND) return; + + const objc_classheader_t& clshi = classOffsets()[h]; + if (! clshi.isDuplicate()) { + // class appears in exactly one header + cls[0] = (void *)((const char *)this + clshi.clsOffset); + hi[0] = (void *)((const char *)this + clshi.hiOffset); + } + else { + // class appears in more than one header + uint32_t count = clshi.duplicateCount(); + const objc_classheader_t *list = + &duplicateOffsets()[clshi.duplicateIndex()]; + for (uint32_t i = 0; i < count; i++) { + cls[i] = (void *)((const char *)this + list[i].clsOffset); + hi[i] = (void *)((const char *)this + list[i].hiOffset); + } + } + } + +#ifdef SELOPT_WRITE + + size_t size() + { + return + objc_stringhash_t::size() + + capacity * sizeof(objc_classheader_t) + + sizeof(duplicateCount()) + + duplicateCount() * sizeof(objc_classheader_t); + } + + void byteswap(bool little_endian) + { + objc_classheader_t *o; + + o = classOffsets(); + for (uint32_t i = 0; i < capacity; i++) { + S32(o[i].clsOffset); + S32(o[i].hiOffset); + } + + o = duplicateOffsets(); + for (uint32_t i = 0; i < duplicateCount(); i++) { + S32(o[i].clsOffset); + S32(o[i].hiOffset); + } + + S32(duplicateCount()); + + objc_stringhash_t::byteswap(little_endian); + } + + const char *write(uint64_t base, size_t remaining, + string_map& strings, class_map& classes, bool verbose) + { + const char *err; + err = objc_stringhash_t::write(base, remaining, strings); + if (err) return err; + + if (size() > remaining) { + return "selector section too small (metadata not optimized)"; + } + + // Set class offsets to 0 + for (uint32_t i = 0; i < capacity; i++) { + classOffsets()[i].clsOffset = 0; + classOffsets()[i].hiOffset = 0; + } + + // Set real class offsets +# define SHIFT (64 - 8*sizeof(objc_stringhash_offset_t)) + class_map::const_iterator c; + for (c = classes.begin(); c != classes.end(); ++c) { + uint32_t h = getIndex(c->first); + if (h == INDEX_NOT_FOUND) { + return "class list busted (metadata not optimized)"; + } + + if (classOffsets()[h].clsOffset != 0) { + // already did this class + continue; + } + + uint32_t count = (uint32_t)classes.count(c->first); + if (count == 1) { + // only one class with this name + + int64_t coff = c->second.first - base; + int64_t hoff = c->second.second - base; + if ((coff<>SHIFT != coff) { + return "class offset too big (metadata not optimized)"; + } + if ((hoff<>SHIFT != hoff) { + return "header offset too big (metadata not optimized)"; + } + + classOffsets()[h].clsOffset = (objc_stringhash_offset_t)coff; + classOffsets()[h].hiOffset = (objc_stringhash_offset_t)hoff; + } + else { + // class name has duplicates - write them all now + if (verbose) { + fprintf(stderr, "update_dyld_shared_cache: %u duplicates of Objective-C class %s\n", count, c->first); + } + + uint32_t dest = duplicateCount(); + duplicateCount() += count; + if (size() > remaining) { + return "selector section too small (metadata not optimized)"; + } + + // classOffsets() instead contains count and array index + classOffsets()[h].clsOffset = count*2 + 1; + classOffsets()[h].hiOffset = dest; + + std::pair + duplicates = classes.equal_range(c->first); + class_map::const_iterator dup; + for (dup = duplicates.first; dup != duplicates.second; ++dup) { + int64_t coff = dup->second.first - base; + int64_t hoff = dup->second.second - base; + if ((coff<>SHIFT != coff) { + return "class offset too big (metadata not optimized)"; + } + if ((hoff<>SHIFT != hoff) { + return "header offset too big (metadata not optimized)"; + } + + duplicateOffsets()[dest].clsOffset = (objc_stringhash_offset_t)coff; + duplicateOffsets()[dest].hiOffset = (objc_stringhash_offset_t)hoff; + dest++; + } + } + } +# undef SHIFT + + return NULL; + } + +// SELOPT_WRITE +#endif +}; + + + +struct objc_protocolopt_t : objc_stringhash_t { + // ...objc_stringhash_t fields... + // uint32_t protocolOffsets[capacity]; /* offsets from &capacity to protocol_t */ + + objc_stringhash_offset_t *protocolOffsets() { return (objc_stringhash_offset_t *)&offsets()[capacity]; } + const objc_stringhash_offset_t *protocolOffsets() const { return (const objc_stringhash_offset_t *)&offsets()[capacity]; } + + void* getProtocol(const char *key) const + { + uint32_t h = getIndex(key); + if (h == INDEX_NOT_FOUND) { + return NULL; + } + + return (void *)((const char *)this + protocolOffsets()[h]); + } + +#ifdef SELOPT_WRITE + + size_t size() + { + return + objc_stringhash_t::size() + capacity * sizeof(objc_stringhash_offset_t); + } + + void byteswap(bool little_endian) + { + objc_stringhash_offset_t *o; + + o = protocolOffsets(); + for (objc_stringhash_offset_t i = 0; i < capacity; i++) { + S32(o[i]); + } + + objc_stringhash_t::byteswap(little_endian); + } + + const char *write(uint64_t base, size_t remaining, + string_map& strings, protocol_map& protocols, + bool verbose) + { + const char *err; + err = objc_stringhash_t::write(base, remaining, strings); + if (err) return err; + + if (size() > remaining) { + return "selector section too small (metadata not optimized)"; + } + + // Set protocol offsets to 0 + for (uint32_t i = 0; i < capacity; i++) { + protocolOffsets()[i] = 0; + } + + // Set real protocol offsets +# define SHIFT (64 - 8*sizeof(objc_stringhash_offset_t)) + protocol_map::const_iterator c; + for (c = protocols.begin(); c != protocols.end(); ++c) { + uint32_t h = getIndex(c->first); + if (h == INDEX_NOT_FOUND) { + return "protocol list busted (metadata not optimized)"; + } + + int64_t offset = c->second - base; + if ((offset<>SHIFT != offset) { + return "protocol offset too big (metadata not optimized)"; + } + + protocolOffsets()[h] = (objc_stringhash_offset_t)offset; + } +# undef SHIFT + + return NULL; + } + +// SELOPT_WRITE +#endif +}; + + +// Precomputed image list. +struct objc_headeropt_ro_t; + +// Precomputed image list. +struct objc_headeropt_rw_t; + +// Precomputed class list. +struct objc_clsopt_t; + +// Edit objc-sel-table.s if you change this value. +// lldb and Symbolication read these structures. Inform them of any changes. +enum { VERSION = 15 }; + +// Values for objc_opt_t::flags +enum : uint32_t { + IsProduction = (1 << 0), // never set in development cache + NoMissingWeakSuperclasses = (1 << 1), // never set in development cache +}; + +// Top-level optimization structure. +// Edit objc-sel-table.s if you change this structure. +struct alignas(alignof(void*)) objc_opt_t { + uint32_t version; + uint32_t flags; + int32_t selopt_offset; + int32_t headeropt_ro_offset; + int32_t clsopt_offset; + int32_t protocolopt_offset; + int32_t headeropt_rw_offset; + + const objc_selopt_t* selopt() const { + if (selopt_offset == 0) return NULL; + return (objc_selopt_t *)((uint8_t *)this + selopt_offset); + } + objc_selopt_t* selopt() { + if (selopt_offset == 0) return NULL; + return (objc_selopt_t *)((uint8_t *)this + selopt_offset); + } + + struct objc_headeropt_ro_t* headeropt_ro() const { + if (headeropt_ro_offset == 0) return NULL; + return (struct objc_headeropt_ro_t *)((uint8_t *)this + headeropt_ro_offset); + } + + struct objc_clsopt_t* clsopt() const { + if (clsopt_offset == 0) return NULL; + return (objc_clsopt_t *)((uint8_t *)this + clsopt_offset); + } + + struct objc_protocolopt_t* protocolopt() const { + if (protocolopt_offset == 0) return NULL; + return (objc_protocolopt_t *)((uint8_t *)this + protocolopt_offset); + } + + struct objc_headeropt_rw_t* headeropt_rw() const { + if (headeropt_rw_offset == 0) return NULL; + return (struct objc_headeropt_rw_t *)((uint8_t *)this + headeropt_rw_offset); + } +}; + +// sizeof(objc_opt_t) must be pointer-aligned +STATIC_ASSERT(sizeof(objc_opt_t) % sizeof(void*) == 0); + + +// List of offsets in libobjc that the shared cache optimization needs to use. +template +struct objc_opt_pointerlist_tt { + T protocolClass; +}; +typedef struct objc_opt_pointerlist_tt objc_opt_pointerlist_t; + + +/* +-------------------------------------------------------------------- +mix -- mix 3 64-bit values reversibly. +mix() takes 48 machine instructions, but only 24 cycles on a superscalar + machine (like Intel's new MMX architecture). It requires 4 64-bit + registers for 4::2 parallelism. +All 1-bit deltas, all 2-bit deltas, all deltas composed of top bits of + (a,b,c), and all deltas of bottom bits were tested. All deltas were + tested both on random keys and on keys that were nearly all zero. + These deltas all cause every bit of c to change between 1/3 and 2/3 + of the time (well, only 113/400 to 287/400 of the time for some + 2-bit delta). These deltas all cause at least 80 bits to change + among (a,b,c) when the mix is run either forward or backward (yes it + is reversible). +This implies that a hash using mix64 has no funnels. There may be + characteristics with 3-bit deltas or bigger, I didn't test for + those. +-------------------------------------------------------------------- +*/ +#define mix64(a,b,c) \ +{ \ + a -= b; a -= c; a ^= (c>>43); \ + b -= c; b -= a; b ^= (a<<9); \ + c -= a; c -= b; c ^= (b>>8); \ + a -= b; a -= c; a ^= (c>>38); \ + b -= c; b -= a; b ^= (a<<23); \ + c -= a; c -= b; c ^= (b>>5); \ + a -= b; a -= c; a ^= (c>>35); \ + b -= c; b -= a; b ^= (a<<49); \ + c -= a; c -= b; c ^= (b>>11); \ + a -= b; a -= c; a ^= (c>>12); \ + b -= c; b -= a; b ^= (a<<18); \ + c -= a; c -= b; c ^= (b>>22); \ +} + +/* +-------------------------------------------------------------------- +hash() -- hash a variable-length key into a 64-bit value + k : the key (the unaligned variable-length array of bytes) + len : the length of the key, counting by bytes + level : can be any 8-byte value +Returns a 64-bit value. Every bit of the key affects every bit of +the return value. No funnels. Every 1-bit and 2-bit delta achieves +avalanche. About 41+5len instructions. + +The best hash table sizes are powers of 2. There is no need to do +mod a prime (mod is sooo slow!). If you need less than 64 bits, +use a bitmask. For example, if you need only 10 bits, do + h = (h & hashmask(10)); +In which case, the hash table should have hashsize(10) elements. + +If you are hashing n strings (uint8_t **)k, do it like this: + for (i=0, h=0; i= 24) + { + a += (k[0] +((uint64_t)k[ 1]<< 8)+((uint64_t)k[ 2]<<16)+((uint64_t)k[ 3]<<24) + +((uint64_t)k[4 ]<<32)+((uint64_t)k[ 5]<<40)+((uint64_t)k[ 6]<<48)+((uint64_t)k[ 7]<<56)); + b += (k[8] +((uint64_t)k[ 9]<< 8)+((uint64_t)k[10]<<16)+((uint64_t)k[11]<<24) + +((uint64_t)k[12]<<32)+((uint64_t)k[13]<<40)+((uint64_t)k[14]<<48)+((uint64_t)k[15]<<56)); + c += (k[16] +((uint64_t)k[17]<< 8)+((uint64_t)k[18]<<16)+((uint64_t)k[19]<<24) + +((uint64_t)k[20]<<32)+((uint64_t)k[21]<<40)+((uint64_t)k[22]<<48)+((uint64_t)k[23]<<56)); + mix64(a,b,c); + k += 24; len -= 24; + } + + /*------------------------------------- handle the last 23 bytes */ + c += length; + switch(len) /* all the case statements fall through */ + { + case 23: c+=((uint64_t)k[22]<<56); + case 22: c+=((uint64_t)k[21]<<48); + case 21: c+=((uint64_t)k[20]<<40); + case 20: c+=((uint64_t)k[19]<<32); + case 19: c+=((uint64_t)k[18]<<24); + case 18: c+=((uint64_t)k[17]<<16); + case 17: c+=((uint64_t)k[16]<<8); + /* the first byte of c is reserved for the length */ + case 16: b+=((uint64_t)k[15]<<56); + case 15: b+=((uint64_t)k[14]<<48); + case 14: b+=((uint64_t)k[13]<<40); + case 13: b+=((uint64_t)k[12]<<32); + case 12: b+=((uint64_t)k[11]<<24); + case 11: b+=((uint64_t)k[10]<<16); + case 10: b+=((uint64_t)k[ 9]<<8); + case 9: b+=((uint64_t)k[ 8]); + case 8: a+=((uint64_t)k[ 7]<<56); + case 7: a+=((uint64_t)k[ 6]<<48); + case 6: a+=((uint64_t)k[ 5]<<40); + case 5: a+=((uint64_t)k[ 4]<<32); + case 4: a+=((uint64_t)k[ 3]<<24); + case 3: a+=((uint64_t)k[ 2]<<16); + case 2: a+=((uint64_t)k[ 1]<<8); + case 1: a+=((uint64_t)k[ 0]); + /* case 0: nothing left to add */ + } + mix64(a,b,c); + /*-------------------------------------------- report the result */ + return c; +} + + +#ifdef SELOPT_WRITE + +/* +------------------------------------------------------------------------------ +This generates a minimal perfect hash function. That means, given a +set of n keys, this determines a hash function that maps each of +those keys into a value in 0..n-1 with no collisions. + +The perfect hash function first uses a normal hash function on the key +to determine (a,b) such that the pair (a,b) is distinct for all +keys, then it computes a^scramble[tab[b]] to get the final perfect hash. +tab[] is an array of 1-byte values and scramble[] is a 256-term array of +2-byte or 4-byte values. If there are n keys, the length of tab[] is a +power of two between n/3 and n. + +I found the idea of computing distinct (a,b) values in "Practical minimal +perfect hash functions for large databases", Fox, Heath, Chen, and Daoud, +Communications of the ACM, January 1992. They found the idea in Chichelli +(CACM Jan 1980). Beyond that, our methods differ. + +The key is hashed to a pair (a,b) where a in 0..*alen*-1 and b in +0..*blen*-1. A fast hash function determines both a and b +simultaneously. Any decent hash function is likely to produce +hashes so that (a,b) is distinct for all pairs. I try the hash +using different values of *salt* until all pairs are distinct. + +The final hash is (a XOR scramble[tab[b]]). *scramble* is a +predetermined mapping of 0..255 into 0..smax-1. *tab* is an +array that we fill in in such a way as to make the hash perfect. + +First we fill in all values of *tab* that are used by more than one +key. We try all possible values for each position until one works. + +This leaves m unmapped keys and m values that something could hash to. +If you treat unmapped keys as lefthand nodes and unused hash values +as righthand nodes, and draw a line connecting each key to each hash +value it could map to, you get a bipartite graph. We attempt to +find a perfect matching in this graph. If we succeed, we have +determined a perfect hash for the whole set of keys. + +*scramble* is used because (a^tab[i]) clusters keys around *a*. +------------------------------------------------------------------------------ +*/ + +typedef uint64_t ub8; +#define UB8MAXVAL 0xffffffffffffffffLL +#define UB8BITS 64 +typedef uint32_t ub4; +#define UB4MAXVAL 0xffffffff +#define UB4BITS 32 +typedef uint16_t ub2; +#define UB2MAXVAL 0xffff +#define UB2BITS 16 +typedef uint8_t ub1; +#define UB1MAXVAL 0xff +#define UB1BITS 8 + +#define TRUE 1 +#define FALSE 0 + +#define SCRAMBLE_LEN 256 // ((ub4)1<<16) /* length of *scramble* */ +#define RETRY_INITKEY 2048 /* number of times to try to find distinct (a,b) */ +#define RETRY_PERFECT 4 /* number of times to try to make a perfect hash */ + + +/* representation of a key */ +struct key +{ + ub1 *name_k; /* the actual key */ + ub4 len_k; /* the length of the actual key */ + ub4 hash_k; /* the initial hash value for this key */ +/* beyond this point is mapping-dependent */ + ub4 a_k; /* a, of the key maps to (a,b) */ + ub4 b_k; /* b, of the key maps to (a,b) */ + struct key *nextb_k; /* next key with this b */ +}; +typedef struct key key; + +/* things indexed by b of original (a,b) pair */ +struct bstuff +{ + ub2 val_b; /* hash=a^tabb[b].val_b */ + key *list_b; /* tabb[i].list_b is list of keys with b==i */ + ub4 listlen_b; /* length of list_b */ + ub4 water_b; /* high watermark of who has visited this map node */ +}; +typedef struct bstuff bstuff; + +/* things indexed by final hash value */ +struct hstuff +{ + key *key_h; /* tabh[i].key_h is the key with a hash of i */ +}; +typedef struct hstuff hstuff; + +/* things indexed by queue position */ +struct qstuff +{ + bstuff *b_q; /* b that currently occupies this hash */ + ub4 parent_q; /* queue position of parent that could use this hash */ + ub2 newval_q; /* what to change parent tab[b] to to use this hash */ + ub2 oldval_q; /* original value of tab[b] */ +}; +typedef struct qstuff qstuff; + + +/* +------------------------------------------------------------------------------ +Find the mapping that will produce a perfect hash +------------------------------------------------------------------------------ +*/ + +/* return the ceiling of the log (base 2) of val */ +static ub4 log2u(ub4 val) +{ + ub4 i; + for (i=0; ((ub4)1<>const3)); + x = (x+(x<>const5)); + } + return x; +} + +/* initialize scramble[] with distinct random values in 0..smax-1 */ +static void scrambleinit(ub4 *scramble, ub4 smax) +// ub4 *scramble; /* hash is a^scramble[tab[b]] */ +// ub4 smax; /* scramble values should be in 0..smax-1 */ +{ + ub4 i; + + /* fill scramble[] with distinct random integers in 0..smax-1 */ + for (i=0; ib_k + * check if the initial hash might work + */ +static int inittab(bstuff *tabb, ub4 blen, key *keys, ub4 nkeys, int complete) +// bstuff *tabb; /* output, list of keys with b for (a,b) */ +// ub4 blen; /* length of tabb */ +// key *keys; /* list of keys already hashed */ +// int complete; /* TRUE means to complete init despite collisions */ +{ + int nocollision = TRUE; + ub4 i; + + memset((void *)tabb, 0, (size_t)(sizeof(bstuff)*blen)); + + /* Two keys with the same (a,b) guarantees a collision */ + for (i = 0; i < nkeys; i++) { + key *mykey = keys+i; + key *otherkey; + + for (otherkey=tabb[mykey->b_k].list_b; + otherkey; + otherkey=otherkey->nextb_k) + { + if (mykey->a_k == otherkey->a_k) + { + nocollision = FALSE; + if (!complete) + return FALSE; + } + } + ++tabb[mykey->b_k].listlen_b; + mykey->nextb_k = tabb[mykey->b_k].list_b; + tabb[mykey->b_k].list_b = mykey; + } + + /* no two keys have the same (a,b) pair */ + return nocollision; +} + + +/* Do the initial hash for normal mode (use lookup and checksum) */ +static void initnorm(key *keys, ub4 nkeys, ub4 alen, ub4 blen, ub4 smax, ub8 salt) +// key *keys; /* list of all keys */ +// ub4 alen; /* (a,b) has a in 0..alen-1, a power of 2 */ +// ub4 blen; /* (a,b) has b in 0..blen-1, a power of 2 */ +// ub4 smax; /* maximum range of computable hash values */ +// ub4 salt; /* used to initialize the hash function */ +// gencode *final; /* output, code for the final hash */ +{ + ub4 loga = log2u(alen); /* log based 2 of blen */ + ub4 i; + for (i = 0; i < nkeys; i++) { + key *mykey = keys+i; + ub8 hash = lookup8(mykey->name_k, mykey->len_k, salt); + mykey->a_k = (loga > 0) ? (ub4)(hash >> (UB8BITS-loga)) : 0; + mykey->b_k = (blen > 1) ? (hash & (blen-1)) : 0; + } +} + + +/* Try to apply an augmenting list */ +static int apply(bstuff *tabb, hstuff *tabh, qstuff *tabq, ub4 blen, ub4 *scramble, ub4 tail, int rollback) +// bstuff *tabb; +// hstuff *tabh; +// qstuff *tabq; +// ub4 blen; +// ub4 *scramble; +// ub4 tail; +// int rollback; /* FALSE applies augmenting path, TRUE rolls back */ +{ + ub4 hash; + key *mykey; + bstuff *pb; + ub4 child; + ub4 parent; + ub4 stabb; /* scramble[tab[b]] */ + + /* walk from child to parent */ + for (child=tail-1; child; child=parent) + { + parent = tabq[child].parent_q; /* find child's parent */ + pb = tabq[parent].b_q; /* find parent's list of siblings */ + + /* erase old hash values */ + stabb = scramble[pb->val_b]; + for (mykey=pb->list_b; mykey; mykey=mykey->nextb_k) + { + hash = mykey->a_k^stabb; + if (mykey == tabh[hash].key_h) + { /* erase hash for all of child's siblings */ + tabh[hash].key_h = (key *)0; + } + } + + /* change pb->val_b, which will change the hashes of all parent siblings */ + pb->val_b = (rollback ? tabq[child].oldval_q : tabq[child].newval_q); + + /* set new hash values */ + stabb = scramble[pb->val_b]; + for (mykey=pb->list_b; mykey; mykey=mykey->nextb_k) + { + hash = mykey->a_k^stabb; + if (rollback) + { + if (parent == 0) continue; /* root never had a hash */ + } + else if (tabh[hash].key_h) + { + /* very rare: roll back any changes */ + apply(tabb, tabh, tabq, blen, scramble, tail, TRUE); + return FALSE; /* failure, collision */ + } + tabh[hash].key_h = mykey; + } + } + return TRUE; +} + + +/* +------------------------------------------------------------------------------- +augment(): Add item to the mapping. + +Construct a spanning tree of *b*s with *item* as root, where each +parent can have all its hashes changed (by some new val_b) with +at most one collision, and each child is the b of that collision. + +I got this from Tarjan's "Data Structures and Network Algorithms". The +path from *item* to a *b* that can be remapped with no collision is +an "augmenting path". Change values of tab[b] along the path so that +the unmapped key gets mapped and the unused hash value gets used. + +Assuming 1 key per b, if m out of n hash values are still unused, +you should expect the transitive closure to cover n/m nodes before +an unused node is found. Sum(i=1..n)(n/i) is about nlogn, so expect +this approach to take about nlogn time to map all single-key b's. +------------------------------------------------------------------------------- +*/ +static int augment(bstuff *tabb, hstuff *tabh, qstuff *tabq, ub4 blen, ub4 *scramble, ub4 smax, bstuff *item, ub4 nkeys, + ub4 highwater) +// bstuff *tabb; /* stuff indexed by b */ +// hstuff *tabh; /* which key is associated with which hash, indexed by hash */ +// qstuff *tabq; /* queue of *b* values, this is the spanning tree */ +// ub4 blen; /* length of tabb */ +// ub4 *scramble; /* final hash is a^scramble[tab[b]] */ +// ub4 smax; /* highest value in scramble */ +// bstuff *item; /* &tabb[b] for the b to be mapped */ +// ub4 nkeys; /* final hash must be in 0..nkeys-1 */ +// ub4 highwater; /* a value higher than any now in tabb[].water_b */ +{ + ub4 q; /* current position walking through the queue */ + ub4 tail; /* tail of the queue. 0 is the head of the queue. */ + ub4 limit=UB1MAXVAL+1; + ub4 highhash = smax; + + /* initialize the root of the spanning tree */ + tabq[0].b_q = item; + tail = 1; + + /* construct the spanning tree by walking the queue, add children to tail */ + for (q=0; qval_b */ + + if (q == 1) + break; /* don't do transitive closure */ + + for (i=0; ilist_b; mykey; mykey=mykey->nextb_k) + { + key *childkey; + ub4 hash = mykey->a_k^scramble[i]; + + if (hash >= highhash) break; /* out of bounds */ + childkey = tabh[hash].key_h; + + if (childkey) + { + bstuff *hitb = &tabb[childkey->b_k]; + + if (childb) + { + if (childb != hitb) break; /* hit at most one child b */ + } + else + { + childb = hitb; /* remember this as childb */ + if (childb->water_b == highwater) break; /* already explored */ + } + } + } + if (mykey) continue; /* myb with i has multiple collisions */ + + /* add childb to the queue of reachable things */ + if (childb) childb->water_b = highwater; + tabq[tail].b_q = childb; + tabq[tail].newval_q = i; /* how to make parent (myb) use this hash */ + tabq[tail].oldval_q = myb->val_b; /* need this for rollback */ + tabq[tail].parent_q = q; + ++tail; + + if (!childb) + { /* found an *i* with no collisions? */ + /* try to apply the augmenting path */ + if (apply(tabb, tabh, tabq, blen, scramble, tail, FALSE)) + return TRUE; /* success, item was added to the perfect hash */ + + --tail; /* don't know how to handle such a child! */ + } + } + } + return FALSE; +} + + +/* find a mapping that makes this a perfect hash */ +static int perfect(bstuff *tabb, hstuff *tabh, qstuff *tabq, ub4 blen, ub4 smax, ub4 *scramble, ub4 nkeys) +{ + ub4 maxkeys; /* maximum number of keys for any b */ + ub4 i, j; + +#if SELOPT_DEBUG + fprintf(stderr, " blen %d smax %d nkeys %d\n", blen, smax, nkeys); +#endif + + /* clear any state from previous attempts */ + memset((void *)tabh, 0, sizeof(hstuff)*smax); + memset((void *)tabq, 0, sizeof(qstuff)*(blen+1)); + + for (maxkeys=0,i=0; i maxkeys) + maxkeys = tabb[i].listlen_b; + + /* In descending order by number of keys, map all *b*s */ + for (j=maxkeys; j>0; --j) + for (i=0; i= RETRY_INITKEY) + { + /* Try to put more bits in (A,B) to make distinct (A,B) more likely */ + if (*alen < maxalen) + { + *alen *= 2; + } + else if (*blen < smax) + { + *blen *= 2; + delete[] tabq; + delete[] *tabb; + *tabb = new bstuff[*blen]; + tabq = new qstuff[*blen+1]; + } + bad_initkey = 0; + bad_perfect = 0; + } + continue; /* two keys have same (a,b) pair */ + } + + /* Given distinct (A,B) for all keys, build a perfect hash */ + if (!perfect(*tabb, tabh, tabq, *blen, smax, scramble, nkeys)) + { + if (++bad_perfect >= RETRY_PERFECT) + { + if (*blen < smax) + { + *blen *= 2; + delete[] *tabb; + delete[] tabq; + *tabb = new bstuff[*blen]; + tabq = new qstuff[*blen+1]; + --si; /* we know this salt got distinct (A,B) */ + } + else + { + return 0; + } + bad_perfect = 0; + } + continue; + } + + break; + } + + /* free working memory */ + delete[] tabh; + delete[] tabq; + + return 1; +} + +/* +------------------------------------------------------------------------------ +Input/output type routines +------------------------------------------------------------------------------ +*/ + +/* get the list of keys */ +static void getkeys(key **keys, ub4 *nkeys, const string_map& strings) +{ + key *buf = new key[strings.size()]; + size_t i; + string_map::const_iterator s; + for (i = 0, s = strings.begin(); s != strings.end(); ++s, ++i) { + key *mykey = buf+i; + mykey->name_k = (ub1 *)s->first; + mykey->len_k = (ub4)strlen(s->first); + } + *keys = buf; + *nkeys = (ub4)strings.size(); +} + + +static perfect_hash +make_perfect(const string_map& strings) +{ + ub4 nkeys; /* number of keys */ + key *keys; /* head of list of keys */ + bstuff *tab; /* table indexed by b */ + ub4 smax; /* scramble[] values in 0..smax-1, a power of 2 */ + ub4 alen; /* a in 0..alen-1, a power of 2 */ + ub4 blen; /* b in 0..blen-1, a power of 2 */ + ub8 salt; /* a parameter to the hash function */ + ub4 scramble[SCRAMBLE_LEN]; /* used in final hash function */ + int ok; + int i; + perfect_hash result; + + /* read in the list of keywords */ + getkeys(&keys, &nkeys, strings); + + /* find the hash */ + smax = ((ub4)1< +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "Trie.hpp" +#include "dyld_cache_config.h" + +#if !NEW_CACHE_FILE_FORMAT + #include "CacheFileAbstraction.hpp" +#endif + +#ifndef EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE + #define EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE 0x02 +#endif + + +namespace { + +template +class Adjustor { +public: + Adjustor(void* cacheBuffer, macho_header

* mh, const std::vector& segNewStartAddresses, + const std::vector& segCacheFileOffset, const std::vector& segCacheFileSizes); + void adjustImageForNewSegmentLocations(std::vector& pointersForASLR); + +private: + void adjustReferencesUsingInfoV2(std::vector& pointersForASLR); + void adjustReference(uint32_t kind, uint8_t* mappedAddr, uint64_t fromNewAddress, uint64_t toNewAddress, int64_t adjust, int64_t targetSlide, + uint64_t imageStartAddress, uint64_t imageEndAddress, std::vector& pointersForASLR, uint32_t*& lastMappedAddr32, + uint32_t& lastKind, uint64_t& lastToNewAddress); + void adjustDataPointers(std::vector& pointersForASLR); + void slidePointer(int segIndex, uint64_t segOffset, uint8_t type, std::vector& pointersForASLR); + void adjustSymbolTable(); + void adjustExportsTrie(std::vector& newTrieBytes); + void rebuildLinkEdit(); + void adjustCode(); + void adjustInstruction(uint8_t kind, uint64_t cacheOffset, uint64_t codeToDataDelta); + void rebuildLinkEditAndLoadCommands(); + uint64_t slideForOrigAddress(uint64_t addr); + + typedef typename P::uint_t pint_t; + typedef typename P::E E; + + void* _cacheBuffer; + macho_header

* _mh; + const uint8_t* _linkeditBias = nullptr; + int64_t _linkeditAdjust = 0; + unsigned _linkeditSegIndex = 0; + bool _maskPointers = false; + bool _splitSegInfoV2 = false; + const char* _installName = nullptr; + macho_symtab_command

* _symTabCmd = nullptr; + macho_dysymtab_command

* _dynSymTabCmd = nullptr; + macho_dyld_info_command

* _dyldInfo = nullptr; + macho_linkedit_data_command

* _splitSegInfoCmd = nullptr; + macho_linkedit_data_command

* _functionStartsCmd = nullptr; + macho_linkedit_data_command

* _dataInCodeCmd = nullptr; + std::vector _segOrigStartAddresses; + std::vector _segNewStartAddresses; + std::vector _segCacheOffsets; + std::vector _segCacheSizes; + std::vector _segSlides; + std::vector*> _segCmds; +}; + +template +Adjustor

::Adjustor(void* cacheBuffer, macho_header

* mh, const std::vector& segNewStartAddresses, + const std::vector& segCacheFileOffsets, const std::vector& segCacheFileSizes) + : _mh(mh), _cacheBuffer(cacheBuffer), _segNewStartAddresses(segNewStartAddresses), + _segCacheOffsets(segCacheFileOffsets), _segCacheSizes(segCacheFileSizes) +{ + macho_segment_command

* segCmd; + const macho_load_command

* const cmds = (macho_load_command

*)((uint8_t*)mh + sizeof(macho_header

)); + const uint32_t cmd_count = mh->ncmds(); + const macho_load_command

* cmd = cmds; + unsigned segIndex = 0; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd()) { + case LC_ID_DYLIB: + _installName = ((macho_dylib_command

*)cmd)->name(); + break; + case LC_SYMTAB: + _symTabCmd = (macho_symtab_command

*)cmd; + break; + case LC_DYSYMTAB: + _dynSymTabCmd = (macho_dysymtab_command

*)cmd; + break; + case LC_DYLD_INFO: + case LC_DYLD_INFO_ONLY: + _dyldInfo = (macho_dyld_info_command

*)cmd; + break; + case LC_SEGMENT_SPLIT_INFO: + _splitSegInfoCmd = (macho_linkedit_data_command

*)cmd; + break; + case LC_FUNCTION_STARTS: + _functionStartsCmd = (macho_linkedit_data_command

*)cmd; + break; + case LC_DATA_IN_CODE: + _dataInCodeCmd = (macho_linkedit_data_command

*)cmd; + break; + case macho_segment_command

::CMD: + segCmd = (macho_segment_command

*)cmd; + _segCmds.push_back(segCmd); + _segOrigStartAddresses.push_back(segCmd->vmaddr()); + _segSlides.push_back(_segNewStartAddresses[segIndex] - segCmd->vmaddr()); + if ( strcmp(segCmd->segname(), "__LINKEDIT") == 0 ) { + _linkeditAdjust = segCacheFileOffsets[segIndex] - segCmd->fileoff(); + _linkeditBias = (uint8_t*)cacheBuffer + _linkeditAdjust; + _linkeditSegIndex = segIndex; + } + ++segIndex; + break; + } + cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); + } + _maskPointers = (P::E::get32(mh->cputype()) == CPU_TYPE_ARM64); + if ( _splitSegInfoCmd != NULL ) { + const uint8_t* infoStart = &_linkeditBias[_splitSegInfoCmd->dataoff()]; + _splitSegInfoV2 = (*infoStart == DYLD_CACHE_ADJ_V2_FORMAT); + } +} + +template +void Adjustor

::adjustImageForNewSegmentLocations(std::vector& pointersForASLR) +{ + if ( _splitSegInfoV2 ) { + adjustReferencesUsingInfoV2(pointersForASLR); + } + else { + adjustDataPointers(pointersForASLR); + adjustCode(); + } + adjustSymbolTable(); + rebuildLinkEditAndLoadCommands(); +} + +template +uint64_t Adjustor

::slideForOrigAddress(uint64_t addr) +{ + for (unsigned i=0; i < _segOrigStartAddresses.size(); ++i) { + if ( (_segOrigStartAddresses[i] <= addr) && (addr < (_segOrigStartAddresses[i]+_segCmds[i]->vmsize())) ) + return _segSlides[i]; + } + // On arm64, high nibble of pointers can have extra bits + if ( _maskPointers && (addr & 0xF000000000000000) ) { + return slideForOrigAddress(addr & 0x0FFFFFFFFFFFFFFF); + } + terminate("slide not known for dylib address 0x%llX in %s", addr, _installName); +} + +template +void Adjustor

::rebuildLinkEditAndLoadCommands() +{ + // Exports trie is only data structure in LINKEDIT that might grow + std::vector newTrieBytes; + adjustExportsTrie(newTrieBytes); + + // Remove: code signature, rebase info, code-sign-dirs, split seg info + uint32_t bindOffset = 0; + uint32_t bindSize = _dyldInfo->bind_size(); + uint32_t lazyBindOffset = bindOffset + bindSize; + uint32_t lazyBindSize = _dyldInfo->lazy_bind_size(); + uint32_t weakBindOffset = lazyBindOffset + lazyBindSize; + uint32_t weakBindSize = _dyldInfo->weak_bind_size(); + uint32_t exportOffset = weakBindOffset + weakBindSize; + uint32_t exportSize = (uint32_t)newTrieBytes.size(); + uint32_t splitSegInfoOffset = exportOffset + exportSize; + uint32_t splitSegInfosSize = (_splitSegInfoCmd ? _splitSegInfoCmd->datasize() : 0); + uint32_t funcStartsOffset = splitSegInfoOffset + splitSegInfosSize; + uint32_t funcStartsSize = (_functionStartsCmd ? _functionStartsCmd->datasize() : 0); + uint32_t dataInCodeOffset = funcStartsOffset + funcStartsSize; + uint32_t dataInCodeSize = (_dataInCodeCmd ? _dataInCodeCmd->datasize() : 0); + uint32_t symbolTableOffset = dataInCodeOffset + dataInCodeSize; + uint32_t symbolTableSize = _symTabCmd->nsyms() * sizeof(macho_nlist

); + uint32_t indirectTableOffset = symbolTableOffset + symbolTableSize; + uint32_t indirectTableSize = _dynSymTabCmd->nindirectsyms() * sizeof(uint32_t); + uint32_t symbolStringsOffset = indirectTableOffset + indirectTableSize; + uint32_t symbolStringsSize = _symTabCmd->strsize(); + uint32_t newLinkEditSize = symbolStringsOffset + symbolStringsSize; + + size_t linkeditBufferSize = align(_segCmds[_linkeditSegIndex]->vmsize(), 12); + if ( linkeditBufferSize < newLinkEditSize ) { + terminate("LINKEDIT overflow in %s", _installName); + } + + uint32_t linkeditStartOffset = (uint32_t)_segCacheOffsets[_linkeditSegIndex]; + uint8_t* newLinkeditBufer = (uint8_t*)::calloc(linkeditBufferSize, 1); + if ( bindSize ) + memcpy(&newLinkeditBufer[bindOffset], &_linkeditBias[_dyldInfo->bind_off()], bindSize); + if ( lazyBindSize ) + memcpy(&newLinkeditBufer[lazyBindOffset], &_linkeditBias[_dyldInfo->lazy_bind_off()], lazyBindSize); + if ( weakBindSize ) + memcpy(&newLinkeditBufer[weakBindOffset], &_linkeditBias[_dyldInfo->weak_bind_off()], weakBindSize); + if ( exportSize ) + memcpy(&newLinkeditBufer[exportOffset], &newTrieBytes[0], exportSize); + if ( splitSegInfosSize ) + memcpy(&newLinkeditBufer[splitSegInfoOffset], &_linkeditBias[_splitSegInfoCmd->dataoff()], splitSegInfosSize); + if ( funcStartsSize ) + memcpy(&newLinkeditBufer[funcStartsOffset], &_linkeditBias[_functionStartsCmd->dataoff()], funcStartsSize); + if ( dataInCodeSize ) + memcpy(&newLinkeditBufer[dataInCodeOffset], &_linkeditBias[_dataInCodeCmd->dataoff()], dataInCodeSize); + if ( symbolTableSize ) + memcpy(&newLinkeditBufer[symbolTableOffset], &_linkeditBias[_symTabCmd->symoff()], symbolTableSize); + if ( indirectTableSize ) + memcpy(&newLinkeditBufer[indirectTableOffset], &_linkeditBias[_dynSymTabCmd->indirectsymoff()], indirectTableSize); + if ( symbolStringsSize ) + memcpy(&newLinkeditBufer[symbolStringsOffset], &_linkeditBias[_symTabCmd->stroff()], symbolStringsSize); + + memcpy((uint8_t*)_cacheBuffer+linkeditStartOffset, newLinkeditBufer, newLinkEditSize); + ::bzero((uint8_t*)_cacheBuffer+linkeditStartOffset+newLinkEditSize, linkeditBufferSize-newLinkEditSize); + ::free(newLinkeditBufer); + + // updates load commands and removed ones no longer needed + macho_load_command

* const cmds = (macho_load_command

*)((uint8_t*)_mh + sizeof(macho_header

)); + uint32_t cmd_count = _mh->ncmds(); + const macho_load_command

* cmd = cmds; + const unsigned origLoadCommandsSize = _mh->sizeofcmds(); + unsigned bytesRemaining = origLoadCommandsSize; + unsigned removedCount = 0; + unsigned segIndex = 0; + for (uint32_t i = 0; i < cmd_count; ++i) { + macho_symtab_command

* symTabCmd; + macho_dysymtab_command

* dynSymTabCmd; + macho_dyld_info_command

* dyldInfo; + macho_linkedit_data_command

* functionStartsCmd; + macho_linkedit_data_command

* dataInCodeCmd; + macho_linkedit_data_command

* splitSegInfoCmd; + macho_segment_command

* segCmd; + macho_routines_command

* routinesCmd; + macho_dylib_command

* dylibIDCmd; + uint32_t cmdSize = cmd->cmdsize(); + int32_t segFileOffsetDelta; + bool remove = false; + switch ( cmd->cmd() ) { + case LC_ID_DYLIB: + dylibIDCmd = (macho_dylib_command

*)cmd; + dylibIDCmd->set_timestamp(2); // match what static linker sets in LC_LOAD_DYLIB + break; + case LC_SYMTAB: + symTabCmd = (macho_symtab_command

*)cmd; + symTabCmd->set_symoff(linkeditStartOffset+symbolTableOffset); + symTabCmd->set_stroff(linkeditStartOffset+symbolStringsOffset); + break; + case LC_DYSYMTAB: + dynSymTabCmd = (macho_dysymtab_command

*)cmd; + dynSymTabCmd->set_indirectsymoff(linkeditStartOffset+indirectTableOffset); + break; + case LC_DYLD_INFO: + case LC_DYLD_INFO_ONLY: + dyldInfo = (macho_dyld_info_command

*)cmd; + dyldInfo->set_rebase_off(0); + dyldInfo->set_rebase_size(0); + dyldInfo->set_bind_off(bindSize ? linkeditStartOffset+bindOffset : 0); + dyldInfo->set_bind_size(bindSize); + dyldInfo->set_weak_bind_off(weakBindSize ? linkeditStartOffset+weakBindOffset : 0); + dyldInfo->set_weak_bind_size(weakBindSize); + dyldInfo->set_lazy_bind_off(lazyBindSize ? linkeditStartOffset+lazyBindOffset : 0); + dyldInfo->set_lazy_bind_size(lazyBindSize); + dyldInfo->set_export_off(exportSize ? linkeditStartOffset+exportOffset : 0); + dyldInfo->set_export_size(exportSize); + break; + case LC_FUNCTION_STARTS: + functionStartsCmd = (macho_linkedit_data_command

*)cmd; + functionStartsCmd->set_dataoff(linkeditStartOffset+funcStartsOffset); + break; + case LC_DATA_IN_CODE: + dataInCodeCmd = (macho_linkedit_data_command

*)cmd; + dataInCodeCmd->set_dataoff(linkeditStartOffset+dataInCodeOffset); + break; + case macho_routines_command

::CMD: + routinesCmd = (macho_routines_command

*)cmd; + routinesCmd->set_init_address(routinesCmd->init_address()+slideForOrigAddress(routinesCmd->init_address())); + break; + case macho_segment_command

::CMD: + segCmd = (macho_segment_command

*)cmd; + segFileOffsetDelta = (int32_t)(_segCacheOffsets[segIndex] - segCmd->fileoff()); + segCmd->set_vmaddr(_segNewStartAddresses[segIndex]); + segCmd->set_vmsize(_segCacheSizes[segIndex]); + segCmd->set_fileoff(_segCacheOffsets[segIndex]); + segCmd->set_filesize(_segCacheSizes[segIndex]); + if ( strcmp(segCmd->segname(), "__LINKEDIT") == 0 ) + segCmd->set_vmsize(linkeditBufferSize); + if ( segCmd->nsects() > 0 ) { + macho_section

* const sectionsStart = (macho_section

*)((uint8_t*)segCmd + sizeof(macho_segment_command

)); + macho_section

* const sectionsEnd = §ionsStart[segCmd->nsects()]; + for (macho_section

* sect=sectionsStart; sect < sectionsEnd; ++sect) { + sect->set_addr(sect->addr() + _segSlides[segIndex]); + if ( sect->offset() != 0 ) + sect->set_offset(sect->offset() + segFileOffsetDelta); + } + } + ++segIndex; + break; + case LC_RPATH: + warning("dyld shared cache does not support LC_RPATH found in %s", _installName); + remove = true; + break; + case LC_SEGMENT_SPLIT_INFO: + splitSegInfoCmd = (macho_linkedit_data_command

*)cmd; + splitSegInfoCmd->set_dataoff(linkeditStartOffset+splitSegInfoOffset); + break; + case LC_CODE_SIGNATURE: + case LC_DYLIB_CODE_SIGN_DRS: + remove = true; + break; + default: + break; + } + macho_load_command

* nextCmd = (macho_load_command

*)(((uint8_t*)cmd)+cmdSize); + if ( remove ) { + ::memmove((void*)cmd, (void*)nextCmd, bytesRemaining); + ++removedCount; + } + else { + bytesRemaining -= cmdSize; + cmd = nextCmd; + } + } + // zero out stuff removed + ::bzero((void*)cmd, bytesRemaining); + // update header + _mh->set_ncmds(cmd_count-removedCount); + _mh->set_sizeofcmds(origLoadCommandsSize-bytesRemaining); + _mh->set_flags(_mh->flags() | 0x80000000); +} + + +template +void Adjustor

::adjustSymbolTable() +{ + macho_nlist

* symbolTable = (macho_nlist

*)&_linkeditBias[_symTabCmd->symoff()]; + + // adjust global symbol table entries + macho_nlist

* lastExport = &symbolTable[_dynSymTabCmd->iextdefsym()+_dynSymTabCmd->nextdefsym()]; + for (macho_nlist

* entry = &symbolTable[_dynSymTabCmd->iextdefsym()]; entry < lastExport; ++entry) { + if ( (entry->n_type() & N_TYPE) == N_SECT ) + entry->set_n_value(entry->n_value() + slideForOrigAddress(entry->n_value())); + } + + // adjust local symbol table entries + macho_nlist

* lastLocal = &symbolTable[_dynSymTabCmd->ilocalsym()+_dynSymTabCmd->nlocalsym()]; + for (macho_nlist

* entry = &symbolTable[_dynSymTabCmd->ilocalsym()]; entry < lastLocal; ++entry) { + if ( (entry->n_sect() != NO_SECT) && ((entry->n_type() & N_STAB) == 0) ) + entry->set_n_value(entry->n_value() + slideForOrigAddress(entry->n_value())); + } +} + +template +void Adjustor

::slidePointer(int segIndex, uint64_t segOffset, uint8_t type, std::vector& pointersForASLR) +{ + pint_t* mappedAddrP = (pint_t*)((uint8_t*)_cacheBuffer + _segCacheOffsets[segIndex] + segOffset); + uint32_t* mappedAddr32 = (uint32_t*)mappedAddrP; + pint_t valueP; + uint32_t value32; + switch ( type ) { + case REBASE_TYPE_POINTER: + valueP = (pint_t)P::getP(*mappedAddrP); + P::setP(*mappedAddrP, valueP + slideForOrigAddress(valueP)); + pointersForASLR.push_back(mappedAddrP); + break; + + case REBASE_TYPE_TEXT_ABSOLUTE32: + value32 = P::E::get32(*mappedAddr32); + P::E::set32(*mappedAddr32, value32 + (uint32_t)slideForOrigAddress(value32)); + break; + + case REBASE_TYPE_TEXT_PCREL32: + // general text relocs not support + default: + terminate("unknown rebase type 0x%02X in %s", type, _installName); + } +} + + +static bool isThumbMovw(uint32_t instruction) +{ + return ( (instruction & 0x8000FBF0) == 0x0000F240 ); +} + +static bool isThumbMovt(uint32_t instruction) +{ + return ( (instruction & 0x8000FBF0) == 0x0000F2C0 ); +} + +static uint16_t getThumbWord(uint32_t instruction) +{ + uint32_t i = ((instruction & 0x00000400) >> 10); + uint32_t imm4 = (instruction & 0x0000000F); + uint32_t imm3 = ((instruction & 0x70000000) >> 28); + uint32_t imm8 = ((instruction & 0x00FF0000) >> 16); + return ((imm4 << 12) | (i << 11) | (imm3 << 8) | imm8); +} + +static uint32_t setThumbWord(uint32_t instruction, uint16_t word) { + uint32_t imm4 = (word & 0xF000) >> 12; + uint32_t i = (word & 0x0800) >> 11; + uint32_t imm3 = (word & 0x0700) >> 8; + uint32_t imm8 = word & 0x00FF; + return (instruction & 0x8F00FBF0) | imm4 | (i << 10) | (imm3 << 28) | (imm8 << 16); +} + +static bool isArmMovw(uint32_t instruction) +{ + return (instruction & 0x0FF00000) == 0x03000000; +} + +static bool isArmMovt(uint32_t instruction) +{ + return (instruction & 0x0FF00000) == 0x03400000; +} + +static uint16_t getArmWord(uint32_t instruction) +{ + uint32_t imm4 = ((instruction & 0x000F0000) >> 16); + uint32_t imm12 = (instruction & 0x00000FFF); + return (imm4 << 12) | imm12; +} + +static uint32_t setArmWord(uint32_t instruction, uint16_t word) { + uint32_t imm4 = (word & 0xF000) >> 12; + uint32_t imm12 = word & 0x0FFF; + return (instruction & 0xFFF0F000) | (imm4 << 16) | imm12; +} + +template +void Adjustor

::adjustReference(uint32_t kind, uint8_t* mappedAddr, uint64_t fromNewAddress, uint64_t toNewAddress, + int64_t adjust, int64_t targetSlide, uint64_t imageStartAddress, uint64_t imageEndAddress, + std::vector& pointersForASLR, uint32_t*& lastMappedAddr32, uint32_t& lastKind, uint64_t& lastToNewAddress) +{ + uint64_t value64; + uint64_t* mappedAddr64; + uint32_t value32; + uint32_t* mappedAddr32; + uint32_t instruction; + int64_t offsetAdjust; + int64_t delta; + switch ( kind ) { + case DYLD_CACHE_ADJ_V2_DELTA_32: + mappedAddr32 = (uint32_t*)mappedAddr; + value32 = P::E::get32(*mappedAddr32); + delta = (int32_t)value32; + delta += adjust; + if ( (delta > 0x80000000) || (-delta > 0x80000000) ) + terminate("DYLD_CACHE_ADJ_V2_DELTA_32 can't be adjust by 0x%016llX in %s", adjust, _installName); + P::E::set32(*mappedAddr32, (int32_t)delta); + break; + case DYLD_CACHE_ADJ_V2_POINTER_32: + mappedAddr32 = (uint32_t*)mappedAddr; + if ( toNewAddress != (E::get32(*mappedAddr32) + targetSlide) ) + terminate("bad DYLD_CACHE_ADJ_V2_POINTER_32 value not as expected at address 0x%llX in %s", fromNewAddress, _installName); + E::set32(*mappedAddr32, (uint32_t)toNewAddress); + pointersForASLR.push_back(mappedAddr); + break; + case DYLD_CACHE_ADJ_V2_POINTER_64: + mappedAddr64 = (uint64_t*)mappedAddr; + if ( toNewAddress != (E::get64(*mappedAddr64) + targetSlide) ) + terminate("bad DYLD_CACHE_ADJ_V2_POINTER_64 value not as expected at address 0x%llX in %s", fromNewAddress, _installName); + E::set64(*mappedAddr64, toNewAddress); + pointersForASLR.push_back(mappedAddr); + break; + case DYLD_CACHE_ADJ_V2_DELTA_64: + mappedAddr64 = (uint64_t*)mappedAddr; + value64 = P::E::get64(*mappedAddr64); + E::set64(*mappedAddr64, value64 + adjust); + break; + case DYLD_CACHE_ADJ_V2_IMAGE_OFF_32: + if ( adjust == 0 ) + break; + mappedAddr32 = (uint32_t*)mappedAddr; + value32 = P::E::get32(*mappedAddr32); + value64 = toNewAddress - imageStartAddress; + if ( value64 > imageEndAddress ) + terminate("DYLD_CACHE_ADJ_V2_IMAGE_OFF_32 can't be adjust to 0x%016llX in %s", toNewAddress, _installName); + P::E::set32(*mappedAddr32, (uint32_t)value64); + break; + break; + case DYLD_CACHE_ADJ_V2_ARM64_ADRP: + mappedAddr32 = (uint32_t*)mappedAddr; + instruction = P::E::get32(*mappedAddr32); + if ( (instruction & 0x9F000000) == 0x90000000 ) { + int64_t pageDistance = ((toNewAddress & ~0xFFF) - (fromNewAddress & ~0xFFF)); + int64_t newPage21 = pageDistance >> 12; + if ( (newPage21 > 2097151) || (newPage21 < -2097151) ) + terminate("DYLD_CACHE_ADJ_V2_ARM64_ADRP can't be adjusted that far in %s", _installName); + instruction = (instruction & 0x9F00001F) | ((newPage21 << 29) & 0x60000000) | ((newPage21 << 3) & 0x00FFFFE0); + P::E::set32(*mappedAddr32, instruction); + } + else { + // ADRP instructions are sometimes optimized to other instructions (e.g. ADR) after the split-seg-info is generated + } + break; + case DYLD_CACHE_ADJ_V2_ARM64_OFF12: + mappedAddr32 = (uint32_t*)mappedAddr; + instruction = P::E::get32(*mappedAddr32); + offsetAdjust = (adjust & 0xFFF); + if ( offsetAdjust == 0 ) + break; + if ( (instruction & 0x3B000000) == 0x39000000 ) { + // LDR/STR imm12 + if ( offsetAdjust != 0 ) { + uint32_t encodedAddend = ((instruction & 0x003FFC00) >> 10); + uint32_t newAddend = 0; + switch ( instruction & 0xC0000000 ) { + case 0x00000000: + if ( (instruction & 0x04800000) == 0x04800000 ) { + if ( offsetAdjust & 0xF ) + terminate("can't adjust off12 scale=16 instruction by %lld bytes at mapped address=%p in %s", offsetAdjust, mappedAddr, _installName); + if ( encodedAddend*16 >= 4096 ) + terminate("off12 scale=16 instruction points outside its page at mapped address=%p in %s", mappedAddr, _installName); + newAddend = (encodedAddend + offsetAdjust/16) % 256; + } + else { + // scale=1 + newAddend = (encodedAddend + (int32_t)offsetAdjust) % 4096; + } + break; + case 0x40000000: + if ( offsetAdjust & 1 ) + terminate("can't adjust off12 scale=2 instruction by %lld bytes at mapped address=%p in %s", offsetAdjust, mappedAddr, _installName); + if ( encodedAddend*2 >= 4096 ) + terminate("off12 scale=2 instruction points outside its page at mapped address=%p in %s", mappedAddr, _installName); + newAddend = (encodedAddend + offsetAdjust/2) % 2048; + break; + case 0x80000000: + if ( offsetAdjust & 3 ) + terminate("can't adjust off12 scale=4 instruction by %lld bytes at mapped address=%p in %s", offsetAdjust, mappedAddr, _installName); + if ( encodedAddend*4 >= 4096 ) + terminate("off12 scale=4 instruction points outside its page at mapped address=%p in %s", mappedAddr, _installName); + newAddend = (encodedAddend + offsetAdjust/4) % 1024; + break; + case 0xC0000000: + if ( offsetAdjust & 7 ) + terminate("can't adjust off12 scale=8 instruction by %lld bytes at mapped address=%p in %s", offsetAdjust, mappedAddr, _installName); + if ( encodedAddend*8 >= 4096 ) + terminate("off12 scale=8 instruction points outside its page at mapped address=%p in %s", mappedAddr, _installName); + newAddend = (encodedAddend + offsetAdjust/8) % 512; + break; + } + uint32_t newInstruction = (instruction & 0xFFC003FF) | (newAddend << 10); + P::E::set32(*mappedAddr32, newInstruction); + } + } + else if ( (instruction & 0xFFC00000) == 0x91000000 ) { + // ADD imm12 + if ( instruction & 0x00C00000 ) + terminate("ADD off12 uses shift at mapped address=%p in %s", mappedAddr, _installName); + uint32_t encodedAddend = ((instruction & 0x003FFC00) >> 10); + uint32_t newAddend = (encodedAddend + offsetAdjust) & 0xFFF; + uint32_t newInstruction = (instruction & 0xFFC003FF) | (newAddend << 10); + P::E::set32(*mappedAddr32, newInstruction); + } + else if ( instruction != 0xD503201F ) { + // ignore imm12 instructions optimized into a NOP, but warn about others + terminate("unknown off12 instruction 0x%08X at 0x%0llX in %s", instruction, fromNewAddress, _installName); + } + break; + case DYLD_CACHE_ADJ_V2_THUMB_MOVW_MOVT: + mappedAddr32 = (uint32_t*)mappedAddr; + // to update a movw/movt pair we need to extract the 32-bit they will make, + // add the adjust and write back the new movw/movt pair. + if ( lastKind == kind ) { + if ( lastToNewAddress == toNewAddress ) { + uint32_t instruction1 = P::E::get32(*lastMappedAddr32); + uint32_t instruction2 = P::E::get32(*mappedAddr32); + if ( isThumbMovw(instruction1) && isThumbMovt(instruction2) ) { + uint16_t high = getThumbWord(instruction2); + uint16_t low = getThumbWord(instruction1); + uint32_t full = high << 16 | low; + full += adjust; + instruction1 = setThumbWord(instruction1, full & 0xFFFF); + instruction2 = setThumbWord(instruction2, full >> 16); + } + else if ( isThumbMovt(instruction1) && isThumbMovw(instruction2) ) { + uint16_t high = getThumbWord(instruction1); + uint16_t low = getThumbWord(instruction2); + uint32_t full = high << 16 | low; + full += adjust; + instruction2 = setThumbWord(instruction2, full & 0xFFFF); + instruction1 = setThumbWord(instruction1, full >> 16); + } + else { + terminate("two DYLD_CACHE_ADJ_V2_THUMB_MOVW_MOVT in a row but not paried in %s", _installName); + } + P::E::set32(*lastMappedAddr32, instruction1); + P::E::set32(*mappedAddr32, instruction2); + kind = 0; + } + else { + terminate("two DYLD_CACHE_ADJ_V2_THUMB_MOVW_MOVT in a row but target different addresses in %s", _installName); + } + } + break; + case DYLD_CACHE_ADJ_V2_ARM_MOVW_MOVT: + mappedAddr32 = (uint32_t*)mappedAddr; + // to update a movw/movt pair we need to extract the 32-bit they will make, + // add the adjust and write back the new movw/movt pair. + if ( lastKind == kind ) { + if ( lastToNewAddress == toNewAddress ) { + uint32_t instruction1 = P::E::get32(*lastMappedAddr32); + uint32_t instruction2 = P::E::get32(*mappedAddr32); + if ( isArmMovw(instruction1) && isArmMovt(instruction2) ) { + uint16_t high = getArmWord(instruction2); + uint16_t low = getArmWord(instruction1); + uint32_t full = high << 16 | low; + full += adjust; + instruction1 = setArmWord(instruction1, full & 0xFFFF); + instruction2 = setArmWord(instruction2, full >> 16); + } + else if ( isArmMovt(instruction1) && isArmMovw(instruction2) ) { + uint16_t high = getArmWord(instruction1); + uint16_t low = getArmWord(instruction2); + uint32_t full = high << 16 | low; + full += adjust; + instruction2 = setArmWord(instruction2, full & 0xFFFF); + instruction1 = setArmWord(instruction1, full >> 16); + } + else { + terminate("two DYLD_CACHE_ADJ_V2_ARM_MOVW_MOVT in a row but not paired in %s", _installName); + } + P::E::set32(*lastMappedAddr32, instruction1); + P::E::set32(*mappedAddr32, instruction2); + kind = 0; + } + else { + terminate("two DYLD_CACHE_ADJ_V2_ARM_MOVW_MOVT in a row but target different addresses in %s", _installName); + } + } + break; + case DYLD_CACHE_ADJ_V2_ARM64_BR26: + case DYLD_CACHE_ADJ_V2_THUMB_BR22: + case DYLD_CACHE_ADJ_V2_ARM_BR24: + // nothing to do with calls to stubs + break; + default: + terminate("unknown split seg kind=%d in %s", kind, _installName); + } + lastKind = kind; + lastToNewAddress = toNewAddress; + lastMappedAddr32 = mappedAddr32; +} + +template +void Adjustor

::adjustReferencesUsingInfoV2(std::vector& pointersForASLR) +{ + static const bool log = false; + + const uint8_t* infoStart = &_linkeditBias[_splitSegInfoCmd->dataoff()]; + const uint8_t* infoEnd = &infoStart[_splitSegInfoCmd->datasize()]; + if ( *infoStart++ != DYLD_CACHE_ADJ_V2_FORMAT ) + terminate("malformed split seg info in %s", _installName); + + // build section arrays of slide and mapped address for each section + std::vector sectionSlides; + std::vector sectionNewAddress; + std::vector sectionMappedAddress; + sectionSlides.reserve(16); + sectionNewAddress.reserve(16); + sectionMappedAddress.reserve(16); + // section index 0 refers to mach_header + sectionMappedAddress.push_back((uint8_t*)_cacheBuffer + _segCacheOffsets[0]); + sectionSlides.push_back(_segSlides[0]); + sectionNewAddress.push_back(_segNewStartAddresses[0]); + // section 1 and later refer to real sections + unsigned sectionIndex = 0; + for (unsigned segmentIndex=0; segmentIndex < _segCmds.size(); ++segmentIndex) { + macho_segment_command

* segCmd = _segCmds[segmentIndex]; + macho_section

* const sectionsStart = (macho_section

*)((char*)segCmd + sizeof(macho_segment_command

)); + macho_section

* const sectionsEnd = §ionsStart[segCmd->nsects()]; + for(macho_section

* sect = sectionsStart; sect < sectionsEnd; ++sect) { + sectionMappedAddress.push_back((uint8_t*)_cacheBuffer + _segCacheOffsets[segmentIndex] + sect->addr() - segCmd->vmaddr()); + sectionSlides.push_back(_segSlides[segmentIndex]); + sectionNewAddress.push_back(_segNewStartAddresses[segmentIndex] + sect->addr() - segCmd->vmaddr()); + if (log) { + fprintf(stderr, " %s/%s, sectIndex=%d, mapped at=%p\n", + sect->segname(), sect->sectname(), sectionIndex, sectionMappedAddress.back()); + } + ++sectionIndex; + } + } + + // Whole :== FromToSection+ + // FromToSection :== ToOffset+ + // ToOffset :== FromOffset+ + // FromOffset :== + const uint8_t* p = infoStart; + uint64_t sectionCount = read_uleb128(p, infoEnd); + for (uint64_t i=0; i < sectionCount; ++i) { + uint32_t* lastMappedAddr32 = NULL; + uint32_t lastKind = 0; + uint64_t lastToNewAddress = 0; + uint64_t fromSectionIndex = read_uleb128(p, infoEnd); + uint64_t toSectionIndex = read_uleb128(p, infoEnd); + uint64_t toOffsetCount = read_uleb128(p, infoEnd); + uint64_t fromSectionSlide = sectionSlides[fromSectionIndex]; + uint64_t fromSectionNewAddress = sectionNewAddress[fromSectionIndex]; + uint8_t* fromSectionMappedAddress = sectionMappedAddress[fromSectionIndex]; + uint64_t toSectionSlide = sectionSlides[toSectionIndex]; + uint64_t toSectionNewAddress = sectionNewAddress[toSectionIndex]; + if (log) printf(" from sect=%lld (mapped=%p), to sect=%lld (new addr=0x%llX):\n", fromSectionIndex, fromSectionMappedAddress, toSectionIndex, toSectionNewAddress); + uint64_t toSectionOffset = 0; + for (uint64_t j=0; j < toOffsetCount; ++j) { + uint64_t toSectionDelta = read_uleb128(p, infoEnd); + uint64_t fromOffsetCount = read_uleb128(p, infoEnd); + toSectionOffset += toSectionDelta; + for (uint64_t k=0; k < fromOffsetCount; ++k) { + uint64_t kind = read_uleb128(p, infoEnd); + if ( kind > 12 ) + terminate("bad kind value (%llu) in %s", kind, _installName); + uint64_t fromSectDeltaCount = read_uleb128(p, infoEnd); + uint64_t fromSectionOffset = 0; + for (uint64_t l=0; l < fromSectDeltaCount; ++l) { + uint64_t delta = read_uleb128(p, infoEnd); + fromSectionOffset += delta; + int64_t deltaAdjust = toSectionSlide - fromSectionSlide; + //if (log) printf(" kind=%lld, from offset=0x%0llX, to offset=0x%0llX, adjust=0x%llX, targetSlide=0x%llX\n", kind, fromSectionOffset, toSectionOffset, deltaAdjust, toSectionSlide); + uint8_t* fromMappedAddr = fromSectionMappedAddress + fromSectionOffset; + uint64_t toNewAddress = toSectionNewAddress + toSectionOffset; + uint64_t fromNewAddress = fromSectionNewAddress + fromSectionOffset; + uint64_t imageStartAddress = sectionNewAddress.front(); + uint64_t imageEndAddress = sectionNewAddress.back(); + if ( toSectionIndex != 255 ) + adjustReference((uint32_t)kind, fromMappedAddr, fromNewAddress, toNewAddress, deltaAdjust, toSectionSlide, imageStartAddress, imageEndAddress, pointersForASLR, lastMappedAddr32, lastKind, lastToNewAddress); + } + } + } + } + +} + +template +void Adjustor

::adjustDataPointers(std::vector& pointersForASLR) +{ + const uint8_t* p = &_linkeditBias[_dyldInfo->rebase_off()]; + const uint8_t* end = &p[_dyldInfo->rebase_size()]; + + uint8_t type = 0; + int segIndex = 0; + uint64_t segOffset = 0; + uint64_t count; + uint64_t skip; + bool done = false; + while ( !done && (p < end) ) { + uint8_t immediate = *p & REBASE_IMMEDIATE_MASK; + uint8_t opcode = *p & REBASE_OPCODE_MASK; + ++p; + switch (opcode) { + case REBASE_OPCODE_DONE: + done = true; + break; + case REBASE_OPCODE_SET_TYPE_IMM: + type = immediate; + break; + case REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: + segIndex = immediate; + segOffset = read_uleb128(p, end); + break; + case REBASE_OPCODE_ADD_ADDR_ULEB: + segOffset += read_uleb128(p, end); + break; + case REBASE_OPCODE_ADD_ADDR_IMM_SCALED: + segOffset += immediate*sizeof(pint_t); + break; + case REBASE_OPCODE_DO_REBASE_IMM_TIMES: + for (int i=0; i < immediate; ++i) { + slidePointer(segIndex, segOffset, type, pointersForASLR); + segOffset += sizeof(pint_t); + } + break; + case REBASE_OPCODE_DO_REBASE_ULEB_TIMES: + count = read_uleb128(p, end); + for (uint32_t i=0; i < count; ++i) { + slidePointer(segIndex, segOffset, type, pointersForASLR); + segOffset += sizeof(pint_t); + } + break; + case REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB: + slidePointer(segIndex, segOffset, type, pointersForASLR); + segOffset += read_uleb128(p, end) + sizeof(pint_t); + break; + case REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB: + count = read_uleb128(p, end); + skip = read_uleb128(p, end); + for (uint32_t i=0; i < count; ++i) { + slidePointer(segIndex, segOffset, type, pointersForASLR); + segOffset += skip + sizeof(pint_t); + } + break; + default: + terminate("unknown rebase opcode 0x%02X in %s", opcode, _installName); + } + } +} + + +template +void Adjustor

::adjustInstruction(uint8_t kind, uint64_t cacheOffset, uint64_t codeToDataDelta) +{ + uint8_t* fixupLoc = (uint8_t*)_cacheBuffer + cacheOffset; + uint32_t* fixupLoc32 = (uint32_t*)fixupLoc; + uint64_t* fixupLoc64 = (uint64_t*)fixupLoc; + uint32_t instruction; + uint32_t value32; + uint64_t value64; + + switch (kind) { + case 1: // 32-bit pointer (including x86_64 RIP-rel) + value32 = P::E::get32(*fixupLoc32); + value32 += codeToDataDelta; + P::E::set32(*fixupLoc32, value32); + break; + case 2: // 64-bit pointer + value64 = P::E::get64(*fixupLoc64); + value64 += codeToDataDelta; + P::E::set64(*fixupLoc64, value64); + break; + case 4: // only used for i386, a reference to something in the IMPORT segment + break; + case 5: // used by thumb2 movw + instruction = P::E::get32(*fixupLoc32); + // slide is always a multiple of 4096, so only top 4 bits of lo16 will ever need adjusting + value32 = (instruction & 0x0000000F) + ((uint32_t)codeToDataDelta >> 12); + instruction = (instruction & 0xFFFFFFF0) | (value32 & 0x0000000F); + P::E::set32(*fixupLoc32, instruction); + break; + case 6: // used by ARM movw + instruction = P::E::get32(*fixupLoc32); + // slide is always a multiple of 4096, so only top 4 bits of lo16 will ever need adjusting + value32 = ((instruction & 0x000F0000) >> 16) + ((uint32_t)codeToDataDelta >> 12); + instruction = (instruction & 0xFFF0FFFF) | ((value32 <<16) & 0x000F0000); + P::E::set32(*fixupLoc32, instruction); + break; + case 0x10: + case 0x11: + case 0x12: + case 0x13: + case 0x14: + case 0x15: + case 0x16: + case 0x17: + case 0x18: + case 0x19: + case 0x1A: + case 0x1B: + case 0x1C: + case 0x1D: + case 0x1E: + case 0x1F: + // used by thumb2 movt (low nibble of kind is high 4-bits of paired movw) + { + instruction = P::E::get32(*fixupLoc32); + assert((instruction & 0x8000FBF0) == 0x0000F2C0); + // extract 16-bit value from instruction + uint32_t i = ((instruction & 0x00000400) >> 10); + uint32_t imm4 = (instruction & 0x0000000F); + uint32_t imm3 = ((instruction & 0x70000000) >> 28); + uint32_t imm8 = ((instruction & 0x00FF0000) >> 16); + uint32_t imm16 = (imm4 << 12) | (i << 11) | (imm3 << 8) | imm8; + // combine with codeToDataDelta and kind nibble + uint32_t targetValue = (imm16 << 16) | ((kind & 0xF) << 12); + uint32_t newTargetValue = targetValue + (uint32_t)codeToDataDelta; + // construct new bits slices + uint32_t imm4_ = (newTargetValue & 0xF0000000) >> 28; + uint32_t i_ = (newTargetValue & 0x08000000) >> 27; + uint32_t imm3_ = (newTargetValue & 0x07000000) >> 24; + uint32_t imm8_ = (newTargetValue & 0x00FF0000) >> 16; + // update instruction to match codeToDataDelta + uint32_t newInstruction = (instruction & 0x8F00FBF0) | imm4_ | (i_ << 10) | (imm3_ << 28) | (imm8_ << 16); + P::E::set32(*fixupLoc32, newInstruction); + } + break; + case 0x20: + case 0x21: + case 0x22: + case 0x23: + case 0x24: + case 0x25: + case 0x26: + case 0x27: + case 0x28: + case 0x29: + case 0x2A: + case 0x2B: + case 0x2C: + case 0x2D: + case 0x2E: + case 0x2F: + // used by arm movt (low nibble of kind is high 4-bits of paired movw) + { + instruction = P::E::get32(*fixupLoc32); + // extract 16-bit value from instruction + uint32_t imm4 = ((instruction & 0x000F0000) >> 16); + uint32_t imm12 = (instruction & 0x00000FFF); + uint32_t imm16 = (imm4 << 12) | imm12; + // combine with codeToDataDelta and kind nibble + uint32_t targetValue = (imm16 << 16) | ((kind & 0xF) << 12); + uint32_t newTargetValue = targetValue + (uint32_t)codeToDataDelta; + // construct new bits slices + uint32_t imm4_ = (newTargetValue & 0xF0000000) >> 28; + uint32_t imm12_ = (newTargetValue & 0x0FFF0000) >> 16; + // update instruction to match codeToDataDelta + uint32_t newInstruction = (instruction & 0xFFF0F000) | (imm4_ << 16) | imm12_; + P::E::set32(*fixupLoc32, newInstruction); + } + break; + case 3: // used for arm64 ADRP + instruction = P::E::get32(*fixupLoc32); + if ( (instruction & 0x9F000000) == 0x90000000 ) { + // codeToDataDelta is always a multiple of 4096, so only top 4 bits of lo16 will ever need adjusting + value64 = ((instruction & 0x60000000) >> 17) | ((instruction & 0x00FFFFE0) << 9); + value64 += codeToDataDelta; + instruction = (instruction & 0x9F00001F) | ((value64 << 17) & 0x60000000) | ((value64 >> 9) & 0x00FFFFE0); + P::E::set32(*fixupLoc32, instruction); + } + break; + default: + break; + } +} + +template +void Adjustor

::adjustCode() +{ + // find compressed info on how code needs to be updated + const uint8_t* infoStart = &_linkeditBias[_splitSegInfoCmd->dataoff()]; + const uint8_t* infoEnd = &infoStart[_splitSegInfoCmd->datasize()];; + + // This encoding only works if all data segments slide by the same amount + uint64_t codeToDataDelta = _segSlides[1] - _segSlides[0]; + + // compressed data is: [ [uleb128-delta]+ <0> ] + <0> + for (const uint8_t* p = infoStart; (*p != 0) && (p < infoEnd);) { + uint8_t kind = *p++; + uint64_t cacheOffset = _segCacheOffsets[0]; + while (uint64_t delta = read_uleb128(p, infoEnd)) { + cacheOffset += delta; + adjustInstruction(kind, cacheOffset, codeToDataDelta); + } + } +} + + +template +void Adjustor

::adjustExportsTrie(std::vector& newTrieBytes) +{ + // if no export info, nothing to adjust + if ( _dyldInfo->export_size() == 0 ) + return; + + // since export info addresses are offsets from mach_header, everything in __TEXT is fine + // only __DATA addresses need to be updated + const uint8_t* start = &_linkeditBias[_dyldInfo->export_off()]; + const uint8_t* end = &start[_dyldInfo->export_size()]; + std::vector originalExports; + if ( !ExportInfoTrie::parseTrie(start, end, originalExports) ) { + terminate("malformed exports trie in %s", _installName); + } + + std::vector newExports; + newExports.reserve(originalExports.size()); + uint64_t baseAddress = _segOrigStartAddresses[0]; + uint64_t baseAddressSlide = slideForOrigAddress(baseAddress); + for (auto& entry: originalExports) { + // remove symbols used by the static linker only + if ( (strncmp(entry.name.c_str(), "$ld$", 4) == 0) + || (strncmp(entry.name.c_str(), ".objc_class_name",16) == 0) + || (strncmp(entry.name.c_str(), ".objc_category_name",19) == 0) ) { + continue; + } + // adjust symbols in slid segments + if ( (entry.info.flags & EXPORT_SYMBOL_FLAGS_KIND_MASK) != EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE ) + entry.info.address += (slideForOrigAddress(entry.info.address + baseAddress) - baseAddressSlide); + newExports.push_back(entry); + } + + // rebuild export trie + newTrieBytes.reserve(_dyldInfo->export_size()); + + ExportInfoTrie(newExports).emit(newTrieBytes); + // align + while ( (newTrieBytes.size() % sizeof(pint_t)) != 0 ) + newTrieBytes.push_back(0); +} + + +} // anonymous namespace + + +void SharedCache::adjustImageForNewSegmentLocations(const std::vector& segNewStartAddresses, + const std::vector& segCacheFileOffsets, + const std::vector& segCacheFileSizes, + std::vector& pointersForASLR) +{ + void* mh = (uint8_t*)_buffer.get() + segCacheFileOffsets[0]; + switch ( _arch.arch ) { + case CPU_TYPE_ARM: + case CPU_TYPE_I386: + { + if ( LittleEndian::get32(*(uint32_t*)mh) != MH_MAGIC ) + return; + Adjustor> adjustor32(_buffer.get(), (macho_header>*)mh, segNewStartAddresses, segCacheFileOffsets, segCacheFileSizes); + adjustor32.adjustImageForNewSegmentLocations(pointersForASLR); + } + break; + case CPU_TYPE_X86_64: + case CPU_TYPE_ARM64: + { + if ( LittleEndian::get32(*(uint32_t*)mh) != MH_MAGIC_64 ) + return; + Adjustor> adjustor64(_buffer.get(), (macho_header>*)mh, segNewStartAddresses, segCacheFileOffsets, segCacheFileSizes); + adjustor64.adjustImageForNewSegmentLocations(pointersForASLR); + } + break; + default: + terminate("unsupported arch 0x%08X", _arch.arch); + } +} + + + + + diff --git a/src/dyld-apple/interlinked-dylibs/BindAllImages.cpp b/src/dyld-apple/interlinked-dylibs/BindAllImages.cpp new file mode 100644 index 000000000..8bd9e6e1e --- /dev/null +++ b/src/dyld-apple/interlinked-dylibs/BindAllImages.cpp @@ -0,0 +1,761 @@ +/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- + * + * Copyright (c) 2014 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@ + */ + +#include "mega-dylib-utils.h" +#include "MachOFileAbstraction.hpp" +#include "Trie.hpp" +#include "Logging.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "dyld_cache_config.h" + +#if !NEW_CACHE_FILE_FORMAT + #include "CacheFileAbstraction.hpp" +#endif + +#ifndef EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE + #define EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE 0x02 +#endif + + +namespace { + +template +class BindInfo { +public: + BindInfo(void* cacheBuffer, macho_header

* mh); + + void setReExports(const std::unordered_map*>& dylibPathToBindInfo); + void setDependentDylibs(const std::unordered_map*>& dylibPathToBindInfo); + void bind(const std::unordered_map*>& dylibPathToBindInfo, std::vector& pointersForASLR); + + static void bindAllImagesInCache(void* cacheBuffer, const std::unordered_map& dylibPathToMachHeader, std::vector& pointersForASLR); + + void addExportsToGlobalMap(std::unordered_map*>& reverseMap); + +private: + typedef typename P::uint_t pint_t; + typedef typename P::E E; + + struct SymbolInfo { + SymbolInfo() { } + pint_t address = 0; + bool isResolver = false; + bool isAbsolute = false; + bool isSymbolReExport = false; + bool isThreadLocal = false; + int reExportDylibIndex = 0; + std::string reExportName; + }; + + void bindImmediates(const std::unordered_map*>& dylibPathToBindInfo, std::vector& pointersForASLR); + void bindLazyPointers(const std::unordered_map*>& dylibPathToBindInfo, std::vector& pointersForASLR); + + void bindLocation(uint8_t segmentIndex, uint64_t segmentOffset, uint8_t type, int libraryOrdinal, + int64_t addend, const char* symbolName, bool lazyPointer, bool weakImport, + const std::unordered_map*>& dylibPathToBindInfo, std::vector& pointersForASLR); + + bool findExportedSymbolAddress(const char* symbolName, const std::unordered_map*>& dylibPathToBindInfo, + pint_t* address, BindInfo

** foundIn, bool* isResolverSymbol, bool* isAbsolute); + pint_t findBlessedLazyPointerFor(const std::string& resolverSymbolName); + void switchStubToUseSharedLazyPointer(const std::string& resolverSymbolName, pint_t lpVMAddr); + void switchArmStubsLazyPointer(uint8_t* stubMappedAddress, pint_t stubVMAddress, uint32_t stubSize, pint_t lpVMAddr); + void switchArm64StubsLazyPointer(uint8_t* stubMappedAddress, pint_t stubVMAddress, uint32_t stubSize, pint_t lpVMAddr); + + typedef std::unordered_map*>> ResolverClientsMap; + typedef std::unordered_map ResolverToBlessedLazyPointerMap; + + void* _cacheBuffer; + macho_header

* _mh; + const uint8_t* _linkeditBias; + const char* _installName; + const macho_symtab_command

* _symTabCmd; + const macho_dysymtab_command

* _dynSymTabCmd; + const macho_dyld_info_command

* _dyldInfo; + std::vector _dependentPaths; + std::vector _segSizes; + std::vector _segCacheOffsets; + std::vector*>_segCmds; + std::unordered_map _exports; + std::vector _reExportedDylibNames; + std::vector*> _reExportedDylibs; + std::vector*> _dependentDylibs; + pint_t _baseAddress; + ResolverClientsMap _resolverClients; + ResolverToBlessedLazyPointerMap _resolverBlessedMap; +}; + + +template +BindInfo

::BindInfo(void* cacheBuffer, macho_header

* mh) + : _cacheBuffer(cacheBuffer), _mh(mh), _linkeditBias((uint8_t*)cacheBuffer), _symTabCmd(nullptr), _dynSymTabCmd(nullptr), _dyldInfo(nullptr), _baseAddress(0) +{ + macho_segment_command

* segCmd; + macho_dylib_command

* dylibCmd; + const macho_load_command

* const cmds = (macho_load_command

*)((uint8_t*)mh + sizeof(macho_header

)); + const uint32_t cmd_count = mh->ncmds(); + unsigned segIndex = 0; + const macho_load_command

* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd()) { + case LC_ID_DYLIB: + dylibCmd = (macho_dylib_command

*)cmd; + _installName = dylibCmd->name(); + break; + case LC_SYMTAB: + _symTabCmd = (macho_symtab_command

*)cmd; + break; + case LC_DYSYMTAB: + _dynSymTabCmd = (macho_dysymtab_command

*)cmd; + break; + case LC_DYLD_INFO: + case LC_DYLD_INFO_ONLY: + _dyldInfo = (macho_dyld_info_command

*)cmd; + break; + case LC_REEXPORT_DYLIB: + dylibCmd = (macho_dylib_command

*)cmd; + _dependentPaths.push_back(dylibCmd->name()); + _reExportedDylibNames.push_back(dylibCmd->name()); + break; + case LC_LOAD_DYLIB: + case LC_LOAD_WEAK_DYLIB: + case LC_LOAD_UPWARD_DYLIB: + dylibCmd = (macho_dylib_command

*)cmd; + _dependentPaths.push_back(dylibCmd->name()); + break; + case macho_segment_command

::CMD: + segCmd = (macho_segment_command

*)cmd; + _segCmds.push_back(segCmd); + _segSizes.push_back(segCmd->vmsize()); + _segCacheOffsets.push_back(segCmd->fileoff()); + if ( segIndex == 0 ) + _baseAddress = (pint_t)segCmd->vmaddr(); + ++segIndex; + break; + } + cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); + } + + // if no export info, no _exports map to build + if ( _dyldInfo->export_size() == 0 ) + return; + + std::vector exports; + const uint8_t* exportsStart = &_linkeditBias[_dyldInfo->export_off()]; + const uint8_t* exportsEnd = &exportsStart[_dyldInfo->export_size()]; + if ( !ExportInfoTrie::parseTrie(exportsStart, exportsEnd, exports) ) { + terminate("malformed exports trie in %s", _installName); + } + + for(const ExportInfoTrie::Entry& entry : exports) { + _exports[entry.name].address = (pint_t)entry.info.address + _baseAddress; + switch ( entry.info.flags & EXPORT_SYMBOL_FLAGS_KIND_MASK ) { + case EXPORT_SYMBOL_FLAGS_KIND_REGULAR: + if ( (entry.info.flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER) ) { + _exports[entry.name].isResolver = true; + } + if ( entry.info.flags & EXPORT_SYMBOL_FLAGS_REEXPORT ) { + SymbolInfo& info = _exports[entry.name]; + info.isSymbolReExport = true; + info.reExportDylibIndex = (int)entry.info.other; + if ( !entry.info.importName.empty()) + info.reExportName = entry.info.importName; + } + break; + case EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL: + _exports[entry.name].isThreadLocal = true; + break; + case EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE: + _exports[entry.name].isAbsolute = true; + _exports[entry.name].address = (pint_t)entry.info.address; + break; + default: + terminate("non-regular symbol binding not supported for %s in %s", entry.name.c_str(), _installName); + break; + } + } + +} + +template +void BindInfo

::bind(const std::unordered_map*>& dylibPathToBindInfo, std::vector& pointersForASLR) +{ + bindImmediates(dylibPathToBindInfo, pointersForASLR); + bindLazyPointers(dylibPathToBindInfo, pointersForASLR); + // weak bind info is processed at launch time +} + + +template +void BindInfo

::setReExports(const std::unordered_map*>& dylibPathToBindInfo) +{ + for (const std::string& depName : _reExportedDylibNames) { + auto pos = dylibPathToBindInfo.find(depName); + if ( pos == dylibPathToBindInfo.end() ) { + terminate("can't find re-exported dylib '%s' needed by '%s'", depName.c_str(), _installName); + } + _reExportedDylibs.push_back(pos->second); + } +} + +template +void BindInfo

::setDependentDylibs(const std::unordered_map*>& dylibPathToBindInfo) +{ + for (const std::string& depName : _dependentPaths) { + auto pos = dylibPathToBindInfo.find(depName); + if ( pos == dylibPathToBindInfo.end() ) { + terminate("can't find dependent dylib '%s' needed by '%s'", depName.c_str(), _installName); + } + _dependentDylibs.push_back(pos->second); + } +} + + +template +void BindInfo

::bindImmediates(const std::unordered_map*>& dylibPathToBindInfo, std::vector& pointersForASLR) +{ + const uint8_t* p = &_linkeditBias[_dyldInfo->bind_off()]; + const uint8_t* end = &p[_dyldInfo->bind_size()]; + + uint8_t type = 0; + uint64_t segmentOffset = 0; + uint8_t segmentIndex = 0; + const char* symbolName = NULL; + int libraryOrdinal = 0; + int64_t addend = 0; + uint64_t count; + uint64_t skip; + bool weakImport = false; + bool done = false; + while ( !done && (p < end) ) { + uint8_t immediate = *p & BIND_IMMEDIATE_MASK; + uint8_t opcode = *p & BIND_OPCODE_MASK; + ++p; + switch (opcode) { + case BIND_OPCODE_DONE: + done = true; + break; + case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM: + libraryOrdinal = immediate; + break; + case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB: + libraryOrdinal = (int)read_uleb128(p, end); + break; + case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM: + // the special ordinals are negative numbers + if ( immediate == 0 ) + libraryOrdinal = 0; + else { + int8_t signExtended = BIND_OPCODE_MASK | immediate; + libraryOrdinal = signExtended; + } + break; + case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: + weakImport = ( (immediate & BIND_SYMBOL_FLAGS_WEAK_IMPORT) != 0 ); + symbolName = (char*)p; + while (*p != '\0') + ++p; + ++p; + break; + case BIND_OPCODE_SET_TYPE_IMM: + type = immediate; + break; + case BIND_OPCODE_SET_ADDEND_SLEB: + addend = read_sleb128(p, end); + break; + case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: + segmentIndex = immediate; + segmentOffset = read_uleb128(p, end); + break; + case BIND_OPCODE_ADD_ADDR_ULEB: + segmentOffset += read_uleb128(p, end); + break; + case BIND_OPCODE_DO_BIND: + bindLocation(segmentIndex, segmentOffset, type, libraryOrdinal, addend, symbolName, false, weakImport, dylibPathToBindInfo, pointersForASLR); + segmentOffset += sizeof(pint_t); + break; + case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: + bindLocation(segmentIndex, segmentOffset, type, libraryOrdinal, addend, symbolName, false, weakImport, dylibPathToBindInfo, pointersForASLR); + segmentOffset += read_uleb128(p, end) + sizeof(pint_t); + break; + case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: + bindLocation(segmentIndex, segmentOffset, type, libraryOrdinal, addend, symbolName, false, weakImport, dylibPathToBindInfo, pointersForASLR); + segmentOffset += immediate*sizeof(pint_t) + sizeof(pint_t); + break; + case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: + count = read_uleb128(p, end); + skip = read_uleb128(p, end); + for (uint32_t i=0; i < count; ++i) { + bindLocation(segmentIndex, segmentOffset, type, libraryOrdinal, addend, symbolName, false, weakImport, dylibPathToBindInfo, pointersForASLR); + segmentOffset += skip + sizeof(pint_t); + } + break; + default: + terminate("bad bind opcode 0x%02X in %s", *p, _installName); + } + } + +} + +template +void BindInfo

::bindLazyPointers(const std::unordered_map*>& dylibPathToBindInfo, std::vector& pointersForASLR) +{ + const uint8_t* p = &_linkeditBias[_dyldInfo->lazy_bind_off()]; + const uint8_t* end = &p[_dyldInfo->lazy_bind_size()]; + + uint8_t type = BIND_TYPE_POINTER; + uint64_t segmentOffset = 0; + uint8_t segmentIndex = 0; + const char* symbolName = NULL; + int libraryOrdinal = 0; + int64_t addend = 0; + bool weakImport = false; + while ( p < end ) { + uint8_t immediate = *p & BIND_IMMEDIATE_MASK; + uint8_t opcode = *p & BIND_OPCODE_MASK; + ++p; + switch (opcode) { + case BIND_OPCODE_DONE: + // this opcode marks the end of each lazy pointer binding + break; + case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM: + libraryOrdinal = immediate; + break; + case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB: + libraryOrdinal = (int)read_uleb128(p, end); + break; + case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM: + // the special ordinals are negative numbers + if ( immediate == 0 ) + libraryOrdinal = 0; + else { + int8_t signExtended = BIND_OPCODE_MASK | immediate; + libraryOrdinal = signExtended; + } + break; + case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: + weakImport = ( (immediate & BIND_SYMBOL_FLAGS_WEAK_IMPORT) != 0 ); + symbolName = (char*)p; + while (*p != '\0') + ++p; + ++p; + break; + case BIND_OPCODE_SET_ADDEND_SLEB: + addend = read_sleb128(p, end); + break; + case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: + segmentIndex = immediate; + segmentOffset = read_uleb128(p, end); + break; + case BIND_OPCODE_DO_BIND: + bindLocation(segmentIndex, segmentOffset, type, libraryOrdinal, addend, symbolName, true, weakImport, dylibPathToBindInfo, pointersForASLR); + segmentOffset += sizeof(pint_t); + break; + case BIND_OPCODE_SET_TYPE_IMM: + case BIND_OPCODE_ADD_ADDR_ULEB: + case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: + case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: + case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: + default: + terminate("bad lazy bind opcode 0x%02X in %s", opcode, _installName); + } + } + +} + + +template +void BindInfo

::bindLocation(uint8_t segmentIndex, uint64_t segmentOffset, uint8_t type, int libraryOrdinal, + int64_t addend, const char* symbolName, bool lazyPointer, bool weakImport, + const std::unordered_map*>& dylibPathToBindInfo, std::vector& pointersForASLR) +{ + //printf("bindLocation: seg=%d, segOffset=0x%08llX, type=%d, lib=%d, addend=%lld, symbol=%s\n", segmentIndex, segmentOffset, type, libraryOrdinal, addend, symbolName); + if ( segmentIndex > _segSizes.size() ) + terminate("bad segment index in bind info in %s", _installName); + + if ( segmentOffset > _segSizes[segmentIndex] ) + terminate("bad segment offset in bind info in %s", _installName); + + BindInfo

* targetBinder = nullptr; + std::string depName; + switch ( libraryOrdinal ) { + case BIND_SPECIAL_DYLIB_FLAT_LOOKUP: + terminate("dynamic lookup linkage not allowed in dyld shared cache in %s", _installName); + break; + + case BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE: + terminate("linkage to main executable not allowed in dyld shared cache in %s", _installName); + break; + + case BIND_SPECIAL_DYLIB_SELF: + targetBinder = this; + break; + + default: + if ( libraryOrdinal < 0 ) + terminate("bad mach-o binary, special library ordinal not allowd in dyld shared cache in %s", _installName); + if ( (unsigned)libraryOrdinal > _dependentPaths.size() ) + terminate("bad mach-o binary, library ordinal too big in %s", _installName); + depName = _dependentPaths[libraryOrdinal-1]; + auto pos = dylibPathToBindInfo.find(depName); + if ( pos != dylibPathToBindInfo.end() ) + targetBinder = pos->second; + break; + } + + pint_t targetSymbolAddress; + bool isResolverSymbol = false; + bool isAbsolute = false; + BindInfo

* foundIn; + if ( weakImport && (targetBinder == nullptr) ) { + targetSymbolAddress = 0; + foundIn = nullptr; + } + else { + if (targetBinder == nullptr) + terminate("could not bind symbol '%s' used in '%s' because installname '%s' not found", symbolName, _installName, depName.c_str()); + if ( ! targetBinder->findExportedSymbolAddress(symbolName, dylibPathToBindInfo, &targetSymbolAddress, &foundIn, &isResolverSymbol, &isAbsolute) ) + terminate("could not bind symbol '%s' used in: %s expected in: %s", symbolName, _installName, targetBinder->_installName); + } + + //if ( isResolverSymbol ) + // fprintf(stderr, "found resolver based symbol '%s' in %s\n", symbolName, targetBinder->_installName); + // don't bind lazy pointers to resolvers in shared cache + if ( lazyPointer && isResolverSymbol ) { + // instead find common lazy pointer that can be re-used by all clients + pint_t lpVMAddr = targetBinder->findBlessedLazyPointerFor(symbolName); + // switch stub to use shared lazy pointer to reduce dirty pages + this->switchStubToUseSharedLazyPointer(symbolName, lpVMAddr); + return; + } + + + // do actual update + uint8_t* mappedAddr = (uint8_t*)_cacheBuffer + _segCacheOffsets[segmentIndex] + segmentOffset; + pint_t* mappedAddrP = (pint_t*)mappedAddr; + pint_t newValue = (pint_t)(targetSymbolAddress + addend); + switch ( type ) { + case BIND_TYPE_POINTER: + // only write new value if it will change it + // this reduces pages dirtied + if ( P::getP(*mappedAddrP) != newValue ) + P::setP(*mappedAddrP, newValue); + break; + + case BIND_TYPE_TEXT_ABSOLUTE32: + case BIND_TYPE_TEXT_PCREL32: + terminate("text relocs not supported for shared cache binding in %s", _installName); + break; + + default: + terminate("bad bind type (%d) in %s", type, _installName); + } + if ( !isAbsolute ) + pointersForASLR.push_back(mappedAddr); +} + + +template +bool BindInfo

::findExportedSymbolAddress(const char* symbolName, const std::unordered_map*>& dylibPathToBindInfo, + pint_t* address, BindInfo

** foundIn, bool* isResolverSymbol, bool* isAbsolute) +{ + auto pos = _exports.find(symbolName); + if ( pos != _exports.end() ) { + if ( pos->second.isSymbolReExport ) { + const char* importName = symbolName; + if ( !pos->second.reExportName.empty() ) + importName = pos->second.reExportName.c_str(); + std::string& depPath = _dependentPaths[pos->second.reExportDylibIndex-1]; + auto pos2 = dylibPathToBindInfo.find(depPath); + if ( pos2 != dylibPathToBindInfo.end() ) { + BindInfo

* reExportFrom = pos2->second; + return reExportFrom->findExportedSymbolAddress(importName, dylibPathToBindInfo, address, foundIn, isResolverSymbol, isAbsolute); + } + else { + verboseLog("findExportedSymbolAddress(%s) => ???\n", symbolName); + } + } + *address = pos->second.address; + *foundIn = this; + *isResolverSymbol = pos->second.isResolver; + *isAbsolute = pos->second.isAbsolute; + //verboseLog("findExportedSymbolAddress(%s) => 0x0%llX\n", symbolName, (uint64_t)*address); + return true; + } + + for (BindInfo

* dep : _reExportedDylibs) { + if ( dep->findExportedSymbolAddress(symbolName, dylibPathToBindInfo, address, foundIn, isResolverSymbol, isAbsolute) ) + return true; + } + return false; +} + +template +void BindInfo

::addExportsToGlobalMap(std::unordered_map*>& reverseMap) +{ + for (const auto& expEntry : _exports) { + const std::string& symName = expEntry.first; + auto pos = reverseMap.find(symName); + if ( pos == reverseMap.end() ) { + reverseMap[symName] = this; + } + else { + BindInfo

* other = pos->second; + if ( expEntry.second.isSymbolReExport ) + continue; + if ( other->_exports[symName].isSymbolReExport ) + continue; + //warning("symbol '%s' exported from %s and %s\n", symName.c_str(), this->_installName, other->_installName); + } + } +} + +template +typename P::uint_t BindInfo

::findBlessedLazyPointerFor(const std::string& resolverSymbolName) +{ + static const bool log = false; + + // check if this has already been looked up + auto pos1 = _resolverBlessedMap.find(resolverSymbolName); + if ( pos1 != _resolverBlessedMap.end() ) { + return pos1->second; + } + + // if this symbol is re-exported from another dylib, look there + bool thisDylibImplementsResolver = false; + auto pos = _exports.find(resolverSymbolName); + if ( pos != _exports.end() ) { + const SymbolInfo& info = pos->second; + if ( info.isSymbolReExport ) { + std::string reImportName = resolverSymbolName; + if ( !info.reExportName.empty() ) + reImportName = info.reExportName; + if ( info.reExportDylibIndex > _dependentDylibs.size() ) { + warning("dylib index for re-exported symbol %s too large (%d) in %s", resolverSymbolName.c_str(), info.reExportDylibIndex, _installName); + } + else { + BindInfo

* reExportedFrom = _dependentDylibs[info.reExportDylibIndex-1]; + if ( log ) verboseLog( "following re-export of %s in %s, to %s in %s", resolverSymbolName.c_str(), _installName, reImportName.c_str(), reExportedFrom->_installName); + pint_t lp = reExportedFrom->findBlessedLazyPointerFor(reImportName); + if ( lp != 0 ) { + _resolverBlessedMap[resolverSymbolName] = lp; + return lp; + } + } + } + if ( info.isResolver ) + thisDylibImplementsResolver = true; + } + + // lookup in lazy pointer section + if ( thisDylibImplementsResolver ) { + const uint32_t* const indirectTable = (uint32_t*)&_linkeditBias[_dynSymTabCmd->indirectsymoff()]; + const macho_nlist

* const symbolTable = (macho_nlist

*)(&_linkeditBias[_symTabCmd->symoff()]); + const char* symbolStringPool = (char*)(&_linkeditBias[_symTabCmd->stroff()]); + + for (const macho_segment_command

* seg : _segCmds) { + const macho_section

* const sectionsStart = (macho_section

*)((uint8_t*)seg + sizeof(macho_segment_command

)); + const macho_section

* const sectionsEnd = §ionsStart[seg->nsects()]; + for (const macho_section

* sect=sectionsStart; sect < sectionsEnd; ++sect) { + uint8_t sectionType = sect->flags() & SECTION_TYPE; + if ( sectionType == S_LAZY_SYMBOL_POINTERS) { + uint32_t elementCount = (uint32_t)(sect->size() / sizeof(pint_t)); + const uint32_t indirectTableOffset = sect->reserved1(); + pint_t vmlocation = (pint_t)sect->addr(); + for (uint32_t j=0; j < elementCount; ++j, vmlocation += sizeof(pint_t)) { + uint32_t symbolIndex = E::get32(indirectTable[indirectTableOffset + j]); + switch ( symbolIndex ) { + case INDIRECT_SYMBOL_ABS: + case INDIRECT_SYMBOL_LOCAL: + break; + default: + const macho_nlist

* aSymbol = &symbolTable[symbolIndex]; + const char* aName = &symbolStringPool[aSymbol->n_strx()]; + if ( resolverSymbolName == aName) { + if ( log ) verboseLog("found shared lazy pointer at 0x%llX for %s in %s in %s", (uint64_t)vmlocation, aName, sect->sectname(), _installName); + _resolverBlessedMap[resolverSymbolName] = vmlocation; + return vmlocation; + } + break; + } + } + } + } + } + } + + if ( log ) verboseLog( "not found shared lazy pointer for %s in %s, checking re-export dylibs", resolverSymbolName.c_str(), _installName); + for (BindInfo

* reExportedDylib : _reExportedDylibs ) { + pint_t result = reExportedDylib->findBlessedLazyPointerFor(resolverSymbolName); + if ( result != 0 ) { + _resolverBlessedMap[resolverSymbolName] = result; + return result; + } + } + + if ( log ) verboseLog( "NOT found shared lazy pointer for %s in %s", resolverSymbolName.c_str(), _installName); + return 0; +} + +template +void BindInfo

::switchStubToUseSharedLazyPointer(const std::string& resolverSymbolName, pint_t lpVMAddr) +{ + // find named stub + const uint32_t* const indirectTable = (uint32_t*)&_linkeditBias[_dynSymTabCmd->indirectsymoff()]; + const macho_nlist

* const symbolTable = (macho_nlist

*)(&_linkeditBias[_symTabCmd->symoff()]); + const char* symbolStringPool = (char*)(&_linkeditBias[_symTabCmd->stroff()]); + for (const macho_segment_command

* seg : _segCmds) { + macho_section

* const sectionsStart = (macho_section

*)((char*)seg + sizeof(macho_segment_command

)); + macho_section

* const sectionsEnd = §ionsStart[seg->nsects()]; + for(macho_section

* sect = sectionsStart; sect < sectionsEnd; ++sect) { + if ( ((sect->flags() & SECTION_TYPE) == S_SYMBOL_STUBS) && (sect->size() != 0) ) { + pint_t stubsVMStart = (pint_t)sect->addr(); + uint8_t* stubsMappingStart = ((uint8_t*)_cacheBuffer) + sect->offset(); + const uint32_t indirectTableOffset = sect->reserved1(); + const uint32_t stubSize = sect->reserved2(); + uint32_t elementCount = (uint32_t)(sect->size() / stubSize); + pint_t stubVMAddr = stubsVMStart; + uint8_t* stubMappedAddr = stubsMappingStart; + for (uint32_t j=0; j < elementCount; ++j, stubMappedAddr += stubSize, stubVMAddr += stubSize) { + uint32_t symbolIndex = E::get32(indirectTable[indirectTableOffset + j]); + switch ( symbolIndex ) { + case INDIRECT_SYMBOL_ABS: + case INDIRECT_SYMBOL_LOCAL: + break; + default: + { + const macho_nlist

* aSymbol = &symbolTable[symbolIndex]; + const char* stubName = &symbolStringPool[aSymbol->n_strx()]; + if ( resolverSymbolName == stubName ) { + switch (_mh->cputype()) { + case CPU_TYPE_ARM: + switchArmStubsLazyPointer(stubMappedAddr, stubVMAddr, stubSize, lpVMAddr); + break; + default: + //warning("shared resolver lazy pointer to %s not implemented for this arch", resolverSymbolName.c_str()); + break; + } + } + } + break; + } + } + } + } + } +} + +template +void BindInfo

::switchArmStubsLazyPointer(uint8_t* stubMappedAddress, pint_t stubVMAddress, uint32_t stubSize, pint_t lpVMAddr) +{ + if ( stubSize != 16 ) { + warning("could not optimize ARM stub to resolver function in %s because it is wrong size\n", _installName); + return; + } + uint32_t* instructions = (uint32_t*)stubMappedAddress; + if ( (E::get32(instructions[0]) != 0xe59fc004) + || (E::get32(instructions[1]) != 0xe08fc00c) + || (E::get32(instructions[2]) != 0xe59cf000) + ) { + warning("could not optimize ARM stub to resolver function in %s because instructions are not as expected", _installName); + return; + } + // last .long in stub is: lazyPtr - (stub+8) + // alter to point to more optimal lazy pointer + uint32_t betterOffset = (uint32_t)(lpVMAddr - (stubVMAddress + 12)); + E::set32(instructions[3], betterOffset); +} + + +template +void BindInfo

::bindAllImagesInCache(void* cacheBuffer, const std::unordered_map& dylibPathToMachHeader, std::vector& pointersForASLR) +{ + // build BindInfo object for each dylib + std::unordered_map*, BindInfo

*> headersToBindInfo; + std::unordered_map*> dylibPathToBindInfo; + for (const auto& entry: dylibPathToMachHeader) { + macho_header

* mh = (macho_header

*)entry.second; + if ( headersToBindInfo.count(mh) == 0 ) + headersToBindInfo[mh] = new BindInfo

(cacheBuffer, mh); + dylibPathToBindInfo[entry.first] = headersToBindInfo[mh]; + } + + // chain re-exported dylibs + for (const auto& entry: headersToBindInfo) { + entry.second->setDependentDylibs(dylibPathToBindInfo); + entry.second->setReExports(dylibPathToBindInfo); + } + + // bind each dylib + for (const auto& entry: headersToBindInfo) { + entry.second->bind(dylibPathToBindInfo, pointersForASLR); + } + + // look for exported symbol collisions + std::unordered_map*> reverseMap; + for (const auto& entry: headersToBindInfo) { + entry.second->addExportsToGlobalMap(reverseMap); + } + + // clean up + for (const auto& entry: headersToBindInfo) { + delete entry.second; + } +} + + +} // anonymous namespace + + +void SharedCache::bindAllImagesInCache(const std::unordered_map& dylibPathToMachHeader, std::vector& pointersForASLR) +{ + switch ( _arch.arch ) { + case CPU_TYPE_ARM: + case CPU_TYPE_I386: + BindInfo>::bindAllImagesInCache(_buffer.get(), dylibPathToMachHeader, pointersForASLR); + break; + case CPU_TYPE_X86_64: + case CPU_TYPE_ARM64: + BindInfo>::bindAllImagesInCache(_buffer.get(), dylibPathToMachHeader, pointersForASLR); + break; + default: + terminate("unsupported arch 0x%08X", _arch.arch); + } +} + + + diff --git a/src/dyld-apple/interlinked-dylibs/CodeSigningTypes.h b/src/dyld-apple/interlinked-dylibs/CodeSigningTypes.h new file mode 100644 index 000000000..e4a4049b1 --- /dev/null +++ b/src/dyld-apple/interlinked-dylibs/CodeSigningTypes.h @@ -0,0 +1,130 @@ +/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- + * + * Copyright (c) 2015 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@ + */ + +#ifndef _CODE_SIGNING_TYPES_ +#define _CODE_SIGNING_TYPES_ + +#include +#include + + +// +// Magic numbers used by Code Signing +// +enum { + CSMAGIC_REQUIREMENT = 0xfade0c00, // single Requirement blob + CSMAGIC_REQUIREMENTS = 0xfade0c01, // Requirements vector (internal requirements) + CSMAGIC_CODEDIRECTORY = 0xfade0c02, // CodeDirectory blob + CSMAGIC_EMBEDDED_SIGNATURE = 0xfade0cc0, // embedded form of signature data + CSMAGIC_DETACHED_SIGNATURE = 0xfade0cc1, // multi-arch collection of embedded signatures + CSMAGIC_BLOBWRAPPER = 0xfade0b01, // used for the cms blob +}; + +enum { + CS_PAGE_SIZE = 4096, + + CS_HASHTYPE_SHA1 = 1, + CS_HASHTYPE_SHA256 = 2, + CS_HASHTYPE_SHA256_TRUNCATED = 3, + + CS_HASH_SIZE_SHA1 = 20, + CS_HASH_SIZE_SHA256 = 32, + CS_HASH_SIZE_SHA256_TRUNCATED = 20, + + CSSLOT_CODEDIRECTORY = 0, + CSSLOT_INFOSLOT = 1, + CSSLOT_REQUIREMENTS = 2, + CSSLOT_RESOURCEDIR = 3, + CSSLOT_APPLICATION = 4, + CSSLOT_ENTITLEMENTS = 5, + CSSLOT_CMS_SIGNATURE = 0x10000, + + kSecCodeSignatureAdhoc = 2 +}; + + + +// +// Structure of a SuperBlob +// +struct CS_BlobIndex { + uint32_t type; // type of entry + uint32_t offset; // offset of entry +}; + +struct CS_SuperBlob { + uint32_t magic; // magic number + uint32_t length; // total length of SuperBlob + uint32_t count; // number of index entries following + CS_BlobIndex index[]; // (count) entries + // followed by Blobs in no particular order as indicated by offsets in index +}; + +// +// C form of a CodeDirectory. +// +struct CS_CodeDirectory { + uint32_t magic; // magic number (CSMAGIC_CODEDIRECTORY) */ + uint32_t length; // total length of CodeDirectory blob + uint32_t version; // compatibility version + uint32_t flags; // setup and mode flags + uint32_t hashOffset; // offset of hash slot element at index zero + uint32_t identOffset; // offset of identifier string + uint32_t nSpecialSlots; // number of special hash slots + uint32_t nCodeSlots; // number of ordinary (code) hash slots + uint32_t codeLimit; // limit to main image signature range + uint8_t hashSize; // size of each hash in bytes + uint8_t hashType; // type of hash (cdHashType* constants) + uint8_t platform; // platform identifier; zero if not platform binary + uint8_t pageSize; // log2(page size in bytes); 0 => infinite + uint32_t spare2; // unused (must be zero) + // Version 0x20100 or later + uint32_t scatterOffset; // offset of optional scatter vector + // followed by dynamic content as located by offset fields above +}; + +struct CS_Blob { + uint32_t magic; // magic number + uint32_t length; // total length of blob +}; + +struct CS_RequirementsBlob { + uint32_t magic; // magic number + uint32_t length; // total length of blob + uint32_t data; // zero for dyld shared cache +}; + + +struct CS_Scatter { + uint32_t count; // number of pages; zero for sentinel (only) + uint32_t base; // first page number + uint64_t targetOffset; // byte offset in target + uint64_t spare; // reserved (must be zero) +}; + + +#endif // _CODE_SIGNING_TYPES_ + + + diff --git a/src/dyld-apple/interlinked-dylibs/FileCache.cpp b/src/dyld-apple/interlinked-dylibs/FileCache.cpp new file mode 100644 index 000000000..3ccb9a181 --- /dev/null +++ b/src/dyld-apple/interlinked-dylibs/FileCache.cpp @@ -0,0 +1,234 @@ +/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- + * + * Copyright (c) 2014 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@ + */ + + +#include "mega-dylib-utils.h" +#include "MachOFileAbstraction.hpp" +#include "Trie.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "dyld_cache_config.h" + +#include "OptimizerBranches.h" + +#include "CacheFileAbstraction.hpp" + +#include "mega-dylib-utils.h" +#include "Logging.h" + + +//#include +extern "C" int rootless_check_trusted(const char *path); +extern "C" int rootless_check_trusted_fd(int fd) __attribute__((weak_import)); + +static bool rootlessEnabled; +static dispatch_once_t onceToken; + +bool isProtectedBySIP(const std::string& path, int fd) +{ + bool isProtected = false; + // Check to make sure file system protections are on at all + dispatch_once(&onceToken, ^{ + rootlessEnabled = csr_check(CSR_ALLOW_UNRESTRICTED_FS); + }); + if (!rootlessEnabled) + return false; +#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101200 + if ( (fd != -1) && (&rootless_check_trusted_fd != NULL) ) + isProtected = (rootless_check_trusted_fd(fd) == 0); + else +#endif + if ( &rootless_check_trusted != NULL ) + isProtected = (rootless_check_trusted(path.c_str()) == 0); + return isProtected; +} + + +std::string toolDir() +{ + char buffer[PATH_MAX]; + uint32_t bufsize = PATH_MAX; + int result = _NSGetExecutablePath(buffer, &bufsize); + if ( result == 0 ) { + std::string path = buffer; + size_t pos = path.rfind('/'); + if ( pos != std::string::npos ) + return path.substr(0,pos+1); + } + warning("tool directory not found"); + return "/tmp/"; +} + +std::string baspath(const std::string& path) +{ + std::string::size_type slash_pos = path.rfind("/"); + if (slash_pos != std::string::npos) { + slash_pos++; + return path.substr(slash_pos); + } else { + return path; + } +} + +std::string dirpath(const std::string& path) +{ + std::string::size_type slash_pos = path.rfind("/"); + if (slash_pos != std::string::npos) { + slash_pos++; + return path.substr(0, slash_pos); + } else { + char cwd[MAXPATHLEN]; + (void)getcwd(cwd, MAXPATHLEN); + return cwd; + } +} + +std::string normalize_absolute_file_path(const std::string &path) { + std::vector components; + std::vector processed_components; + std::stringstream ss(path); + std::string retval; + std::string item; + + while (std::getline(ss, item, '/')) { + components.push_back(item); + } + + if (components[0] == ".") { + retval = "."; + } + + for (auto& component : components) { + if (component.empty() || component == ".") + continue; + else if (component == ".." && processed_components.size()) + processed_components.pop_back(); + else + processed_components.push_back(component); + } + + for (auto & component : processed_components) { + retval = retval + "/" + component; + } + + return retval; +} + +FileCache fileCache; + +FileCache::FileCache(void) { + cache_queue = dispatch_queue_create("com.apple.dyld.cache.cache", DISPATCH_QUEUE_SERIAL); +} + + +void FileCache::preflightCache(const std::unordered_set& paths) { + for (auto &path : paths) { + preflightCache(path); + } +} + +void FileCache::preflightCache(const std::string& path) +{ + cacheBuilderDispatchAsync(cache_queue, [=] { + std::string normalizedPath = normalize_absolute_file_path(path); + if (entries.count(normalizedPath) == 0) { + fill(normalizedPath); + } + }); +} + +std::tuple FileCache::cacheLoad(const std::string path) { + std::string normalizedPath = normalize_absolute_file_path(path); + cacheBuilderDispatchSync(cache_queue, [=] { + if ( entries.count(normalizedPath) == 0 ) + fill(normalizedPath); + }); + + return entries[normalizedPath]; +} + + +//FIXME error handling +void FileCache::fill(const std::string& path) { + struct stat stat_buf; + + int fd = ::open(path.c_str(), O_RDONLY, 0); + if ( fd == -1 ) { + verboseLog("can't open file '%s', errno=%d", path.c_str(), errno); + entries[path] = std::make_tuple((uint8_t*)(-1), stat_buf, false); + return; + } + + if ( fstat(fd, &stat_buf) == -1) { + verboseLog("can't stat open file '%s', errno=%d", path.c_str(), errno); + entries[path] = std::make_tuple((uint8_t*)(-1), stat_buf, false); + ::close(fd); + return; + } + + if ( stat_buf.st_size < 4096 ) { + verboseLog("file too small '%s'", path.c_str()); + entries[path] = std::make_tuple((uint8_t*)(-1), stat_buf, false); + ::close(fd); + return; + } + + bool rootlessProtected = isProtectedBySIP(path, fd); + + void* buffer_ptr = mmap(NULL, stat_buf.st_size, PROT_READ, MAP_PRIVATE, fd, 0); + if (buffer_ptr == MAP_FAILED) { + verboseLog("mmap() for shared cache at %s failed, errno=%d", path.c_str(), errno); + entries[path] = std::make_tuple((uint8_t*)(-1), stat_buf, false); + ::close(fd); + return; + } + + entries[path] = std::make_tuple((uint8_t*)buffer_ptr, stat_buf, rootlessProtected); + ::close(fd); +} + + + + + + diff --git a/src/dyld-apple/interlinked-dylibs/Logging.cpp b/src/dyld-apple/interlinked-dylibs/Logging.cpp new file mode 100644 index 000000000..0f744efb5 --- /dev/null +++ b/src/dyld-apple/interlinked-dylibs/Logging.cpp @@ -0,0 +1,257 @@ +/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- + * + * Copyright (c) 2016 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@ + */ + +#include +#include +#include +#include + +#include +#include "mega-dylib-utils.h" + +#include "Logging.h" + +#define MAX_LOG_STR_LEN (512) +//const char* kDispatchQueueLogNameKey = "kDispatchQueueLogNameKey"; +const char* kDispatchWarningArrayKey = "kDispatchWarningArrayKey"; + +static dispatch_queue_t log_queue; +static dispatch_once_t logQueueInit = 0; +static dispatch_queue_t unique_queue; +static dispatch_once_t uniqueQueueInit = 0; + +static uint32_t verbose = 0; +static bool returnNonZeroIfTerminateCalled = false; +static bool terminateCalled = false; + +static const char* warningPrefix = "WARNING: "; +static const char* errorPrefix = "ERROR: "; + +LoggingContext::LoggingContext(const std::string& N) + : _name(N) + , _tainted(false) +{ +} + +LoggingContext::LoggingContext(const std::string& N, WarningTargets T) + : _name(N) + , _warnings(T) + , _tainted(false) +{ +} + +void LoggingContext::taint() +{ + _tainted = true; +} + +bool LoggingContext::isTainted() +{ + return _tainted; +} + +const std::string& LoggingContext::name() +{ + return _name; +} + +const WarningTargets& LoggingContext::targets() +{ + return _warnings; +} + + + +pthread_key_t getLoggingContextKey(void) { + static pthread_key_t logContextKey; + static dispatch_once_t logContextToken; + dispatch_once(&logContextToken, ^{ + pthread_key_create(&logContextKey, nullptr); + }); + return logContextKey; +} + + +void setLoggingContext(std::shared_ptr& context) +{ + pthread_setspecific(getLoggingContextKey(), (void*)&context); + + if (context && !context->name().empty()) { + pthread_setname_np(context->name().substr(0, MAXTHREADNAMESIZE-1).c_str()); + } +} + +std::shared_ptr getLoggingContext() +{ + if (void* val = pthread_getspecific(getLoggingContextKey())) + return *((std::shared_ptr*)val); + return nullptr; +} + +void runBody(void* Ctx) +{ + std::unique_ptr> + Body(reinterpret_cast*>(Ctx)); + (*Body)(); +} + +static dispatch_queue_t getLogQueue() +{ + dispatch_once(&logQueueInit, ^{ + log_queue = dispatch_queue_create("com.apple.dyld.cache.logging", DISPATCH_QUEUE_SERIAL); + }); + return log_queue; +} + +void setVerbose(bool level) +{ + verbose = level; +} + +void setWarnAnErrorPrefixes(const char* warn, const char* err) +{ + warningPrefix = warn; + errorPrefix = err; +} + +void setReturnNonZeroOnTerminate() +{ + returnNonZeroIfTerminateCalled = true; +} + +void queued_print(FILE* __restrict fd, const char* str) +{ + const char* qstr = strdup(str); + + dispatch_async(getLogQueue(), ^{ + (void)fprintf(fd, "%s", qstr); + free((void*)qstr); + }); +} + +#define VLOG(header) \ + va_list list; \ + va_start(list, format); \ + char temp[MAX_LOG_STR_LEN]; \ + vsprintf(temp, format, list); \ + auto ctx = getLoggingContext(); \ + char temp2[MAX_LOG_STR_LEN]; \ + if (ctx && !ctx->name().empty()) { \ + snprintf(temp2, MAX_LOG_STR_LEN, "[%s] %s%s\n", ctx->name().c_str(), header, \ + temp); \ + } else { \ + snprintf(temp2, MAX_LOG_STR_LEN, "%s%s\n", header, temp); \ + } \ + queued_print(stderr, temp2); \ + va_end(list); + +void log(const char* __restrict format, ...) +{ + VLOG(""); +} + +void verboseLog(const char* format, ...) +{ + if (verbose) { + VLOG(""); + } +} + +static std::set warnings; + +void warning(const char* format, ...) +{ + dispatch_once(&uniqueQueueInit, ^{ + unique_queue = dispatch_queue_create("com.apple.dyld.cache.logging", DISPATCH_QUEUE_SERIAL); + }); + + va_list list; + va_start(list, format); + char temp[MAX_LOG_STR_LEN]; + vsprintf(temp, format, list); + char* blockTemp = strdup(temp); + + auto ctx = getLoggingContext(); + if (ctx) { + for (auto& target : ctx->targets().second) { + ctx->targets().first->configurations[target.first].architectures[target.second].results.warnings.push_back(blockTemp); + } + } + + dispatch_sync(unique_queue, ^{ + if (warnings.count(blockTemp) == 0) { + warnings.insert(blockTemp); + } + + free(blockTemp); + }); + + va_end(list); +} + +void terminate(const char* format, ...) +{ + VLOG(errorPrefix); + + terminateCalled = true; + + if (ctx) { + // We are a work in a logging context, throw + throw std::string(temp); + } else { + // We are in general handing, let the loggging queue darain and exit + dispatch_sync(getLogQueue(), ^{ + for (auto& warning : warnings) { + (void)fprintf(stderr, "%s%s\n", warningPrefix, warning.c_str()); + } + if ( returnNonZeroIfTerminateCalled ) { + exit(1); + } + else { + time_t endtime = time(0); + (void)fprintf(stderr, "Finished: %s\n", asctime(localtime(&endtime))); + (void)fprintf(stderr, "Exiting\n"); + exit(0); + } + }); + } + + // clang can't reason out that we won't hit this due to the dispatch_sync in the exit path + __builtin_unreachable(); +} + +void dumpLogAndExit(bool logFinishTime) +{ + dispatch_async(getLogQueue(), ^{ + for (auto& warning : warnings) { + (void)fprintf(stderr, "%s%s\n", warningPrefix, warning.c_str()); + } + if ( logFinishTime ) { + time_t endtime = time(0); + (void)fprintf(stderr, "Finished: %s\n", asctime(localtime(&endtime))); + (void)fprintf(stderr, "Exiting\n"); + } + exit(returnNonZeroIfTerminateCalled && terminateCalled ? 1 : 0); + }); +} diff --git a/src/dyld-apple/interlinked-dylibs/Logging.h b/src/dyld-apple/interlinked-dylibs/Logging.h new file mode 100644 index 000000000..db4c992f7 --- /dev/null +++ b/src/dyld-apple/interlinked-dylibs/Logging.h @@ -0,0 +1,132 @@ +/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- + * + * Copyright (c) 2016 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@ + */ + +#ifndef LOGGING_H +#define LOGGING_H + +#include +#include +#include "mega-dylib-utils.h" + +void verboseLog(const char* format, ...) __printflike(1, 2); +void log(const char* __restrict format, ...) __printflike(1, 2); +void alwaysLog(const char* __restrict format, ...) __printflike(1, 2); + +void warning(const char* format, ...) __printflike(1, 2); +void terminate(const char* format, ...) __printflike(1, 2) __attribute__((noreturn)); +void dumpLogAndExit(bool logFinishTime=true); + +void setVerbose(bool level); +void setReturnNonZeroOnTerminate(); +void setWarnAnErrorPrefixes(const char* warn, const char* err); + +struct LoggingContext { + LoggingContext(const std::string& N); + LoggingContext(const std::string& N, WarningTargets T); + void taint(); + bool isTainted(); + const std::string& name(); + const WarningTargets& targets(); + +private: + const std::string _name; + const WarningTargets _warnings; + bool _tainted; +}; + +void setLoggingContext(std::shared_ptr& context); +std::shared_ptr getLoggingContext(); + +/* Okay, so this gets tricky + * Naively, what we are doing is stashing some information in pthread specific + * variables so that the logging system can pick it up and tag messages with it, + * but without us having to track it all through the entire cache builder code base. + * Additionally, we use the presence of that information to determine if we are in + * the root logging context (where terminate() calls are fatal), or a thread context + * (where they should just throw so the thread fails, logs an error, and cleans up its + * state. + * + * The problem is that we need that context to follow our blocks when we switch threads. + * We achieve that by wrapping dispatch_(a)sync with our own calls that setup try{} blocks + * around the executing lambda, that way it is always safe to throw in a named context + * name. We also use those wrappers to copy the context between the threads using + * the closures as glue. Finally, we set a taint variable that can back propgate to stop + * the execution of any furthur blocks related to a context that has thrown. + * + * This is exposed in the header because we need it for the templates to work, but aside from + * cacheBuilderDispatchAsync() and friends nothing here should be used directly. + */ + +void runBody(void* Ctx); + +pthread_key_t getLoggingContextKey(void); + +template +std::function* heapSafe(BodyFtor&& Body, std::shared_ptr context) +{ + auto retval = new std::function([ B = std::move(Body), context ]() mutable { + if (!context || !context->isTainted()) { + + void* oldCtx = pthread_getspecific(getLoggingContextKey()); + setLoggingContext(context); + try { + B(); + } catch (std::string exception) { + WarningTargets warningTargets = context->targets(); + for (auto& target : warningTargets.second) { + warningTargets.first->configurations[target.first].architectures[target.second].results.failure = exception; + } + if (context) { + context->taint(); + } + } catch (...) { + if (context) { + context->taint(); + } + } + pthread_setspecific(getLoggingContextKey(), oldCtx); + } + }); + return retval; +} + +template +void cacheBuilderDispatchAsync(dispatch_queue_t queue, BodyFtor&& Body) +{ + dispatch_async_f(queue, heapSafe(Body, getLoggingContext()), runBody); +} + +template +void cacheBuilderDispatchGroupAsync(dispatch_group_t group, dispatch_queue_t queue, BodyFtor&& Body) +{ + dispatch_group_async_f(group, queue, heapSafe(Body, getLoggingContext()), runBody); +} + +template +void cacheBuilderDispatchSync(dispatch_queue_t queue, BodyFtor&& Body) +{ + dispatch_sync_f(queue, heapSafe(Body, getLoggingContext()), runBody); +} + +#endif /* LOGGING_H */ diff --git a/src/dyld-apple/interlinked-dylibs/MachOProxy.cpp b/src/dyld-apple/interlinked-dylibs/MachOProxy.cpp new file mode 100644 index 000000000..e1923f80b --- /dev/null +++ b/src/dyld-apple/interlinked-dylibs/MachOProxy.cpp @@ -0,0 +1,254 @@ +// +// DylibProxy.cpp +// dyld +// +// Created by Louis Gerbarg on 1/27/16. +// +// + +#include +#include + +#include "mega-dylib-utils.h" +#include "Logging.h" + +#include "MachOProxy.h" + +namespace { +std::map mapMachOFile( const std::string& path ) { + std::map retval; + const uint8_t* p = (uint8_t*)( -1 ); + struct stat stat_buf; + bool rootless; + + std::tie( p, stat_buf, rootless ) = fileCache.cacheLoad( path ); + + if ( p == (uint8_t*)( -1 ) ) { + return retval; + } + + // if fat file, process each architecture + const fat_header* fh = (fat_header*)p; + const mach_header* mh = (mach_header*)p; + if ( OSSwapBigToHostInt32( fh->magic ) == FAT_MAGIC ) { + // Fat header is always big-endian + const fat_arch* slices = (const fat_arch*)( (char*)fh + sizeof( fat_header ) ); + const uint32_t sliceCount = OSSwapBigToHostInt32( fh->nfat_arch ); + for ( uint32_t i = 0; i < sliceCount; ++i ) { + // FIXME Should we validate the fat header matches the slices? + ArchPair arch( OSSwapBigToHostInt32( slices[i].cputype ), OSSwapBigToHostInt32( slices[i].cpusubtype ) ); + uint32_t fileOffset = OSSwapBigToHostInt32( slices[i].offset ); + const mach_header* th = (mach_header*)(p+fileOffset); + if ( ( OSSwapLittleToHostInt32( th->magic ) == MH_MAGIC ) || ( OSSwapLittleToHostInt32( th->magic ) == MH_MAGIC_64 ) ) { + uint32_t fileSize = static_cast( stat_buf.st_size ); + retval[stringForArch( arch )] = new MachOProxy( path, stat_buf.st_ino, stat_buf.st_mtime, fileOffset, fileSize, rootless ); + } + } + } else if ( ( OSSwapLittleToHostInt32( mh->magic ) == MH_MAGIC ) || ( OSSwapLittleToHostInt32( mh->magic ) == MH_MAGIC_64 ) ) { + ArchPair arch( OSSwapLittleToHostInt32( mh->cputype ), OSSwapLittleToHostInt32( mh->cpusubtype ) ); + uint32_t fileOffset = OSSwapBigToHostInt32( 0 ); + uint32_t fileSize = static_cast( stat_buf.st_size ); + retval[stringForArch( arch )] = new MachOProxy( path, stat_buf.st_ino, stat_buf.st_mtime, fileOffset, fileSize, rootless ); + } else { + // warning( "file '%s' is not contain requested a MachO", path.c_str() ); + } + return retval; +} +} + +template +std::string MachOProxy::machoParser(bool ignoreUncacheableDylibsInExecutables) +{ + const uint8_t* buffer = getBuffer(); + bool hasSplitSegInfo = false; + bool hasDylidInfo = false; + const macho_header

* mh = (const macho_header

*)buffer; + const macho_symtab_command

* symTab = nullptr; + const macho_dysymtab_command

* dynSymTab = nullptr; + const macho_load_command

* const cmds = (macho_load_command

*)((uint8_t*)mh + sizeof(macho_header

)); + const uint32_t cmd_count = mh->ncmds(); + const macho_load_command

* cmd = cmds; + _dylib = (mh->filetype() == MH_DYLIB); + _executable = (mh->filetype() == MH_EXECUTE); + if (mh->filetype() == MH_DYLIB_STUB) { + return "stub dylib"; + } + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd()) { + case LC_ID_DYLIB: { + macho_dylib_command

* dylib = (macho_dylib_command

*)cmd; + if (dylib->name()[0] != '/') { + if (strncmp(dylib->name(), "@rpath", 6) == 0) + return "@rpath cannot be used in -install_name for OS dylibs"; + else + return "-install_name is not an absolute path"; + } + installName = dylib->name(); + installNameOffsetInTEXT = (uint32_t)((uint8_t*)cmd - buffer) + dylib->name_offset(); + } break; + case LC_UUID: { + const macho_uuid_command

* uuidCmd = (macho_uuid_command

*)cmd; + ::memcpy(uuid, uuidCmd->uuid(), sizeof(uuid_t)); + } break; + case LC_LOAD_DYLIB: + case LC_LOAD_WEAK_DYLIB: + case LC_REEXPORT_DYLIB: + case LC_LOAD_UPWARD_DYLIB: { + macho_dylib_command

* dylib = (macho_dylib_command

*)cmd; + std::string depName = dylib->name(); + if ( _executable && ignoreUncacheableDylibsInExecutables && !has_prefix(depName, "/usr/lib/") && !has_prefix(depName, "/System/Library/") ) { + // in update_dyld_shared_cache don't warn if root executable links with something not eligible for shared cache + break; + } + else if ( depName[0] != '/' ) { + return "linked against a dylib whose -install_name was non-absolute (e.g. @rpath)"; + } + dependencies.insert(depName); + } break; + case macho_segment_command

::CMD: { + const macho_segment_command

* segCmd = (macho_segment_command

*)cmd; + MachOProxy::Segment seg; + seg.name = segCmd->segname(); + seg.size = align(segCmd->vmsize(), 12); + seg.diskSize = (uint32_t)segCmd->filesize(); + seg.fileOffset = (uint32_t)segCmd->fileoff(); + seg.protection = segCmd->initprot(); + if (segCmd->nsects() > 0) { + seg.p2align = 0; + const macho_section

* const sectionsStart = (macho_section

*)((uint8_t*)segCmd + sizeof(macho_segment_command

)); + const macho_section

* const sectionsLast = §ionsStart[segCmd->nsects() - 1]; + const macho_section

* const sectionsEnd = §ionsStart[segCmd->nsects()]; + for (const macho_section

* sect = sectionsStart; sect < sectionsEnd; ++sect) { + if (sect->align() > seg.p2align) + seg.p2align = sect->align(); + } + seg.sizeOfSections = sectionsLast->addr() + sectionsLast->size() - segCmd->vmaddr(); + } else { + seg.p2align = 12; + } + segments.push_back(seg); + } break; + case LC_SEGMENT_SPLIT_INFO: + hasSplitSegInfo = true; + break; + case LC_SYMTAB: + symTab = (macho_symtab_command

*)cmd; + break; + case LC_DYSYMTAB: + dynSymTab = (macho_dysymtab_command

*)cmd; + break; + case LC_DYLD_INFO: + case LC_DYLD_INFO_ONLY: + hasDylidInfo = true; + break; + } + cmd = (const macho_load_command

*)(((uint8_t*)cmd) + cmd->cmdsize()); + } + + if (!_dylib) { + return ""; + } + + if (!hasDylidInfo) { + return "built for old OS"; + } + + if ((mh->flags() & MH_TWOLEVEL) == 0) { + return "built with -flat_namespace"; + } + + if (!hasSplitSegInfo) { + bool inUsrLib = (installName.size() > 9) && (installName.substr(0, 9) == "/usr/lib/"); + bool inSystemLibrary = (installName.size() > 16) && (installName.substr(0, 16) == "/System/Library/"); + if (!inUsrLib && !inSystemLibrary) { + return "-install_name not /usr/lib/* or /System/Library/*"; + } + return "no shared region info"; + } + + if ((symTab == nullptr) && (dynSymTab == nullptr)) { + return "no symbol table"; + } + + if (installName.empty()) { + return "dylib missing install name"; + } + + // scan undefines looking for invalid ordinals + const macho_nlist

* symbolTable = (macho_nlist

*)((uint8_t*)mh + symTab->symoff()); + const uint32_t startUndefs = dynSymTab->iundefsym(); + const uint32_t endUndefs = startUndefs + dynSymTab->nundefsym(); + for (uint32_t i = startUndefs; i < endUndefs; ++i) { + uint8_t ordinal = GET_LIBRARY_ORDINAL(symbolTable[i].n_desc()); + if (ordinal == DYNAMIC_LOOKUP_ORDINAL) { + return "built with '-undefined dynamic_lookup'"; + } else if (ordinal == EXECUTABLE_ORDINAL) { + return "built with -bundle_loader"; + } + } + + return ""; +} + +const bool MachOProxy::isDylib() +{ + return _dylib; +} + +const bool MachOProxy::isExecutable() +{ + return _executable; +} + +std::map MachOProxy::findDylibInfo(const std::string& path, bool warnOnProblems, bool ignoreUncacheableDylibsInExecutables) { + std::map slices = mapMachOFile( path ); + std::vector errorSlices; + + for ( auto& slice : slices ) { + std::string errorMessage; + verboseLog( "analyzing file '%s'", path.c_str() ); + switch ( archForString( slice.first ).arch ) { + case CPU_TYPE_ARM: + case CPU_TYPE_I386: + errorMessage = slice.second->machoParser>(ignoreUncacheableDylibsInExecutables); + break; + case CPU_TYPE_X86_64: + case CPU_TYPE_ARM64: + errorMessage = slice.second->machoParser>(ignoreUncacheableDylibsInExecutables); + break; + default: + errorMessage = "unsupported arch '" + slice.first + "'"; + break; + } + + if ( !errorMessage.empty() ) { + if ( warnOnProblems ) + warning( "%s (%s)", errorMessage.c_str(), path.c_str() ); + errorSlices.push_back( slice.first ); + } + } + + for ( const auto& slice : errorSlices ) { + slices.erase( slice ); + } + + return slices; +} + +const uint8_t* MachOProxy::getBuffer() { + const uint8_t* p = (uint8_t*)( -1 ); + struct stat stat_buf; + bool rootless; + std::tie(p, stat_buf,rootless) = fileCache.cacheLoad(path); + return p + fatFileOffset; +} + +bool MachOProxy::addAlias( const std::string& alias ) { + if (!has_prefix(alias, "/usr/lib/") && !has_prefix(alias, "/System/Library/")) + return false; + if ( alias != installName && installNameAliases.count( alias ) == 0 ) { + installNameAliases.insert( alias ); + return true; + } + return false; +} diff --git a/src/dyld-apple/interlinked-dylibs/MachOProxy.h b/src/dyld-apple/interlinked-dylibs/MachOProxy.h new file mode 100644 index 000000000..7a7f620b8 --- /dev/null +++ b/src/dyld-apple/interlinked-dylibs/MachOProxy.h @@ -0,0 +1,66 @@ +// +// DylibProxy.h +// dyld +// +// Created by Louis Gerbarg on 1/27/16. +// +// + +#ifndef MachOProxy_h +#define MachOProxy_h + +#include +#include +#include +#include +#include + +#include + +struct MachOProxy { + MachOProxy(const std::string& p, ino_t i, time_t t, uint32_t o, uint32_t s, bool r) : + path(p), fatFileOffset(o), fileSize(s), lastModTime(t), inode(i), installNameOffsetInTEXT(0), rootlessProtected(r) { + bzero( &uuid, sizeof( uuid_t ) ); + } + + struct Segment { + std::string name; + uint64_t size; + uint64_t sizeOfSections; + uint32_t diskSize; + uint32_t fileOffset; + uint8_t p2align; + uint8_t protection; + }; + + const std::string path; + const uint32_t fatFileOffset; + const uint32_t fileSize; + const time_t lastModTime; + const ino_t inode; + const bool rootlessProtected; + std::string installName; + std::set installNameAliases; + uint32_t installNameOffsetInTEXT; + std::set dependencies; + std::set dependents; + uuid_t uuid; + std::vector segments; + + const uint8_t* getBuffer(); + const bool isDylib(); + const bool isExecutable(); + bool addAlias(const std::string& alias); + + static std::map findDylibInfo(const std::string& path, bool warnOnProblems=false, bool ignoreUncacheableDylibsInExecutables=false); + +private: + bool _dylib; + bool _executable; + + template + std::string machoParser(bool ignoreUncacheableDylibsInExecutables); +}; + + +#endif /* MachOProxy_h */ diff --git a/src/dyld-apple/interlinked-dylibs/Manifest.h b/src/dyld-apple/interlinked-dylibs/Manifest.h new file mode 100644 index 000000000..bface3ccd --- /dev/null +++ b/src/dyld-apple/interlinked-dylibs/Manifest.h @@ -0,0 +1,208 @@ +// +// Manifest.h +// dyld +// +// Created by Louis Gerbarg on 7/23/15. +// +// + +#ifndef Manifest_h +#define Manifest_h + +#include +#include +#include +#include + +#include +#include + + +struct SharedCache; +struct MachOProxy; +struct Manifest; + +struct Manifest { + struct Project { + std::vector sources; + }; + + struct File { + MachOProxy* proxy; + + File( MachOProxy* P ) : proxy( P ) {} + }; + + struct FileSet { + std::map dylibs; + std::map executables; + }; + + struct Anchor { + std::string installname; + bool required; + + Anchor( const std::string& IN ) : installname( IN ) {} + }; + + struct SegmentInfo { + std::string name; + uint64_t startAddr; + uint64_t endAddr; + }; + + struct SegmentInfoHasher { + std::size_t operator()(const SegmentInfo &x) const { + return std::hash()(x.name) ^ std::hash()(x.startAddr) ^ std::hash()(x.endAddr); + } + }; + + struct CacheInfo { + std::vector regions; + std::string cdHash; + }; + + struct DylibInfo { + bool included; + std::string exclusionInfo; + uuid_t uuid; + std::vector segments; + DylibInfo(void) : included(true) {} + void exclude(const std::string& reason) { + included = false; + exclusionInfo = reason; + } + }; + + struct Results { + std::string failure; + std::map dylibs; + std::vector warnings; + CacheInfo developmentCache; + CacheInfo productionCache; + }; + + struct Architecture { + std::vector anchors; + mutable Results results; + //FIXME: Gross + std::unordered_map> dependents; + + uint64_t hash(void) const { + if (!_hash) { + for (auto& dylib : results.dylibs) { + if (dylib.second.included) { + _hash ^= std::hash()(dylib.first); + //HACK to get some of the UUID into the hash + _hash ^= std::hash()(*(uint64_t *)(&dylib.second.uuid[0])); + } + }; + } + + return _hash; + } + + bool equivalent(const Architecture& O) const { + if (hash() != O.hash()) { + return false; + } + for (auto& dylib : results.dylibs) { + if (dylib.second.included) { + auto Odylib = O.results.dylibs.find(dylib.first); + if (Odylib == O.results.dylibs.end() + || Odylib->second.included == false + || memcmp(&Odylib->second.uuid[0], &dylib.second.uuid[0], sizeof(uuid_t)) != 0) + return false; + } + } + //Iterate over O.results to make sure we included all the same things + for (auto Odylib : O.results.dylibs) { + if (Odylib.second.included) { + auto dylib = results.dylibs.find(Odylib.first); + if (dylib == results.dylibs.end() + || dylib->second.included == false) + return false; + } + } + return true; + } + private: + mutable uint64_t _hash = 0; + }; + + struct Configuration { + std::string platformName; + std::string metabomTag; + std::set metabomExcludeTags; + std::set metabomRestrictTags; + std::set restrictedInstallnames; + std::map architectures; + + uint64_t hash( void ) const { + if (!_hash) { + _hash ^= std::hash()(architectures.size()); + // We want the preliminary info here to make dedupe decisions + for (auto& arch : architectures) { + _hash ^= arch.second.hash(); + }; + } + return _hash; + } + + //Used for dedupe + bool equivalent(const Configuration& O) const { + if (hash() != O.hash()) + return false; + for (const auto& arch : architectures) { + if (O.architectures.count(arch.first) == 0) + return false; + if (!arch.second.equivalent(O.architectures.find(arch.first)->second)) + return false; + } + + return true; + } + private: + mutable uint64_t _hash = 0; + }; + + std::map architectureFiles; + std::map projects; + std::map configurations; + std::string dylibOrderFile; + std::string dirtyDataOrderFile; + std::string metabomFile; + std::string build; + // FIXME every needs to adopt platform string for v5 + std::string platform; + uint32_t manifest_version; + bool normalized; + + Manifest( void ) {} +#if BOM_SUPPORT + Manifest( const std::string& path ); + Manifest( const std::string& path, const std::set& overlays ); +#endif + void write( const std::string& path ); + void canonicalize( void ); + void calculateClosure( bool enforeceRootless ); + void pruneClosure(); + bool sameContentsAsCacheAtPath( const std::string& configuration, const std::string& architecture, + const std::string& path ) const; + MachOProxy* dylibProxy( const std::string& installname, const std::string& arch ); + MachOProxy* removeLargestLeafDylib( const std::string& configuration, const std::string& architecture ); + +private: + void removeDylib( MachOProxy* proxy, const std::string& reason, const std::string& configuration, const std::string& architecture, + std::unordered_set& processedInstallnames ); + File* dylibForInstallName( const std::string& installname, const std::string& arch ); + void calculateClosure( const std::string& configuration, const std::string& architecture); + void pruneClosure(const std::string& configuration, const std::string& architecture); + void canonicalizeDylib( const std::string& installname ); + template + void canonicalizeDylib( const std::string& installname, const uint8_t* p ); + void addImplicitAliases( void ); +}; + + +#endif /* Manifest_h */ diff --git a/src/dyld-apple/interlinked-dylibs/Manifest.mm b/src/dyld-apple/interlinked-dylibs/Manifest.mm new file mode 100644 index 000000000..34e81ecf1 --- /dev/null +++ b/src/dyld-apple/interlinked-dylibs/Manifest.mm @@ -0,0 +1,740 @@ +// +// Manifest.mm +// dyld +// +// Created by Louis Gerbarg on 7/23/15. +// +// + +#if BOM_SUPPORT +extern "C" { +#include +#include +#include +#include +#include +}; +#endif /* BOM_SUPPORT */ + +#include + +#include +#include + +#include "MachOFileAbstraction.hpp" +#include "FileAbstraction.hpp" +#include "Trie.hpp" +#include "Logging.h" + +#include +#include + +#include +#include + +#include "Manifest.h" +#include "dsc_iterator.h" + +#include "mega-dylib-utils.h" + +namespace { + //FIXME this should be in a class + static bool rootless = true; + + static inline NSString* cppToObjStr(const std::string& str) { return [NSString stringWithUTF8String:str.c_str()]; } + +#if BOM_SUPPORT + std::string effectivePath(const std::string source, const std::string target) + { + if (target[0] == '/') + return normalize_absolute_file_path(target); + + std::size_t found = source.find_last_of('/'); + return normalize_absolute_file_path(source.substr(0, found) + "/" + target); + } + + std::string checkSymlink(const std::string path, const std::pair& symlink, const std::set& directories) + { + if (directories.count(symlink.second) == 0) { + if (path == symlink.second) + return symlink.first; + } else { + auto res = std::mismatch(symlink.second.begin(), symlink.second.end(), path.begin()); + if (res.first == symlink.second.end()) { + std::string alias = normalize_absolute_file_path(symlink.first + std::string(res.second, path.end())); + return alias; + } + } + return ""; + } +#endif + } + +#if BOM_SUPPORT + + Manifest::Manifest(const std::string& path) + : Manifest(path, std::set()) + { + } + + Manifest::Manifest(const std::string& path, const std::set& overlays) + { + NSMutableDictionary* manifestDict = [NSMutableDictionary dictionaryWithContentsOfFile:cppToObjStr(path)]; + std::map metabomTagMap; + std::map> metabomExcludeTagMap; + std::map> metabomRestrictedTagMap; + metabomFile = [manifestDict[@"metabomFile"] UTF8String]; + + for (NSString* project in manifestDict[@"projects"]) { + for (NSString* source in manifestDict[@"projects"][project]) { + projects[[project UTF8String]].sources.push_back([source UTF8String]); + } + } + + for (NSString* configuration in manifestDict[@"configurations"]) { + std::string configStr = [configuration UTF8String]; + std::string configTag = [manifestDict[@"configurations"][configuration][@"metabomTag"] UTF8String]; + metabomTagMap[configTag] = configStr; + + if (manifestDict[@"configurations"][configuration][@"metabomExcludeTags"]) { + for (NSString* excludeTag in manifestDict[@"configurations"][configuration][@"metabomExcludeTags"]) { + metabomExcludeTagMap[configStr].insert([excludeTag UTF8String]); + configurations[configStr].metabomExcludeTags.insert([excludeTag UTF8String]); + } + } + + if (manifestDict[@"configurations"][configuration][@"metabomRestrictTags"]) { + for (NSString* restrictTag in manifestDict[@"configurations"][configuration][@"metabomRestrictTags"]) { + metabomRestrictedTagMap[configStr].insert([restrictTag UTF8String]); + configurations[configStr].metabomRestrictTags.insert([restrictTag UTF8String]); + } + } + + configurations[configStr].metabomTag = configTag; + configurations[configStr].platformName = + [manifestDict[@"configurations"][configuration][@"platformName"] UTF8String]; + } + + manifest_version = [manifestDict[@"manifest-version"] unsignedIntValue]; + build = [manifestDict[@"build"] UTF8String]; + if (manifestDict[@"dylibOrderFile"]) { + dylibOrderFile = [manifestDict[@"dylibOrderFile"] UTF8String]; + } + if (manifestDict[@"dirtyDataOrderFile"]) { + dirtyDataOrderFile = [manifestDict[@"dirtyDataOrderFile"] UTF8String]; + } + + auto metabom = MBMetabomOpen(metabomFile.c_str(), false); + auto metabomEnumerator = MBIteratorNewWithPath(metabom, ".", ""); + MBEntry entry; + + std::map symlinks; + std::set directories; + + // FIXME error handling (NULL metabom) + + while ((entry = MBIteratorNext(metabomEnumerator))) { + auto fsObject = MBEntryGetFSObject(entry); + std::string entryPath = BOMFSObjectPathName(fsObject); + if (entryPath[0] == '.') { + entryPath.erase(0, 1); + } + auto entryType = BOMFSObjectType(fsObject); + + switch (entryType) { + case BOMFileType: { + MBTag tag; + auto tagCount = MBEntryGetNumberOfProjectTags(entry); + + if (!BOMFSObjectIsBinaryObject(fsObject)) + break; + + if (tagCount == 0) { + break; + } else if (tagCount == 1) { + MBEntryGetProjectTags(entry, &tag); + } else { + MBTag* tags = (MBTag*)malloc(sizeof(MBTag) * tagCount); + MBEntryGetProjectTags(entry, tags); + + //Sigh, we can have duplicate entries for the same tag, so build a set to work with + std::set tagStrs; + std::map tagStrMap; + for (auto i = 0; i < tagCount; ++i) { + tagStrs.insert(MBMetabomGetProjectForTag(metabom, tags[i])); + tagStrMap.insert(std::make_pair(MBMetabomGetProjectForTag(metabom, tags[i]), tags[i])); + } + + if (tagStrs.size() > 1) { + std::string projects; + for (const auto& tagStr : tagStrs) { + if (!projects.empty()) + projects += ", "; + + projects += "'" + tagStr + "'"; + } + warning("Bom entry '%s' is claimed by multiple projects: %s, taking first entry", entryPath.c_str(), projects.c_str()); + } + tag = tagStrMap[*tagStrs.begin()]; + free(tags); + } + + std::string projectName = MBMetabomGetProjectForTag(metabom, tag); + + // FIXME we need to actually walk down the searchpaths + auto project = projects.find(projectName); + if (project == projects.end()) + break; + if (project->second.sources.size() == 0) + break; + std::string projectPath = project->second.sources[0]; + std::map proxies; + + for (const auto& overlay : overlays) { + proxies = MachOProxy::findDylibInfo(overlay + "/" + entryPath); + if (proxies.size() > 0) + break; + } + + if (proxies.size() == 0) { + proxies = MachOProxy::findDylibInfo(projectPath + "/" + entryPath); + } + + for (auto& proxy : proxies) { + assert(proxy.second != nullptr); + if (proxy.second->isExecutable()) { + architectureFiles[proxy.first].executables.insert(std::make_pair(entryPath, File(proxy.second))); + continue; + } + if (!proxy.second->isDylib()) + continue; + assert(proxy.second->installName != ""); + proxy.second->addAlias(entryPath); + architectureFiles[proxy.first].dylibs.insert( + std::make_pair(proxy.second->installName, File(proxy.second))); + auto tagCount = MBEntryGetNumberOfPackageTags(entry); + MBTag* tags = (MBTag*)malloc(sizeof(MBTag) * tagCount); + MBEntryGetPackageTags(entry, tags); + std::set tagStrs; + + for (auto i = 0; i < tagCount; ++i) { + tagStrs.insert(MBMetabomGetPackageForTag(metabom, tags[i])); + } + + for (const auto& tagStr : tagStrs) { + // Does the configuration exist + auto configuration = metabomTagMap.find(tagStr); + if (configuration == metabomTagMap.end()) + continue; + + auto restrictions = metabomRestrictedTagMap.find(configuration->second); + if (restrictions != metabomRestrictedTagMap.end() && !is_disjoint(restrictions->second, tagStrs)) { + configurations[configuration->second].restrictedInstallnames.insert(proxy.second->installName); + } + // Is the configuration excluded + auto exclusions = metabomExcludeTagMap.find(configuration->second); + if (exclusions != metabomExcludeTagMap.end() && !is_disjoint(exclusions->second, tagStrs)) + continue; + + if ([manifestDict[@"configurations"][cppToObjStr(configuration->second)][@"architectures"] + containsObject:cppToObjStr(proxy.first)]) { + configurations[configuration->second.c_str()].architectures[proxy.first].anchors.push_back( + proxy.second->installName); + } + } + + free(tags); + } + } break; + case BOMSymlinkType: { + if (!has_prefix(entryPath, "/usr/lib/") && !has_prefix(entryPath, "/System/Library/")) + break; + const char* target = BOMFSObjectSymlinkTarget(fsObject); + if (target) { + symlinks[entryPath] = effectivePath(entryPath, target); + } + } break; + case BOMDirectoryType: { + if (!has_prefix(entryPath, "/usr/lib/") && !has_prefix(entryPath, "/System/Library/")) + break; + directories.insert(entryPath); + } break; + default: + break; + } + } + + MBIteratorFree(metabomEnumerator); + MBMetabomFree(metabom); + + dispatch_queue_t symlinkQueue = dispatch_get_global_queue(QOS_CLASS_DEFAULT, NULL); + dispatch_group_t symlinkGroup = dispatch_group_create(); + + for (auto& fileSet : architectureFiles) { + cacheBuilderDispatchGroupAsync(symlinkGroup, symlinkQueue, [&] { + for (auto& file : fileSet.second.dylibs) { + bool aliasAdded = true; + auto proxy = file.second.proxy; + + while (aliasAdded) { + aliasAdded = false; + + for (auto& symlink : symlinks) { + std::set newAliases; + auto alias = checkSymlink(proxy->installName, symlink, directories); + if (alias != "") { + newAliases.insert(alias); + } + + for (auto& existingAlias : proxy->installNameAliases) { + alias = checkSymlink(existingAlias, symlink, directories); + if (alias != "") { + newAliases.insert(alias); + } + } + + for (auto& alias : newAliases) { + if (proxy->addAlias(alias)) { + aliasAdded = true; + } + } + } + } + } + }); + } + dispatch_group_wait(symlinkGroup, DISPATCH_TIME_FOREVER); + + for (auto& fileSet : architectureFiles) { + for (auto& file : fileSet.second.dylibs) { + auto proxy = file.second.proxy; + + for (const auto& dependency : proxy->dependencies) { + auto dependencyProxy = dylibProxy(dependency, fileSet.first); + if (dependencyProxy == nullptr) + break; + + dependencyProxy->dependents.insert(proxy->installName); + } + } + } +} +#endif + +void Manifest::calculateClosure( bool enforceRootless ) { + rootless = enforceRootless; + + for ( auto& config : configurations ) { + for ( auto& arch : config.second.architectures ) { + calculateClosure( config.first, arch.first ); + } + } +} + +Manifest::File* Manifest::dylibForInstallName( const std::string& installname, const std::string& arch ) { + auto archIter = architectureFiles.find( arch ); + if ( archIter == architectureFiles.end() ) return nullptr; + + auto& files = archIter->second.dylibs; + auto dylibIterator = files.find( installname ); + + if ( dylibIterator != files.end() ) return &dylibIterator->second; + + for ( auto& candidate : files ) { + if ( candidate.second.proxy->installNameAliases.count( installname ) > 0 ) { + dylibIterator = files.find( candidate.first ); + return &dylibIterator->second; + } + } + // Check if we can fallback to an interworkable architecture + std::string fallbackArchStr = fallbackArchStringForArchString( arch ); + if ( !fallbackArchStr.empty() ) { + return dylibForInstallName( installname, fallbackArchStr ); + } + + return nullptr; +} + + +MachOProxy* Manifest::dylibProxy( const std::string& installname, const std::string& arch ) { + auto dylib = dylibForInstallName( installname, arch ); + + if ( dylib != nullptr ) { + assert( dylib->proxy != nullptr ); + return dylib->proxy; + } + + return nullptr; +} + +bool +Manifest::sameContentsAsCacheAtPath(const std::string& configuration, const std::string& architecture, const std::string& path) const { + __block std::set>> cacheDylibs; + std::set>> manifestDylibs; + struct stat statbuf; + if ( ::stat(path.c_str(), &statbuf) == -1 ) { + // don't warn if there is no existing cache file + if ( errno != ENOENT ) + warning("stat() failed for dyld shared cache at %s, errno=%d", path.c_str(), errno); + return false; + } + + int cache_fd = ::open(path.c_str(), O_RDONLY); + if ( cache_fd < 0 ) { + warning("open() failed for shared cache file at %s, errno=%d", path.c_str(), errno); + return false; + } + + const void *mappedCache = ::mmap(NULL, statbuf.st_size, PROT_READ, MAP_PRIVATE, cache_fd, 0); + if (mappedCache == MAP_FAILED) { + ::close(cache_fd); + warning("mmap() for shared cache at %s failed, errno=%d", path.c_str(), errno); + return false; + } + ::close(cache_fd); + + if (configurations.count(configuration) == 0 + || configurations.find(configuration)->second.architectures.count(architecture) == 0) + return false; + + for (auto& dylib : configurations.find(configuration)->second.architectures.find(architecture)->second.results.dylibs) { + if ( dylib.second.included == true) { + std::pair> dylibPair; + dylibPair.first = dylib.first; + bcopy((const void *)&dylib.second.uuid[0], &dylibPair.second[0], sizeof(uuid_t)); + manifestDylibs.insert(dylibPair); + auto file = architectureFiles.find(architecture)->second.dylibs.find(dylib.first); + if (file != architectureFiles.find(architecture)->second.dylibs.end()) { + for ( auto& alias : file->second.proxy->installNameAliases ) { + std::pair> aliasPair; + aliasPair.first = alias; + bcopy((const void *)&dylib.second.uuid[0], &aliasPair.second[0], sizeof(uuid_t)); + manifestDylibs.insert(aliasPair); + } + } + } + } + + (void)dyld_shared_cache_iterate(mappedCache, (uint32_t)statbuf.st_size, + ^(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo){ + std::pair> dylibPair; + dylibPair.first = dylibInfo->path; + bcopy((const void *)&dylibInfo->uuid[0], &dylibPair.second[0], sizeof(uuid_t)); + cacheDylibs.insert(dylibPair); + }); + + return (manifestDylibs == cacheDylibs); +} + +void Manifest::removeDylib( MachOProxy* proxy, const std::string& reason, const std::string& configuration, + const std::string& architecture, std::unordered_set& processedInstallnames ) { + auto configIter = configurations.find( configuration ); + if ( configIter == configurations.end() ) return; + auto archIter = configIter->second.architectures.find( architecture ); + if ( archIter == configIter->second.architectures.end() ) return; + auto& archManifest = archIter->second; + + if ( archManifest.results.dylibs.count( proxy->installName ) == 0 ) { + bcopy( &proxy->uuid[0], &archManifest.results.dylibs[proxy->installName].uuid[0], sizeof( uuid_t ) ); + processedInstallnames.insert( proxy->installName ); + } + archManifest.results.dylibs[proxy->installName].exclude( reason ); + + processedInstallnames.insert( proxy->installName ); + for ( auto& alias : proxy->installNameAliases ) { + processedInstallnames.insert( alias ); + } + + for ( const auto& dependent : proxy->dependents ) { + auto dependentProxy = dylibProxy( dependent, architecture ); + auto dependentResultIter = archManifest.results.dylibs.find( dependentProxy->installName ); + if ( dependentProxy && + ( dependentResultIter == archManifest.results.dylibs.end() || dependentResultIter->second.included == true ) ) { + removeDylib( dependentProxy, "Missing dependency: " + proxy->installName, configuration, architecture, + processedInstallnames ); + } + } +} + +MachOProxy* Manifest::removeLargestLeafDylib( const std::string& configuration, const std::string& architecture ) { + std::set activeInstallnames; + std::set leafDylibs; + + auto configIter = configurations.find( configuration ); + if ( configIter == configurations.end() ) terminate( "Internal error" ); + ; + auto archIter = configIter->second.architectures.find( architecture ); + if ( archIter == configIter->second.architectures.end() ) terminate( "Internal error" ); + ; + for ( const auto& dylibInfo : archIter->second.results.dylibs ) { + if ( dylibInfo.second.included ) { + activeInstallnames.insert( dylibInfo.first ); + } + } + for ( const auto& installname : activeInstallnames ) { + auto dylib = dylibProxy( installname, architecture ); + bool dependents = false; + for ( const auto& depedent : dylib->dependents ) { + if ( depedent != dylib->installName && activeInstallnames.count( depedent ) ) { + dependents = true; + break; + } + } + if ( !dependents ) { + leafDylibs.insert( dylib ); + } + } + if ( leafDylibs.empty() ) { + terminate( "No leaf dylibs to evict" ); + } + MachOProxy* largestLeafDylib = nullptr; + for ( const auto& dylib : leafDylibs ) { + if ( largestLeafDylib == nullptr || dylib->fileSize > largestLeafDylib->fileSize ) { + largestLeafDylib = dylib; + } + } + std::unordered_set empty; + removeDylib( largestLeafDylib, "VM space overflow", configuration, architecture, empty ); + return largestLeafDylib; +} + +static void recursiveInvalidate(const std::string& invalidName, std::unordered_map>& usesOf, std::unordered_set& unusableInstallNames) +{ + if ( unusableInstallNames.count(invalidName) ) + return; + unusableInstallNames.insert(invalidName); + for (const std::string& name : usesOf[invalidName] ) { + recursiveInvalidate(name, usesOf, unusableInstallNames); + } +} + +void Manifest::pruneClosure() +{ + for (auto& config : configurations) { + for (auto& arch : config.second.architectures) { + pruneClosure(config.first, arch.first); + } + } +} + +void Manifest::pruneClosure(const std::string& configuration, const std::string& architecture) +{ + auto configIter = configurations.find(configuration); + if ( configIter == configurations.end() ) + return; + auto archIter = configIter->second.architectures.find(architecture); + if ( archIter == configIter->second.architectures.end() ) + return; + auto& archManifest = archIter->second; + + // build reverse dependency map and list of excluded dylibs + std::unordered_map> reverseDep; + std::unordered_set unusableStart; + for (const auto& dylib : archManifest.results.dylibs) { + const std::string dylibInstallName = dylib.first; + if ( dylib.second.included ) { + if ( MachOProxy* proxy = dylibProxy(dylibInstallName, architecture) ) { + for (const std::string& dependentPath : proxy->dependencies) { + reverseDep[dependentPath].insert(dylibInstallName); + } + } + } + else { + unusableStart.insert(dylibInstallName); + } + } + + // mark unusable, all dylibs depending on the initially unusable dylibs + std::unordered_set newUnusable; + for (const std::string& unusable : unusableStart) { + recursiveInvalidate(unusable, reverseDep, newUnusable); + } + + // remove unusable dylibs from manifest + std::unordered_set dummy; + for (const std::string& unusable : newUnusable) { + if ( MachOProxy* proxy = dylibProxy(unusable, architecture) ) + removeDylib(proxy, "Missing dependency: " + unusable, configuration, architecture, dummy); + warning("can't use: %s because dependent dylib cannot be used", unusable.c_str()); + } +} + +void Manifest::calculateClosure( const std::string& configuration, const std::string& architecture ) { + auto& archManifest = configurations[configuration].architectures[architecture]; + auto archFileIter = architectureFiles.find( architecture ); + assert( archFileIter != architectureFiles.end() ); + auto files = archFileIter->second.dylibs; + + std::unordered_set newInstallnames; + + for ( auto& anchor : archManifest.anchors ) { + newInstallnames.insert(anchor.installname); + } + + std::unordered_set processedInstallnames; + + while (!newInstallnames.empty()) { + std::unordered_set installnamesToProcess = newInstallnames; + newInstallnames.clear(); + + for (const std::string& installname : installnamesToProcess) { + if (processedInstallnames.count(installname) > 0) { + continue; + } + + auto proxy = dylibProxy( installname, architecture ); + + if ( proxy == nullptr ) { + // No path + archManifest.results.dylibs[installname].exclude( "Could not find file for install name" ); + warning("Could not find file for install name (%s)", installname.c_str()); + continue; + } + + if (configurations[configuration].restrictedInstallnames.count(installname) != 0) { + removeDylib(proxy, "Dylib '" + installname + "' removed due to explict restriction", configuration, architecture, + processedInstallnames); + continue; + } + // Validate we have all are depedencies + for ( const auto& dependency : proxy->dependencies ) { + if ( !dylibProxy( dependency, architecture ) ) { + removeDylib( proxy, "Missing dependency: " + dependency, configuration, architecture, + processedInstallnames ); + break; + } + } + + // assert(info->installName == installname); + if ( archManifest.results.dylibs.count( proxy->installName ) == 0 ) { + bcopy( &proxy->uuid[0], &archManifest.results.dylibs[proxy->installName].uuid[0], sizeof( uuid_t ) ); + processedInstallnames.insert( proxy->installName ); + + auto fileIter = files.find( proxy->installName ); + if ( fileIter != files.end() ) { + for ( auto& aliasName : fileIter->second.proxy->installNameAliases ) { + processedInstallnames.insert( aliasName ); + } + } + } + + for ( const auto& dependency : proxy->dependencies ) { + if ( processedInstallnames.count( dependency ) == 0 ) { + newInstallnames.insert( dependency ); + } + } + } + } +} + +void Manifest::write( const std::string& path ) { + if ( path.empty() ) return; + + NSMutableDictionary* cacheDict = [[NSMutableDictionary alloc] init]; + NSMutableDictionary* projectDict = [[NSMutableDictionary alloc] init]; + NSMutableDictionary* configurationsDict = [[NSMutableDictionary alloc] init]; + NSMutableDictionary* resultsDict = [[NSMutableDictionary alloc] init]; + + cacheDict[@"manifest-version"] = @( manifest_version ); + cacheDict[@"build"] = cppToObjStr( build ); + cacheDict[@"dylibOrderFile"] = cppToObjStr( dylibOrderFile ); + cacheDict[@"dirtyDataOrderFile"] = cppToObjStr( dirtyDataOrderFile ); + cacheDict[@"metabomFile"] = cppToObjStr( metabomFile ); + + cacheDict[@"projects"] = projectDict; + cacheDict[@"results"] = resultsDict; + cacheDict[@"configurations"] = configurationsDict; + + for ( const auto& project : projects ) { + NSMutableArray* sources = [[NSMutableArray alloc] init]; + + for ( const auto& source : project.second.sources ) { + [sources addObject:cppToObjStr( source )]; + } + + projectDict[cppToObjStr( project.first )] = sources; + } + + for ( auto& configuration : configurations ) { + NSMutableArray* archArray = [[NSMutableArray alloc] init]; + for ( auto& arch : configuration.second.architectures ) { + [archArray addObject:cppToObjStr( arch.first )]; + } + + NSMutableArray* excludeTags = [[NSMutableArray alloc] init]; + for ( const auto& excludeTag : configuration.second.metabomExcludeTags ) { + [excludeTags addObject:cppToObjStr( excludeTag )]; + } + + configurationsDict[cppToObjStr( configuration.first )] = @{ + @"platformName" : cppToObjStr( configuration.second.platformName ), + @"metabomTag" : cppToObjStr( configuration.second.metabomTag ), + @"metabomExcludeTags" : excludeTags, + @"architectures" : archArray + }; + } + + for ( auto& configuration : configurations ) { + NSMutableDictionary* archResultsDict = [[NSMutableDictionary alloc] init]; + for ( auto& arch : configuration.second.architectures ) { + NSMutableDictionary* dylibsDict = [[NSMutableDictionary alloc] init]; + NSMutableArray* warningsArray = [[NSMutableArray alloc] init]; + NSMutableDictionary* devRegionsDict = [[NSMutableDictionary alloc] init]; + NSMutableDictionary* prodRegionsDict = [[NSMutableDictionary alloc] init]; + NSString *prodCDHash = cppToObjStr(arch.second.results.productionCache.cdHash); + NSString *devCDHash = cppToObjStr(arch.second.results.developmentCache.cdHash); + + for ( auto& dylib : arch.second.results.dylibs ) { + NSMutableDictionary* dylibDict = [[NSMutableDictionary alloc] init]; + if ( dylib.second.included ) { + NSMutableDictionary* segments = [[NSMutableDictionary alloc] init]; + dylibDict[@"included"] = @YES; + for ( auto& segment : dylib.second.segments ) { + segments[cppToObjStr( segment.name )] = + @{ @"startAddr" : @( segment.startAddr ), + @"endAddr" : @( segment.endAddr ) }; + } + dylibDict[@"segments"] = segments; + } else { + dylibDict[@"included"] = @NO; + dylibDict[@"exclusionInfo"] = cppToObjStr(dylib.second.exclusionInfo); + } + dylibsDict[cppToObjStr( dylib.first )] = dylibDict; + } + + for ( auto& region : arch.second.results.developmentCache.regions ) { + devRegionsDict[cppToObjStr( region.name )] = + @{ @"startAddr" : @( region.startAddr ), + @"endAddr" : @( region.endAddr ) }; + } + + for ( auto& region : arch.second.results.productionCache.regions ) { + prodRegionsDict[cppToObjStr( region.name )] = + @{ @"startAddr" : @( region.startAddr ), + @"endAddr" : @( region.endAddr ) }; + } + + for ( auto& warning : arch.second.results.warnings ) { + [warningsArray addObject:cppToObjStr( warning )]; + } + + BOOL built = arch.second.results.failure.empty(); + archResultsDict[cppToObjStr( arch.first )] = @{ + @"dylibs" : dylibsDict, + @"built" : @( built ), + @"failure" : cppToObjStr( arch.second.results.failure ), + @"productionCache" : @{@"cdhash" : prodCDHash, @"regions" : prodRegionsDict}, + @"developmentCache" : @{@"cdhash" : devCDHash, @"regions" : devRegionsDict}, + @"warnings" : warningsArray + }; + } + resultsDict[cppToObjStr( configuration.first )] = archResultsDict; + } + + NSError* error = nil; + NSData *outData = [NSPropertyListSerialization dataWithPropertyList:cacheDict + format:NSPropertyListBinaryFormat_v1_0 + options:0 + error:&error]; + (void)[outData writeToFile:cppToObjStr(path) atomically:YES]; +} diff --git a/src/dyld-apple/interlinked-dylibs/MultiCacheBuilder.h b/src/dyld-apple/interlinked-dylibs/MultiCacheBuilder.h new file mode 100644 index 000000000..9bba12d15 --- /dev/null +++ b/src/dyld-apple/interlinked-dylibs/MultiCacheBuilder.h @@ -0,0 +1,48 @@ +// +// MultiCacheBuilder.h +// dyld +// +// Created by Louis Gerbarg on 6/16/15. +// +// + +#ifndef MultiCacheBuilder_h +#define MultiCacheBuilder_h + +#include + +#include "Manifest.h" + +#include "mega-dylib-utils.h" +#include + +typedef struct _BOMBom* BOMBom; + +struct MultiCacheBuilder { + dispatch_semaphore_t _concurrencyLimitingSemaphore; + dispatch_semaphore_t _writeLimitingSemaphore; + dispatch_queue_t _writeQueue; + dispatch_group_t _writeGroup; + dispatch_queue_t _buildQueue; + Manifest& _manifest; + + uint64_t _filesWritten = 0; + uint64_t _bytesWritten = 0; + const bool _bniMode; + const bool _skipWrites; + const bool _skipBuilds; + const bool _buildRoot; + const bool _enforceRootless; + + MultiCacheBuilder(Manifest& manifest, bool BNI = false, bool SW = false, bool buildRoot = false, bool skipBuilds = false, bool enforceRootless = false); + + void buildCaches(std::string masterDstRoot); + + void logStats(); +private: + void runOnManifestConcurrently(std::function lambda); + void buildCache(const std::string cachePath, const std::set configurations, const std::string architecture, bool development); + void write_cache(std::string cachePath, const std::set& configurations, const std::string& architecture, std::shared_ptr cache, bool developmentCache); +}; + +#endif /* MultiCacheBuilder_h */ diff --git a/src/dyld-apple/interlinked-dylibs/MultiCacheBuilder.mm b/src/dyld-apple/interlinked-dylibs/MultiCacheBuilder.mm new file mode 100644 index 000000000..d1c7babff --- /dev/null +++ b/src/dyld-apple/interlinked-dylibs/MultiCacheBuilder.mm @@ -0,0 +1,441 @@ +// +// SharedCacheBuilder.m +// dyld +// +// Created by Louis Gerbarg on 6/15/15. +// +// + +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include // std::setfill, std::setw +#include "mega-dylib-utils.h" +#include "Logging.h" + +#include "MultiCacheBuilder.h" + + +namespace { +#if BOM_SUPPORT +void insertDirInBom( const std::string& path, const std::string& name, BOMBom bom ) { + std::string fullPath = path + "/" + name; + BOMFSObject fso = BOMFSObjectNew( BOMDirectoryType ); + BOMFSObjectSetFlags( fso, B_PATHONLY ); + BOMFSObjectSetPathName( fso, fullPath.c_str(), true ); + BOMFSObjectSetShortName( fso, name.c_str(), true ); + (void)BOMBomInsertFSObject( bom, fso, false ); + BOMFSObjectFree( fso ); +} + +void insertFileInBom( const std::string& path, const std::string& name, BOMBom bom ) { + std::string fullPath = path + "/" + name; + BOMFSObject fso = BOMFSObjectNew( BOMFileType ); + BOMFSObjectSetFlags( fso, B_PATHONLY ); + BOMFSObjectSetPathName( fso, fullPath.c_str(), true ); + BOMFSObjectSetShortName( fso, name.c_str(), true ); + (void)BOMBomInsertFSObject( bom, fso, false ); + BOMFSObjectFree( fso ); +} + +void insertCacheDirInBom( BOMBom bom ) { + BOMFSObject fso = BOMFSObjectNew( BOMDirectoryType ); + BOMFSObjectSetFlags( fso, B_PATHONLY ); + BOMFSObjectSetPathName( fso, ".", true ); + BOMFSObjectSetShortName( fso, ".", true ); + (void)BOMBomInsertFSObject( bom, fso, false ); + BOMFSObjectFree( fso ); + insertDirInBom( ".", "System", bom ); + insertDirInBom( "./System", "Library", bom ); + insertDirInBom( "./System/Library", "Caches", bom ); + insertDirInBom( "./System/Library/Caches", "com.apple.dyld", bom ); +} +#endif /* BOM_SUPPORT */ +} + +MultiCacheBuilder::MultiCacheBuilder(Manifest& manifest, bool BNI, bool SW, bool buildRoot, bool skipBuilds, bool enforceRootles) + : _manifest(manifest), _bniMode(BNI), _skipWrites(SW), _buildRoot(buildRoot), _skipBuilds(skipBuilds), _enforceRootless(enforceRootles), + _writeQueue(dispatch_queue_create("com.apple.dyld.cache.writeout", + dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, + QOS_CLASS_USER_INITIATED, 0))), + _writeGroup(dispatch_group_create()), + _buildQueue(dispatch_queue_create("com.apple.dyld.cache.multi-build", DISPATCH_QUEUE_CONCURRENT)) { + uint64_t thread_count; + uint64_t ram_size; + + size_t len = sizeof(thread_count); + sysctlbyname ("hw.logicalcpu",&thread_count,&len,NULL,0); + len = sizeof(ram_size); + sysctlbyname ("hw.memsize",&ram_size,&len,NULL,0); + + uint64_t buildCount = MIN((ram_size/(1024*1024*1024)/2), thread_count); + uint64_t writerCount = MAX((ram_size/((uint64_t)2*1024*1024*1024)) - buildCount, 1); + + _buildQueue = dispatch_queue_create("com.apple.dyld.cache.build", DISPATCH_QUEUE_CONCURRENT); + _concurrencyLimitingSemaphore = dispatch_semaphore_create(buildCount); + _writeLimitingSemaphore = dispatch_semaphore_create(writerCount); + + if ( _bniMode ) { + log("Running: %llu threads", buildCount); + log("Queuing: %llu writers", writerCount); + } +} + +void MultiCacheBuilder::write_cache(std::string cachePath, const std::set& configurations, const std::string& architecture, std::shared_ptr cache, bool developmentCache) +{ + //FIXME + dispatch_semaphore_wait(_writeLimitingSemaphore, DISPATCH_TIME_FOREVER); + dispatch_group_enter(_writeGroup); + cacheBuilderDispatchAsync(_writeQueue, [=] { + if (!_skipWrites) { + verboseLog("Queuing write out: %s", cachePath.c_str()); + + //Turn off file caching since we won't read it back + //(void)fcntl(fd, F_NOCACHE, 1); + // We should do this after the cache write, but that would involve copying the path string + std::string tempPath = cachePath; + cache->writeCacheMapFile(cachePath + ".map"); + char tempEXT[] = ".XXXXXX"; + mktemp(tempEXT); + tempPath += tempEXT; + + int fd = ::open(tempPath.c_str(), O_CREAT | O_RDWR | O_TRUNC, 0644); + if (fd == -1) { + dispatch_group_leave(_writeGroup); + dispatch_semaphore_signal(_writeLimitingSemaphore); + terminate("can't create temp file for %s, errnor=%d (%s)", cachePath.c_str(), errno, strerror(errno)); + } + + if (isProtectedBySIP(tempPath, fd) != _enforceRootless) { + ::close(fd); + ::unlink(tempPath.c_str()); + dispatch_group_leave(_writeGroup); + dispatch_semaphore_signal(_writeLimitingSemaphore); + terminate("SIP protection of output cache file changed (%s)", cachePath.c_str()); + } + + ssize_t writtenSize = pwrite(fd, cache->buffer().get(), cache->fileSize(), 0); + if (writtenSize != cache->fileSize()) { + ::close(fd); + ::unlink(tempPath.c_str()); + dispatch_group_leave(_writeGroup); + dispatch_semaphore_signal(_writeLimitingSemaphore); + terminate("write() failure creating cache file, requested %lld, wrote %ld, errno=%d (%s)", cache->fileSize(), writtenSize, errno, strerror(errno)); + } + + ::close(fd); + + if (rename(tempPath.c_str(), cachePath.c_str()) != 0) { + dispatch_group_leave(_writeGroup); + dispatch_semaphore_signal(_writeLimitingSemaphore); + terminate("move() failure creating cache file, errno=%d (%s)", errno, strerror(errno)); + } + if (_bniMode) + log("Wrote out: %s", cachePath.c_str()); + } else { + log("Skipped: %s", cachePath.c_str()); + } + _filesWritten++; + _bytesWritten += cache->fileSize(); + dispatch_group_leave(_writeGroup); + dispatch_semaphore_signal(_writeLimitingSemaphore); + }); +} + +//FIXME (make development a type) +void MultiCacheBuilder::buildCache(const std::string cachePath, const std::set configurations, const std::string architecture, bool development) +{ + auto& configResults = _manifest.configurations[*configurations.begin()].architectures[architecture].results.dylibs; + + if ( _skipBuilds ) { + log( "Build Skipped" ); + + for ( auto& config : configurations ) { + for ( auto& dylib : configResults ) { + _manifest.configurations[config].architectures[architecture].results.dylibs[dylib.first].exclude( + "All dylibs excluded" ); + } + } + return; + } + + Manifest::Architecture arch; + std::vector> dylibs; + std::vector emptyList; + std::shared_ptr cache = std::make_shared(_manifest, *configurations.begin(), architecture); + + for (auto& config : configurations) { + auto& results = _manifest.configurations[config].architectures[architecture].results.dylibs; + + for (auto& dylib : configResults) { + if (dylib.second.included == false + && results.count(dylib.first) + && results[dylib.first].included == true) { + results[dylib.first].exclude(dylib.second.exclusionInfo); + } + } + } + + if (development) { + cache->buildForDevelopment(cachePath); + } else { + cache->buildForProduction(cachePath); + } + + std::vector regionStartAddresses; + std::vector regionSizes; + std::vector regionFileOffsets; + + cache->forEachRegion([&] (void* content, uint64_t vmAddr, uint64_t size, uint32_t permissions) { + regionStartAddresses.push_back(vmAddr); + regionSizes.push_back(size); + regionFileOffsets.push_back((uint8_t*)content - (uint8_t*)cache->buffer().get()); + const char* prot = "RW"; + if ( permissions == (VM_PROT_EXECUTE|VM_PROT_READ) ) + prot = "EX"; + else if ( permissions == VM_PROT_READ ) + prot = "RO"; + for (auto& config : configurations) { + if (development) { + _manifest.configurations[config].architectures[architecture].results.developmentCache.regions.push_back({prot, vmAddr,vmAddr+size }); + } else { + _manifest.configurations[config].architectures[architecture].results.productionCache.regions.push_back({prot, vmAddr,vmAddr+size }); + } + } + }); + + cache->forEachImage([&](const void* machHeader, const char* installName, time_t mtime, + ino_t inode, const std::vector& segments) { + for (auto& seg : segments) { + uint64_t vmAddr = 0; + for (int i=0; i < regionSizes.size(); ++i) { + if ( (seg.fileOffset >= regionFileOffsets[i]) && (seg.fileOffset < (regionFileOffsets[i]+regionSizes[i])) ) { + vmAddr = regionStartAddresses[i] + seg.fileOffset - regionFileOffsets[i]; + } + } + for (auto& config : configurations) { + _manifest.configurations[config].architectures[architecture].results.dylibs[installName].segments.push_back({seg.name, vmAddr, vmAddr+seg.size}); + if (_manifest.configurations[config].architectures[architecture].results.dylibs[installName].segments.size() == 0) { + warning("Attempting to write info for excluded dylib"); + _manifest.configurations[config].architectures[architecture].results.dylibs[installName].exclude("Internal Error"); + } + } + } + }); + if (development) { + verboseLog("developement cache size = %llu", cache->fileSize()); + } else { + verboseLog("production cache size = %llu", cache->fileSize()); + } + if ( cache->vmSize()+align(cache->vmSize()/200, sharedRegionRegionAlignment(archForString(architecture))) > sharedRegionRegionSize(archForString(architecture))) { + warning("shared cache will not fit in shared regions address space. Overflow amount: %llu", + cache->vmSize() + align(cache->vmSize() / 200, sharedRegionRegionAlignment(archForString(architecture))) - sharedRegionRegionSize(archForString(architecture))); + return; + } + write_cache(cachePath, configurations, architecture, cache, development); + for (auto& config : configurations) { + if (development) { + _manifest.configurations[config].architectures[architecture].results.developmentCache.cdHash = cache->cdHashString(); + } + else { + _manifest.configurations[config].architectures[architecture].results.productionCache.cdHash = cache->cdHashString(); + } + } +} + +void MultiCacheBuilder::runOnManifestConcurrently(std::function lambda) +{ + dispatch_group_t runGroup = dispatch_group_create(); + for (auto& config : _manifest.configurations) { + for (auto& architecture : config.second.architectures) { + dispatch_semaphore_wait(_concurrencyLimitingSemaphore, DISPATCH_TIME_FOREVER); + cacheBuilderDispatchGroupAsync(runGroup, _buildQueue, [&] { + WarningTargets targets; + targets.first = &_manifest; + targets.second.insert(std::make_pair(config.first, architecture.first)); + auto ctx = std::make_shared(config.first + "/" + architecture.first, targets); + setLoggingContext(ctx); + lambda(config.first, architecture.first); + dispatch_semaphore_signal(_concurrencyLimitingSemaphore); + }); + } + } + + dispatch_group_wait(runGroup, DISPATCH_TIME_FOREVER); +} + +void MultiCacheBuilder::buildCaches(std::string masterDstRoot) { + if (_bniMode) { + std::vector> dedupedCacheSets; + for (auto& config : _manifest.configurations) { + bool dupeFound = false; + + for (auto& cacheSet : dedupedCacheSets) { + if (config.second.equivalent(_manifest.configurations[*cacheSet.begin()])) { + cacheSet.insert(config.first); + dupeFound = true; + break; + } + } + + if (!dupeFound) { + std::set temp; + temp.insert(config.first); + dedupedCacheSets.push_back(temp); + } + } + + for (auto& cacheSet : dedupedCacheSets) { + //FIXME we may want to consider moving to hashes of UUID sets + std::string setName; + + for (auto &archName : cacheSet) { + if (!setName.empty()) { + setName += "|"; + } + setName += archName; + } + + std::stringstream fileNameStream; + std::array digest = {0}; + CC_SHA1(setName.c_str(), (unsigned int)setName.length(), &digest[0]); + + fileNameStream << std::hex << std::uppercase << std::setfill( '0' ); + for( int c : digest ) { + fileNameStream << std::setw( 2 ) << c; + } + + std::string fileName(fileNameStream.str()); + + for (auto& config : cacheSet) { + if (!_skipWrites) { + int err = symlink(("DedupedConfigs/" + fileName).c_str(), (masterDstRoot + "/" + config).c_str()); + if (err) { + warning("Could not create symlink '%s' -> 'DedupedConfigs/%s' (%d)", config.c_str(), fileName.c_str(), err); + } + } + } + + for (auto& arch : _manifest.configurations[*cacheSet.begin()].architectures) { + dispatch_semaphore_wait(_concurrencyLimitingSemaphore, DISPATCH_TIME_FOREVER); + cacheBuilderDispatchGroupAsync(_writeGroup, _buildQueue, [=] { + WarningTargets targets; + targets.first = &_manifest; + for (auto& config : cacheSet) { + targets.second.insert(std::make_pair(config, arch.first)); + } + auto ctx = std::make_shared(setName + "/" + arch.first, targets); + setLoggingContext(ctx); + + std::string configPath = masterDstRoot + "/DedupedConfigs/" + fileName + "/System/Library/Caches/com.apple.dyld/"; + + if (!_skipWrites) { + int err = mkpath_np(configPath.c_str(), 0755); + + if (err != 0 && err != EEXIST) { + dispatch_semaphore_signal(_concurrencyLimitingSemaphore); + terminate("mkpath_np fail: %d", err); + } + } + + buildCache(configPath + "dyld_shared_cache_" + arch.first + ".development", cacheSet, arch.first, true); + buildCache(configPath + "dyld_shared_cache_" + arch.first, cacheSet, arch.first, false); + dispatch_semaphore_signal(_concurrencyLimitingSemaphore); + }); + } + } + + dispatch_group_wait(_writeGroup, DISPATCH_TIME_FOREVER); + +#if BOM_SUPPORT + if ( !_skipWrites ) { + for ( auto& configuration : _manifest.configurations ) { + std::vector prodBomPaths; + std::vector devBomPaths; + + for (auto& arch : configuration.second.architectures) { + std::string cachePath = "dyld_shared_cache_" + arch.first; + prodBomPaths.push_back(cachePath); + cachePath += ".development"; + devBomPaths.push_back(cachePath); + dispatch_group_enter(_writeGroup); + cacheBuilderDispatchAsync(_writeQueue, [=] { + char buffer[MAXPATHLEN]; + sprintf(buffer, "%s/Boms/%s.prod.bom", masterDstRoot.c_str(), configuration.first.c_str()); + BOMBom bom = BOMBomNew(buffer); + insertCacheDirInBom(bom); + for (auto& path : prodBomPaths) { + insertFileInBom("./System/Library/Caches/com.apple.dyld", path, bom); + } + BOMBomFree(bom); + + sprintf(buffer, "%s/Boms/%s.dev.bom", masterDstRoot.c_str(), configuration.first.c_str()); + bom = BOMBomNew(buffer); + insertCacheDirInBom(bom); + for (auto& path : devBomPaths) { + insertFileInBom("./System/Library/Caches/com.apple.dyld", path, bom); + } + BOMBomFree(bom); + + sprintf(buffer, "%s/Boms/%s.full.bom", masterDstRoot.c_str(), configuration.first.c_str()); + bom = BOMBomNew(buffer); + insertCacheDirInBom(bom); + for (auto& path : prodBomPaths) { + insertFileInBom("./System/Library/Caches/com.apple.dyld", path, bom); + } + for (auto& path : devBomPaths) { + insertFileInBom("./System/Library/Caches/com.apple.dyld", path, bom); + } + BOMBomFree(bom); + dispatch_group_leave(_writeGroup); + }); + } + } + } +#endif /* BOM_SUPPORT */ + } else { + runOnManifestConcurrently( + [&](const std::string configuration, const std::string architecture) { + cacheBuilderDispatchGroupAsync(_writeGroup, _buildQueue, [=] { + std::set configurations; + configurations.insert( configuration ); + // FIXME hacky, we make implicit assumptions about dev vs non-dev and layout depending on the flags + if ( _buildRoot ) { + int err = mkpath_np( ( masterDstRoot + "/System/Library/Caches/com.apple.dyld/" ).c_str(), 0755 ); + + if ( err != 0 && err != EEXIST ) { + terminate( "mkpath_np fail: %d", err ); + } + buildCache(masterDstRoot + "/System/Library/Caches/com.apple.dyld/dyld_shared_cache_" + architecture, + configurations, architecture, false); + buildCache(masterDstRoot + "/System/Library/Caches/com.apple.dyld/dyld_shared_cache_" + architecture + ".development", + configurations, architecture, true); + } else { + buildCache(masterDstRoot + "/dyld_shared_cache_" + architecture, configurations, architecture, true); + } + }); + }); + dispatch_group_wait(_writeGroup, DISPATCH_TIME_FOREVER); + } + + int err = sync_volume_np(masterDstRoot.c_str(), SYNC_VOLUME_FULLSYNC | SYNC_VOLUME_WAIT); + if (err) { + warning("Volume sync failed errnor=%d (%s)", err, strerror(err)); + } +} + +void MultiCacheBuilder::logStats(void) { + if ( _bniMode ) + log("Processed %llu caches (%.2fGB)", _filesWritten, ((float)_bytesWritten)/(1024*1024*1024)); +} + diff --git a/src/dyld-apple/interlinked-dylibs/ObjC1Abstraction.hpp b/src/dyld-apple/interlinked-dylibs/ObjC1Abstraction.hpp new file mode 100644 index 000000000..93bf751cd --- /dev/null +++ b/src/dyld-apple/interlinked-dylibs/ObjC1Abstraction.hpp @@ -0,0 +1,231 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2008-2010 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@ + */ + +#define OBJC_IMAGE_SUPPORTS_GC (1<<1) +#define OBJC_IMAGE_REQUIRES_GC (1<<2) + +template +struct objc_image_info { + uint32_t version; + uint32_t flags; + + uint32_t getFlags() INLINE { return P::E::get32(flags); } + + bool supportsGCFlagSet() INLINE { return getFlags() & OBJC_IMAGE_SUPPORTS_GC; } + bool requiresGCFlagSet() INLINE { return getFlags() & OBJC_IMAGE_REQUIRES_GC; } + + void setFlag(uint32_t bits) INLINE { uint32_t old = P::E::get32(flags); P::E::set32(flags, old | bits); } + void setOptimizedByDyld() INLINE { setFlag(1<<3); } +}; + +template +struct objc_method { + uint32_t method_name; // SEL + uint32_t method_types; // char * + uint32_t method_imp; // IMP + + uint32_t getName() const INLINE { return P::E::get32(method_name); } + void setName(uint32_t newName) INLINE { P::E::set32(method_name, newName); } +}; + +template +struct objc_method_list { + enum { OBJC_FIXED_UP = 1771 }; + uint32_t obsolete; // struct objc_method_list * + uint32_t method_count; // int + struct objc_method

method_list[0]; + + uint32_t getCount() const INLINE { return P::E::get32(method_count); } + void setFixedUp(bool fixed) INLINE { P::E::set32(obsolete, fixed ? OBJC_FIXED_UP : 0); } +}; + +template +struct objc_class { + uint32_t isa; // struct objc_class * + uint32_t super_class; // struct objc_class * + uint32_t name; // const char * + uint32_t version; // long + uint32_t info; // long + uint32_t instance_size; // long + uint32_t ivars; // struct objc_ivar_list * + uint32_t methodList; // struct objc_method_list * + uint32_t method_cache; // struct objc_cache * + uint32_t protocols; // objc_protocol_list * + uint32_t ivar_layout; // const char * + uint32_t ext; // struct objc_class_ext * + + struct objc_class

*getIsa(ContentAccessor* cache) const INLINE { return (struct objc_class

*)cache->contentForVMAddr(P::E::get32(isa)); } + struct objc_method_list

*getMethodList(ContentAccessor* cache) const INLINE { return (struct objc_method_list

*)cache->contentForVMAddr(P::E::get32(methodList)); } +}; + +template +struct objc_category { + uint32_t category_name; // char * + uint32_t class_name; // char * + uint32_t instance_methods; // struct objc_method_list * + uint32_t class_methods; // struct objc_method_list * + uint32_t protocols; // objc_protocol_list * + uint32_t size; // uint32_t + uint32_t instance_properties; // struct objc_property_list * + + struct objc_method_list

*getInstanceMethods(ContentAccessor* cache) const INLINE { return (struct objc_method_list

*)cache->contentForVMAddr(P::E::get32(instance_methods)); } + struct objc_method_list

*getClassMethods(ContentAccessor* cache) const INLINE { return (struct objc_method_list

*)cache->contentForVMAddr(P::E::get32(class_methods)); } +}; + +template +struct objc_symtab { + uint32_t sel_ref_cnt; // unsigned long + uint32_t refs; // SEL * + uint16_t cls_def_cnt; // unsigned short + uint16_t cat_def_cnt; // unsigned short + uint32_t defs[0]; // void * + + uint16_t getClassCount(void) const INLINE { return P::E::get16(cls_def_cnt); } + uint16_t getCategoryCount(void) const INLINE { return P::E::get16(cat_def_cnt); } + struct objc_class

*getClass(ContentAccessor* cache, int index) const INLINE { return (struct objc_class

*)cache->contentForVMAddr(P::E::get32(defs[index])); } + struct objc_category

*getCategory(ContentAccessor* cache, int index) const INLINE { return (struct objc_category

*)cache->contentForVMAddr(P::E::get32(defs[getClassCount() + index])); } +}; + +template +struct objc_module { + uint32_t version; // unsigned long + uint32_t size; // unsigned long + uint32_t name; // char* + uint32_t symtab; // Symtab + + struct objc_symtab

*getSymtab(ContentAccessor* cache) const INLINE { return (struct objc_symtab

*)cache->contentForVMAddr(P::E::get32(symtab)); } +}; + +template +struct objc_method_description { + uint32_t name; // SEL + uint32_t types; // char * + + uint32_t getName() const INLINE { return P::E::get32(name); } + void setName(uint32_t newName) INLINE { P::E::set32(name, newName); } +}; + +template +struct objc_method_description_list { + uint32_t count; // int + struct objc_method_description

list[0]; + + uint32_t getCount() const INLINE { return P::E::get32(count); } +}; + +template +struct objc_protocol { + uint32_t isa; // danger! contains strange values! + uint32_t protocol_name; // const char * + uint32_t protocol_list; // struct objc_protocol_list + uint32_t instance_methods; // struct objc_method_description_list * + uint32_t class_methods; // struct objc_method_description_list * + + struct objc_method_description_list

*getInstanceMethodDescriptions(ContentAccessor* cache) const INLINE { return (struct objc_method_description_list

*)cache->contentForVMAddr(P::E::get32(instance_methods)); } + struct objc_method_description_list

*getClassMethodDescriptions(ContentAccessor* cache) const INLINE { return (struct objc_method_description_list

*)cache->contentForVMAddr(P::E::get32(class_methods)); } +}; + + +template +class LegacySelectorUpdater { + typedef typename P::uint_t pint_t; + + static void visitMethodList(objc_method_list

*mlist, V& visitor) + { + for (uint32_t m = 0; m < mlist->getCount(); m++) { + pint_t oldValue = mlist->method_list[m].getName(); + pint_t newValue = visitor.visit(oldValue); + mlist->method_list[m].setName((uint32_t)newValue); + } + mlist->setFixedUp(true); + } + + static void visitMethodDescriptionList(objc_method_description_list

*mlist, V& visitor) + { + for (pint_t m = 0; m < mlist->getCount(); m++) { + pint_t oldValue = mlist->list[m].getName(); + pint_t newValue = visitor.visit(oldValue); + mlist->list[m].setName((uint32_t)newValue); + } + } + +public: + + static void update(ContentAccessor* cache, const macho_header

* header, V& visitor) + { + ArraySection > + modules(cache, header, "__OBJC", "__module_info"); + for (uint64_t m = 0; m < modules.count(); m++) { + objc_symtab

*symtab = modules.get(m).getSymtab(cache); + if (!symtab) continue; + + // Method lists in classes + for (int c = 0; c < symtab->getClassCount(); c++) { + objc_class

*cls = symtab->getClass(cache, c); + objc_class

*isa = cls->getIsa(cache); + objc_method_list

*mlist; + if ((mlist = cls->getMethodList(cache))) { + visitMethodList(mlist, visitor); + } + if ((mlist = isa->getMethodList(cache))) { + visitMethodList(mlist, visitor); + } + } + + // Method lists from categories + for (int c = 0; c < symtab->getCategoryCount(); c++) { + objc_category

*cat = symtab->getCategory(cache, c); + objc_method_list

*mlist; + if ((mlist = cat->getInstanceMethods(cache))) { + visitMethodList(mlist, visitor); + } + if ((mlist = cat->getClassMethods(cache))) { + visitMethodList(mlist, visitor); + } + } + } + + // Method description lists from protocols + ArraySection> + protocols(cache, header, "__OBJC", "__protocol"); + for (uint64_t p = 0; p < protocols.count(); p++) { + objc_protocol

& protocol = protocols.get(p); + objc_method_description_list

*mlist; + if ((mlist = protocol.getInstanceMethodDescriptions(cache))) { + visitMethodDescriptionList(mlist, visitor); + } + if ((mlist = protocol.getClassMethodDescriptions(cache))) { + visitMethodDescriptionList(mlist, visitor); + } + } + + // Message refs + PointerSection selrefs(cache, header, "__OBJC", "__message_refs"); + for (pint_t s = 0; s < selrefs.count(); s++) { + pint_t oldValue = selrefs.getVMAddress(s); + pint_t newValue = visitor.visit(oldValue); + selrefs.setVMAddress(s, newValue); + } + } +}; diff --git a/src/dyld-apple/interlinked-dylibs/ObjC2Abstraction.hpp b/src/dyld-apple/interlinked-dylibs/ObjC2Abstraction.hpp new file mode 100644 index 000000000..35e9aba9e --- /dev/null +++ b/src/dyld-apple/interlinked-dylibs/ObjC2Abstraction.hpp @@ -0,0 +1,1217 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2008-2010 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@ + */ + +#include +#include +#include + +// iterate an entsize-based list +// typedef entsize_iterator, type_list_t

> type_iterator; +template +struct entsize_iterator { + uint32_t entsize; + uint32_t index; // keeping track of this saves a divide in operator- + T* current; + + typedef std::random_access_iterator_tag iterator_category; + typedef T value_type; + typedef ptrdiff_t difference_type; + typedef T* pointer; + typedef T& reference; + + entsize_iterator() { } + + entsize_iterator(const Tlist& list, uint32_t start = 0) + : entsize(list.getEntsize()), index(start), current(&list.get(start)) + { } + + const entsize_iterator& operator += (ptrdiff_t count) { + current = (T*)((uint8_t *)current + count*entsize); + index += count; + return *this; + } + const entsize_iterator& operator -= (ptrdiff_t count) { + current = (T*)((uint8_t *)current - count*entsize); + index -= count; + return *this; + } + const entsize_iterator operator + (ptrdiff_t count) const { + return entsize_iterator(*this) += count; + } + const entsize_iterator operator - (ptrdiff_t count) const { + return entsize_iterator(*this) -= count; + } + + entsize_iterator& operator ++ () { *this += 1; return *this; } + entsize_iterator& operator -- () { *this -= 1; return *this; } + entsize_iterator operator ++ (int) { + entsize_iterator result(*this); *this += 1; return result; + } + entsize_iterator operator -- (int) { + entsize_iterator result(*this); *this -= 1; return result; + } + + ptrdiff_t operator - (const entsize_iterator& rhs) const { + return (ptrdiff_t)this->index - (ptrdiff_t)rhs.index; + } + + T& operator * () { return *current; } + T& operator * () const { return *current; } + T& operator -> () { return *current; } + const T& operator -> () const { return *current; } + + operator T& () const { return *current; } + + bool operator == (const entsize_iterator& rhs) { + return this->current == rhs.current; + } + bool operator != (const entsize_iterator& rhs) { + return this->current != rhs.current; + } + + bool operator < (const entsize_iterator& rhs) { + return this->current < rhs.current; + } + bool operator > (const entsize_iterator& rhs) { + return this->current > rhs.current; + } + + + static void overwrite(entsize_iterator& dst, const Tlist* srcList) + { + entsize_iterator src; + uint32_t ee = srcList->getEntsize(); + for (src = srcList->begin(); src != srcList->end(); ++src) { + memcpy(&*dst, &*src, ee); + ++dst; + } + } +}; + +template +class objc_header_info_rw_t { + + typedef typename P::uint_t pint_t; + + pint_t data; // loaded:1, allRealised:1, objc_header_info *:ptr + +public: + objc_header_info_rw_t(ContentAccessor* cache, const macho_header

* mh) + : data(0) { + } +}; + +template +class objc_header_info_ro_t { + + typedef typename P::uint_t pint_t; + + pint_t mhdr_offset; // offset to mach_header or mach_header_64 + pint_t info_offset; // offset to objc_image_info * + +public: + objc_header_info_ro_t(ContentAccessor* cache, const macho_header

* mh) + : mhdr_offset(0), info_offset(0) { + P::setP(mhdr_offset, (uint64_t)cache->vmAddrForContent((void*)mh) - (uint64_t)cache->vmAddrForContent(&mhdr_offset)); + assert(header_vmaddr(cache) == (uint64_t)cache->vmAddrForContent((void*)mh)); + const macho_section

* sect = mh->getSection("__DATA", "__objc_imageinfo"); + if (sect) { + P::setP(info_offset, (uint64_t)sect->addr() - (uint64_t)cache->vmAddrForContent(&info_offset)); + // set bit in mach_header.flags to tell dyld that this image has objc content + macho_header

* rwmh = const_cast*>(mh); + rwmh->set_flags(mh->flags() | MH_HAS_OBJC); + } + else + P::setP(info_offset, - (uint64_t)cache->vmAddrForContent(&info_offset)); + } + + pint_t header_vmaddr(ContentAccessor* cache) const { + return (pint_t)(((uint64_t)cache->vmAddrForContent(&mhdr_offset)) + mhdr_offset); + } +}; + + +template +class objc_method_list_t; // forward reference + + +template +class objc_method_t { + typedef typename P::uint_t pint_t; + pint_t name; // SEL + pint_t types; // const char * + pint_t imp; // IMP + friend class objc_method_list_t

; +public: + pint_t getName() const { return (pint_t)P::getP(name); } + void setName(pint_t newName) { P::setP(name, newName); } + + struct SortBySELAddress : + public std::binary_function&, + const objc_method_t

&, bool> + { + bool operator() (const objc_method_t

& lhs, + const objc_method_t

& rhs) + { + return lhs.getName() < rhs.getName(); + } + }; +}; + +template +class objc_method_list_t { + uint32_t entsize; + uint32_t count; + objc_method_t

first; + + void* operator new (size_t, void* buf) { return buf; } + +public: + + typedef entsize_iterator, objc_method_list_t

> method_iterator; + + uint32_t getCount() const { return P::E::get32(count); } + + uint32_t getEntsize() const {return P::E::get32(entsize)&~(uint32_t)3;} + + objc_method_t

& get(uint32_t i) const { return *(objc_method_t

*)((uint8_t *)&first + i * getEntsize()); } + + uint32_t byteSize() const { + return byteSizeForCount(getCount(), getEntsize()); + } + + static uint32_t byteSizeForCount(uint32_t c, uint32_t e = sizeof(objc_method_t

)) { + return sizeof(objc_method_list_t

) - sizeof(objc_method_t

) + c*e; + } + + method_iterator begin() { return method_iterator(*this, 0); } + method_iterator end() { return method_iterator(*this, getCount()); } + const method_iterator begin() const { return method_iterator(*this, 0); } + const method_iterator end() const { return method_iterator(*this, getCount()); } + + void setFixedUp() { P::E::set32(entsize, getEntsize() | 3); } + + void getPointers(std::set& pointersToRemove) { + for(method_iterator it = begin(); it != end(); ++it) { + objc_method_t

& entry = *it; + pointersToRemove.insert(&(entry.name)); + pointersToRemove.insert(&(entry.types)); + pointersToRemove.insert(&(entry.imp)); + } + } + + static void addPointers(uint8_t* methodList, std::vector& pointersToAdd) { + objc_method_list_t

* mlist = (objc_method_list_t

*)methodList; + for(method_iterator it = mlist->begin(); it != mlist->end(); ++it) { + objc_method_t

& entry = *it; + pointersToAdd.push_back(&(entry.name)); + pointersToAdd.push_back(&(entry.types)); + pointersToAdd.push_back(&(entry.imp)); + } + } + + static objc_method_list_t

* newMethodList(size_t newCount, uint32_t newEntsize) { + void *buf = ::calloc(byteSizeForCount(newCount, newEntsize), 1); + return new (buf) objc_method_list_t

(newCount, newEntsize); + } + + void operator delete(void * p) { + ::free(p); + } + + objc_method_list_t(uint32_t newCount, + uint32_t newEntsize = sizeof(objc_method_t

)) + : entsize(newEntsize), count(newCount) + { } + +private: + // use newMethodList instead + void* operator new (size_t); +}; + + +template +class objc_ivar_t { + typedef typename P::uint_t pint_t; + + pint_t offset; // uint32_t* (uint64_t* on x86_64) + pint_t name; // const char* + pint_t type; // const char* + uint32_t alignment; + uint32_t size; + +public: + const char* getName(ContentAccessor* cache) const { return (const char *)cache->contentForVMAddr(P::getP(name)); } + + bool hasOffset() const { return offset != 0; } + uint32_t getOffset(ContentAccessor* cache) const { return P::E::get32(*(uint32_t*)(cache->contentForVMAddr(P::getP(offset)))); } + void setOffset(ContentAccessor* cache, uint32_t newOffset) { P::E::set32(*(uint32_t*)(cache->contentForVMAddr(P::getP(offset))), newOffset); } + + + uint32_t getAlignment() { + uint32_t a = P::E::get32(alignment); + return (a == (uint32_t)-1) ? sizeof(pint_t) : 1< +class objc_ivar_list_t { + typedef typename P::uint_t pint_t; + uint32_t entsize; + uint32_t count; + objc_ivar_t

first; + + void* operator new (size_t, void* buf) { return buf; } + +public: + + typedef entsize_iterator, objc_ivar_list_t

> ivar_iterator; + + uint32_t getCount() const { return P::E::get32(count); } + + uint32_t getEntsize() const { return P::E::get32(entsize); } + + objc_ivar_t

& get(pint_t i) const { return *(objc_ivar_t

*)((uint8_t *)&first + i * P::E::get32(entsize)); } + + uint32_t byteSize() const { + return byteSizeForCount(getCount(), getEntsize()); + } + + static uint32_t byteSizeForCount(uint32_t c, uint32_t e = sizeof(objc_ivar_t

)) { + return sizeof(objc_ivar_list_t

) - sizeof(objc_ivar_t

) + c*e; + } + + ivar_iterator begin() { return ivar_iterator(*this, 0); } + ivar_iterator end() { return ivar_iterator(*this, getCount()); } + const ivar_iterator begin() const { return ivar_iterator(*this, 0); } + const ivar_iterator end() const { return ivar_iterator(*this, getCount()); } + + static objc_ivar_list_t

* newIvarList(size_t newCount, uint32_t newEntsize) { + void *buf = ::calloc(byteSizeForCount(newCount, newEntsize), 1); + return new (buf) objc_ivar_list_t

(newCount, newEntsize); + } + + void operator delete(void * p) { + ::free(p); + } + + objc_ivar_list_t(uint32_t newCount, + uint32_t newEntsize = sizeof(objc_ivar_t

)) + : entsize(newEntsize), count(newCount) + { } +private: + // use newIvarList instead + void* operator new (size_t); +}; + + +template class objc_property_list_t; // forward + +template +class objc_property_t { + typedef typename P::uint_t pint_t; + pint_t name; + pint_t attributes; + friend class objc_property_list_t

; +public: + + const char * getName(ContentAccessor* cache) const { return (const char *)cache->contentForVMAddr(P::getP(name)); } + + const char * getAttributes(ContentAccessor* cache) const { return (const char *)cache->contentForVMAddr(P::getP(attributes)); } +}; + +template +class objc_property_list_t { + uint32_t entsize; + uint32_t count; + objc_property_t

first; + + void* operator new (size_t, void* buf) { return buf; } + +public: + + typedef entsize_iterator, objc_property_list_t

> property_iterator; + + uint32_t getCount() const { return P::E::get32(count); } + + uint32_t getEntsize() const { return P::E::get32(entsize); } + + objc_property_t

& get(uint32_t i) const { return *(objc_property_t

*)((uint8_t *)&first + i * getEntsize()); } + + uint32_t byteSize() const { + return byteSizeForCount(getCount(), getEntsize()); + } + + static uint32_t byteSizeForCount(uint32_t c, uint32_t e = sizeof(objc_property_t

)) { + return sizeof(objc_property_list_t

) - sizeof(objc_property_t

) + c*e; + } + + property_iterator begin() { return property_iterator(*this, 0); } + property_iterator end() { return property_iterator(*this, getCount()); } + const property_iterator begin() const { return property_iterator(*this, 0); } + const property_iterator end() const { return property_iterator(*this, getCount()); } + + void getPointers(std::set& pointersToRemove) { + for(property_iterator it = begin(); it != end(); ++it) { + objc_property_t

& entry = *it; + pointersToRemove.insert(&(entry.name)); + pointersToRemove.insert(&(entry.attributes)); + } + } + + static void addPointers(uint8_t* propertyList, std::vector& pointersToAdd) { + objc_property_list_t

* plist = (objc_property_list_t

*)propertyList; + for(property_iterator it = plist->begin(); it != plist->end(); ++it) { + objc_property_t

& entry = *it; + pointersToAdd.push_back(&(entry.name)); + pointersToAdd.push_back(&(entry.attributes)); + } + } + + static objc_property_list_t

* newPropertyList(size_t newCount, uint32_t newEntsize) { + void *buf = ::calloc(byteSizeForCount(newCount, newEntsize), 1); + return new (buf) objc_property_list_t

(newCount, newEntsize); + } + + void operator delete(void * p) { + ::free(p); + } + + objc_property_list_t(uint32_t newCount, + uint32_t newEntsize = sizeof(objc_property_t

)) + : entsize(newEntsize), count(newCount) + { } +private: + // use newPropertyList instead + void* operator new (size_t); +}; + + +template class objc_protocol_list_t; // forward reference + +template +class objc_protocol_t { + typedef typename P::uint_t pint_t; + + pint_t isa; + pint_t name; + pint_t protocols; + pint_t instanceMethods; + pint_t classMethods; + pint_t optionalInstanceMethods; + pint_t optionalClassMethods; + pint_t instanceProperties; + uint32_t size; + uint32_t flags; + pint_t extendedMethodTypes; + pint_t demangledName; + pint_t classProperties; + +public: + pint_t getIsaVMAddr() const { return (pint_t)P::getP(isa); } + void setIsaVMAddr(pint_t newIsa) { P::setP(isa, newIsa); } + + const char *getName(ContentAccessor* cache) const { return (const char *)cache->contentForVMAddr(P::getP(name)); } + + uint32_t getSize() const { return P::E::get32(size); } + void setSize(uint32_t newSize) { P::E::set32(size, newSize); } + + uint32_t getFlags() const { return P::E::get32(flags); } + + void setFixedUp() { P::E::set32(flags, getFlags() | (1<<30)); } + + objc_protocol_list_t

*getProtocols(ContentAccessor* cache) const { return (objc_protocol_list_t

*)cache->contentForVMAddr(P::getP(protocols)); } + + objc_method_list_t

*getInstanceMethods(ContentAccessor* cache) const { return (objc_method_list_t

*)cache->contentForVMAddr(P::getP(instanceMethods)); } + + objc_method_list_t

*getClassMethods(ContentAccessor* cache) const { return (objc_method_list_t

*)cache->contentForVMAddr(P::getP(classMethods)); } + + objc_method_list_t

*getOptionalInstanceMethods(ContentAccessor* cache) const { return (objc_method_list_t

*)cache->contentForVMAddr(P::getP(optionalInstanceMethods)); } + + objc_method_list_t

*getOptionalClassMethods(ContentAccessor* cache) const { return (objc_method_list_t

*)cache->contentForVMAddr(P::getP(optionalClassMethods)); } + + objc_property_list_t

*getInstanceProperties(ContentAccessor* cache) const { return (objc_property_list_t

*)cache->contentForVMAddr(P::getP(instanceProperties)); } + + pint_t *getExtendedMethodTypes(ContentAccessor* cache) const { + if (getSize() < offsetof(objc_protocol_t

, extendedMethodTypes) + sizeof(extendedMethodTypes)) { + return NULL; + } + return (pint_t *)cache->contentForVMAddr(P::getP(extendedMethodTypes)); + } + + const char *getDemangledName(ContentAccessor* cache) const { + if (sizeof(*this) < offsetof(objc_protocol_t

, demangledName) + sizeof(demangledName)) { + return NULL; + } + return (const char *)cache->contentForVMAddr(P::getP(demangledName)); + } + + void setDemangledName(ContentAccessor* cache, const char *newName) { + if (sizeof(*this) < offsetof(objc_protocol_t

, demangledName) + sizeof(demangledName)) { + terminate("objc protocol has the wrong size"); + } + P::setP(demangledName, cache->vmAddrForContent((void*)newName)); + } + + void addPointers(std::vector& pointersToAdd) + { + pointersToAdd.push_back(&isa); + pointersToAdd.push_back(&name); + if (protocols) pointersToAdd.push_back(&protocols); + if (instanceMethods) pointersToAdd.push_back(&instanceMethods); + if (classMethods) pointersToAdd.push_back(&classMethods); + if (optionalInstanceMethods) pointersToAdd.push_back(&optionalInstanceMethods); + if (optionalClassMethods) pointersToAdd.push_back(&optionalClassMethods); + if (instanceProperties) pointersToAdd.push_back(&instanceProperties); + if (extendedMethodTypes) pointersToAdd.push_back(&extendedMethodTypes); + if (demangledName) pointersToAdd.push_back(&demangledName); + } +}; + + +template +class objc_protocol_list_t { + typedef typename P::uint_t pint_t; + pint_t count; + pint_t list[0]; + + void* operator new (size_t, void* buf) { return buf; } + +public: + + pint_t getCount() const { return (pint_t)P::getP(count); } + + pint_t getVMAddress(pint_t i) { + return (pint_t)P::getP(list[i]); + } + + objc_protocol_t

* get(ContentAccessor* cache, pint_t i) { + return (objc_protocol_t

*)cache->contentForVMAddr(getVMAddress(i)); + } + + void setVMAddress(pint_t i, pint_t protoVMAddr) { + P::setP(list[i], protoVMAddr); + } + + void set(ContentAccessor* cache, pint_t i, objc_protocol_t

* proto) { + setVMAddress(i, cache->vmAddrForContent(proto)); + } + + uint32_t byteSize() const { + return byteSizeForCount(getCount()); + } + static uint32_t byteSizeForCount(pint_t c) { + return sizeof(objc_protocol_list_t

) + c*sizeof(pint_t); + } + + void getPointers(std::set& pointersToRemove) { + for(int i=0 ; i < count; ++i) { + pointersToRemove.insert(&list[i]); + } + } + + static void addPointers(uint8_t* protocolList, std::vector& pointersToAdd) { + objc_protocol_list_t

* plist = (objc_protocol_list_t

*)protocolList; + for(int i=0 ; i < plist->count; ++i) { + pointersToAdd.push_back(&plist->list[i]); + } + } + + static objc_protocol_list_t

* newProtocolList(pint_t newCount) { + void *buf = ::calloc(byteSizeForCount(newCount), 1); + return new (buf) objc_protocol_list_t

(newCount); + } + + void operator delete(void * p) { + ::free(p); + } + + objc_protocol_list_t(uint32_t newCount) : count(newCount) { } +private: + // use newProtocolList instead + void* operator new (size_t); +}; + + +template +class objc_class_data_t { + typedef typename P::uint_t pint_t; + uint32_t flags; + uint32_t instanceStart; + // Note there is 4-bytes of alignment padding between instanceSize and ivarLayout + // on 64-bit archs, but no padding on 32-bit archs. + // This union is a way to model that. + union { + uint32_t instanceSize; + pint_t pad; + } instanceSize; + pint_t ivarLayout; + pint_t name; + pint_t baseMethods; + pint_t baseProtocols; + pint_t ivars; + pint_t weakIvarLayout; + pint_t baseProperties; + +public: + bool isMetaClass() { return P::E::get32(flags) & (1 << 0); } + bool isRootClass() { return P::E::get32(flags) & (1 << 1); } + + uint32_t getInstanceStart() { return P::E::get32(instanceStart); } + void setInstanceStart(uint32_t newStart) { P::E::set32(instanceStart, newStart); } + + uint32_t getInstanceSize() { return P::E::get32(instanceSize.instanceSize); } + void setInstanceSize(uint32_t newSiz) { P::E::set32(instanceSize.instanceSize, newSiz); } + + objc_method_list_t

*getMethodList(ContentAccessor* cache) const { return (objc_method_list_t

*)cache->contentForVMAddr(P::getP(baseMethods)); } + + objc_protocol_list_t

*getProtocolList(ContentAccessor* cache) const { return (objc_protocol_list_t

*)cache->contentForVMAddr(P::getP(baseProtocols)); } + + objc_ivar_list_t

*getIvarList(ContentAccessor* cache) const { return (objc_ivar_list_t

*)cache->contentForVMAddr(P::getP(ivars)); } + + objc_property_list_t

*getPropertyList(ContentAccessor* cache) const { return (objc_property_list_t

*)cache->contentForVMAddr(P::getP(baseProperties)); } + + const char * getName(ContentAccessor* cache) const { return (const char *)cache->contentForVMAddr(P::getP(name)); } + + void setMethodList(ContentAccessor* cache, objc_method_list_t

* mlist) { + P::setP(baseMethods, cache->vmAddrForContent(mlist)); + } + + void setProtocolList(ContentAccessor* cache, objc_protocol_list_t

* protolist) { + P::setP(baseProtocols, cache->vmAddrForContent(protolist)); + } + + void setPropertyList(ContentAccessor* cache, objc_property_list_t

* proplist) { + P::setP(baseProperties, cache->vmAddrForContent(proplist)); + } + + void addMethodListPointer(std::vector& pointersToAdd) { + pointersToAdd.push_back(&this->baseMethods); + } + + void addPropertyListPointer(std::vector& pointersToAdd) { + pointersToAdd.push_back(&this->baseProperties); + } + + void addProtocolListPointer(std::vector& pointersToAdd) { + pointersToAdd.push_back(&this->baseProtocols); + } +}; + +template +class objc_class_t { + typedef typename P::uint_t pint_t; + + pint_t isa; + pint_t superclass; + pint_t method_cache; + pint_t vtable; + pint_t data; + +public: + bool isMetaClass(ContentAccessor* cache) const { return getData(cache)->isMetaClass(); } + bool isRootClass(ContentAccessor* cache) const { return getData(cache)->isRootClass(); } + + objc_class_t

*getIsa(ContentAccessor* cache) const { return (objc_class_t

*)cache->contentForVMAddr(P::getP(isa)); } + + objc_class_t

*getSuperclass(ContentAccessor* cache) const { return (objc_class_t

*)cache->contentForVMAddr(P::getP(superclass)); } + + // Low bit marks Swift classes. + objc_class_data_t

*getData(ContentAccessor* cache) const { return (objc_class_data_t

*)cache->contentForVMAddr(P::getP(data & ~0x1LL)); } + + objc_method_list_t

*getMethodList(ContentAccessor* cache) const { + objc_class_data_t

* d = getData(cache); + return d->getMethodList(cache); + } + + objc_protocol_list_t

*getProtocolList(ContentAccessor* cache) const { return getData(cache)->getProtocolList(cache); } + + objc_property_list_t

*getPropertyList(ContentAccessor* cache) const { return getData(cache)->getPropertyList(cache); } + + const char* getName(ContentAccessor* cache) const { + return getData(cache)->getName(cache); + } + + void setMethodList(ContentAccessor* cache, objc_method_list_t

* mlist) { + getData(cache)->setMethodList(cache, mlist); + } + + void setProtocolList(ContentAccessor* cache, objc_protocol_list_t

* protolist) { + getData(cache)->setProtocolList(cache, protolist); + } + + void setPropertyList(ContentAccessor* cache, objc_property_list_t

* proplist) { + getData(cache)->setPropertyList(cache, proplist); + } + + void addMethodListPointer(ContentAccessor* cache, std::vector& pointersToAdd) { + getData(cache)->addMethodListPointer(pointersToAdd); + } + + void addPropertyListPointer(ContentAccessor* cache, std::vector& pointersToAdd) { + getData(cache)->addPropertyListPointer(pointersToAdd); + } + + void addProtocolListPointer(ContentAccessor* cache, std::vector& pointersToAdd) { + getData(cache)->addProtocolListPointer(pointersToAdd); + } + +}; + + + +template +class objc_category_t { + typedef typename P::uint_t pint_t; + + pint_t name; + pint_t cls; + pint_t instanceMethods; + pint_t classMethods; + pint_t protocols; + pint_t instanceProperties; + +public: + + const char * getName(ContentAccessor* cache) const { return (const char *)cache->contentForVMAddr(P::getP(name)); } + + objc_class_t

*getClass(ContentAccessor* cache) const { return (objc_class_t

*)cache->contentForVMAddr(P::getP(cls)); } + + objc_method_list_t

*getInstanceMethods(ContentAccessor* cache) const { return (objc_method_list_t

*)cache->contentForVMAddr(P::getP(instanceMethods)); } + + objc_method_list_t

*getClassMethods(ContentAccessor* cache) const { return (objc_method_list_t

*)cache->contentForVMAddr(P::getP(classMethods)); } + + objc_protocol_list_t

*getProtocols(ContentAccessor* cache) const { return (objc_protocol_list_t

*)cache->contentForVMAddr(P::getP(protocols)); } + + objc_property_list_t

*getInstanceProperties(ContentAccessor* cache) const { return (objc_property_list_t

*)cache->contentForVMAddr(P::getP(instanceProperties)); } + + void getPointers(std::set& pointersToRemove) { + pointersToRemove.insert(&name); + pointersToRemove.insert(&cls); + pointersToRemove.insert(&instanceMethods); + pointersToRemove.insert(&classMethods); + pointersToRemove.insert(&protocols); + pointersToRemove.insert(&instanceProperties); + } + + +}; + +template +class objc_message_ref_t { + typedef typename P::uint_t pint_t; + + pint_t imp; + pint_t sel; + +public: + pint_t getName() const { return (pint_t)P::getP(sel); } + + void setName(pint_t newName) { P::setP(sel, newName); } +}; + +// Call visitor.visitIvar() on every ivar in a given class. +template +class IvarWalker { + typedef typename P::uint_t pint_t; + V& ivarVisitor; +public: + + IvarWalker(V& visitor) : ivarVisitor(visitor) { } + + void walk(ContentAccessor* cache, const macho_header

* header, objc_class_t

*cls) + { + objc_class_data_t

*data = cls->getData(cache); + objc_ivar_list_t

*ivars = data->getIvarList(cache); + if (ivars) { + for (pint_t i = 0; i < ivars->getCount(); i++) { + objc_ivar_t

& ivar = ivars->get(i); + //fprintf(stderr, "visiting ivar: %s\n", ivar.getName(cache)); + ivarVisitor.visitIvar(cache, header, cls, &ivar); + } + } else { + //fprintf(stderr, "no ivars\n"); + } + } + + void visitClass(ContentAccessor* cache, const macho_header

* header, objc_class_t

*cls) + { + walk(cache, header, cls); + } +}; + +// Call visitor.visitClass() on every class. +template +class ClassWalker { + typedef typename P::uint_t pint_t; + V& _visitor; +public: + + ClassWalker(V& visitor) : _visitor(visitor) { } + + void walk(ContentAccessor* cache, const macho_header

* header) + { + PointerSection*> classList(cache, header, "__DATA", "__objc_classlist"); + + for (pint_t i = 0; i < classList.count(); i++) { + objc_class_t

* cls = classList.get(i); + //fprintf(stderr, "visiting class: %s\n", cls->getName(cache)); + if (cls) _visitor.visitClass(cache, header, cls); + } + } +}; + +// Call visitor.visitProtocol() on every protocol. +template +class ProtocolWalker { + typedef typename P::uint_t pint_t; + V& _protocolVisitor; +public: + + ProtocolWalker(V& visitor) : _protocolVisitor(visitor) { } + + void walk(ContentAccessor* cache, const macho_header

* header) + { + PointerSection *> + protocols(cache, header, "__DATA", "__objc_protolist"); + + for (pint_t i = 0; i < protocols.count(); i++) { + objc_protocol_t

*proto = protocols.get(i); + _protocolVisitor.visitProtocol(cache, header, proto); + } + } +}; + +// Call visitor.visitProtocolReference() on every protocol. +template +class ProtocolReferenceWalker { + typedef typename P::uint_t pint_t; + V& _visitor; + + void visitProtocolList(ContentAccessor* cache, + objc_protocol_list_t

* protolist) + { + if (!protolist) return; + for (pint_t i = 0; i < protolist->getCount(); i++) { + pint_t oldValue = protolist->getVMAddress(i); + pint_t newValue = _visitor.visitProtocolReference(cache, oldValue); + protolist->setVMAddress(i, newValue); + } + } + + friend class ClassWalker>; + + void visitClass(ContentAccessor* cache, const macho_header

*, + objc_class_t

* cls) + { + visitProtocolList(cache, cls->getProtocolList(cache)); + visitProtocolList(cache, cls->getIsa(cache)->getProtocolList(cache)); + } + +public: + + ProtocolReferenceWalker(V& visitor) : _visitor(visitor) { } + void walk(ContentAccessor* cache, const macho_header

* header) + { + // @protocol expressions + PointerSection *> + protorefs(cache, header, "__DATA", "__objc_protorefs"); + for (pint_t i = 0; i < protorefs.count(); i++) { + pint_t oldValue = protorefs.getVMAddress(i); + pint_t newValue = _visitor.visitProtocolReference(cache, oldValue); + protorefs.setVMAddress(i, newValue); + } + + // protocol lists in classes + ClassWalker> classes(*this); + classes.walk(cache, header); + + // protocol lists in protocols + // __objc_protolists itself is NOT updated + PointerSection *> + protocols(cache, header, "__DATA", "__objc_protolist"); + for (pint_t i = 0; i < protocols.count(); i++) { + objc_protocol_t

* proto = protocols.get(i); + visitProtocolList(cache, proto->getProtocols(cache)); + // not recursive: every old protocol object + // must be in some protolist section somewhere + } + } +}; + +// Call visitor.visitMethodList(mlist) on every +// class and category method list in a header. +// Call visitor.visitProtocolMethodList(mlist, typelist) on every +// protocol method list in a header. +template +class MethodListWalker { + + typedef typename P::uint_t pint_t; + + V& mVisitor; + +public: + + MethodListWalker(V& visitor) : mVisitor(visitor) { } + + void walk(ContentAccessor* cache, const macho_header

* header) + { + // Method lists in classes + PointerSection *> + classes(cache, header, "__DATA", "__objc_classlist"); + + for (pint_t i = 0; i < classes.count(); i++) { + objc_class_t

*cls = classes.get(i); + objc_method_list_t

*mlist; + if ((mlist = cls->getMethodList(cache))) { + mVisitor.visitMethodList(mlist); + } + if ((mlist = cls->getIsa(cache)->getMethodList(cache))) { + mVisitor.visitMethodList(mlist); + } + } + + // Method lists from categories + PointerSection *> + cats(cache, header, "__DATA", "__objc_catlist"); + for (pint_t i = 0; i < cats.count(); i++) { + objc_category_t

*cat = cats.get(i); + objc_method_list_t

*mlist; + if ((mlist = cat->getInstanceMethods(cache))) { + mVisitor.visitMethodList(mlist); + } + if ((mlist = cat->getClassMethods(cache))) { + mVisitor.visitMethodList(mlist); + } + } + + // Method description lists from protocols + PointerSection *> + protocols(cache, header, "__DATA", "__objc_protolist"); + for (pint_t i = 0; i < protocols.count(); i++) { + objc_protocol_t

*proto = protocols.get(i); + objc_method_list_t

*mlist; + pint_t *typelist = proto->getExtendedMethodTypes(cache); + + if ((mlist = proto->getInstanceMethods(cache))) { + mVisitor.visitProtocolMethodList(mlist, typelist); + if (typelist) typelist += mlist->getCount(); + } + if ((mlist = proto->getClassMethods(cache))) { + mVisitor.visitProtocolMethodList(mlist, typelist); + if (typelist) typelist += mlist->getCount(); + } + if ((mlist = proto->getOptionalInstanceMethods(cache))) { + mVisitor.visitProtocolMethodList(mlist, typelist); + if (typelist) typelist += mlist->getCount(); + } + if ((mlist = proto->getOptionalClassMethods(cache))) { + mVisitor.visitProtocolMethodList(mlist, typelist); + if (typelist) typelist += mlist->getCount(); + } + } + } +}; + + +// Update selector references. The visitor performs recording and uniquing. +template +class SelectorOptimizer { + + typedef typename P::uint_t pint_t; + + V& mVisitor; + + friend class MethodListWalker >; + void visitMethodList(objc_method_list_t

*mlist) + { + // Gather selectors. Update method names. + for (uint32_t m = 0; m < mlist->getCount(); m++) { + pint_t oldValue = mlist->get(m).getName(); + pint_t newValue = mVisitor.visit(oldValue); + mlist->get(m).setName(newValue); + } + // Do not setFixedUp: the methods are not yet sorted. + } + + void visitProtocolMethodList(objc_method_list_t

*mlist, pint_t *types) + { + visitMethodList(mlist); + } + +public: + + SelectorOptimizer(V& visitor) : mVisitor(visitor) { } + + void optimize(ContentAccessor* cache, const macho_header

* header) + { + // method lists in classes, categories, and protocols + MethodListWalker > mw(*this); + mw.walk(cache, header); + + // @selector references + PointerSection + selrefs(cache, header, "__DATA", "__objc_selrefs"); + for (pint_t i = 0; i < selrefs.count(); i++) { + pint_t oldValue = selrefs.getVMAddress(i); + pint_t newValue = mVisitor.visit(oldValue); + selrefs.setVMAddress(i, newValue); + } + + // message references + ArraySection > + msgrefs(cache, header, "__DATA", "__objc_msgrefs"); + for (pint_t i = 0; i < msgrefs.count(); i++) { + objc_message_ref_t

& msg = msgrefs.get(i); + pint_t oldValue = msg.getName(); + pint_t newValue = mVisitor.visit(oldValue); + msg.setName(newValue); + } + } +}; + + +// Update selector references. The visitor performs recording and uniquing. +template +class IvarOffsetOptimizer { + uint32_t _slide; + uint32_t _maxAlignment; + uint32_t _optimized; + +public: + + IvarOffsetOptimizer() : _optimized(0) { } + + size_t optimized() const { return _optimized; } + + // dual purpose ivar visitor function + // if slide!=0 then slides the ivar by that amount, otherwise computes _maxAlignment + void visitIvar(ContentAccessor* cache, const macho_header

* /*unused, may be NULL*/, objc_class_t

*cls, objc_ivar_t

*ivar) + { + if (_slide == 0) { + uint32_t alignment = ivar->getAlignment(); + if (alignment > _maxAlignment) _maxAlignment = alignment; + } else { + // skip anonymous bitfields + if (ivar->hasOffset()) { + uint32_t oldOffset = (uint32_t)ivar->getOffset(cache); + ivar->setOffset(cache, oldOffset + _slide); + _optimized++; + //fprintf(stderr, "%d -> %d for %s.%s\n", oldOffset, oldOffset + _slide, cls->getName(cache), ivar->getName(cache)); + } else { + //fprintf(stderr, "NULL offset\n"); + } + } + } + + // Class visitor function. Evaluates whether to slide ivars and performs slide if needed. + // The slide algorithm is also implemented in objc. Any changes here should be reflected there also. + void visitClass(ContentAccessor* cache, const macho_header

* /*unused, may be NULL*/, objc_class_t

*cls) + { + objc_class_t

*super = cls->getSuperclass(cache); + if (super) { + // Recursively visit superclasses to ensure we have the correct superclass start + // Note that we don't need the macho_header, so just pass NULL. + visitClass(cache, nullptr, super); + + objc_class_data_t

*data = cls->getData(cache); + objc_class_data_t

*super_data = super->getData(cache); + int32_t diff = super_data->getInstanceSize() - data->getInstanceStart(); + if (diff > 0) { + IvarWalker > ivarVisitor(*this); + _maxAlignment = 1; + _slide = 0; + + // This walk computes _maxAlignment + ivarVisitor.walk(cache, nullptr, cls); + + // Compute a slide value that preserves that alignment + uint32_t alignMask = _maxAlignment - 1; + if (diff & alignMask) diff = (diff + alignMask) & ~alignMask; + + // Slide all of this class's ivars en masse + _slide = diff; + if (_slide != 0) { + //fprintf(stderr, "Sliding ivars in %s by %u (superclass was %d, now %d)\n", cls->getName(cache), _slide, data->getInstanceStart(), super_data->getInstanceSize()); + ivarVisitor.walk(cache, nullptr, cls); + data->setInstanceStart(data->getInstanceStart() + _slide); + data->setInstanceSize(data->getInstanceSize() + _slide); + } + } + } + } + + // Enumerates objc classes in the module and performs any ivar slides + void optimize(ContentAccessor* cache, const macho_header

* header) + { + // The slide code cannot fix up GC layout strings so skip modules that support or require GC + const macho_section

*imageInfoSection = header->getSection("__DATA", "__objc_imageinfo"); + if (imageInfoSection) { + objc_image_info

*info = (objc_image_info

*)cache->contentForVMAddr(imageInfoSection->addr()); + if (!info->supportsGCFlagSet() && !info->requiresGCFlagSet()) { + ClassWalker > classVisitor(*this); + classVisitor.walk(cache, header); + } else { + //fprintf(stderr, "GC support present - skipped module\n"); + } + } + } +}; + + +// Detect classes that have missing weak-import superclasses. +template +class WeakClassDetector { + bool noMissing; + + friend class ClassWalker>; + void visitClass(ContentAccessor* cache, const macho_header

*, + objc_class_t

* cls) + { + auto supercls = cls->getSuperclass(cache); + if (supercls) { + // okay: class with superclass + // Note that the superclass itself might have a missing superclass. + // That is fine for mere detection because we will visit the + // superclass separately. + } else if (cls->isRootClass(cache)) { + // okay: root class is expected to have no superclass + } else { + // bad: cls's superclass is missing. + warning("Superclass of class '%s' is weak-import and missing.", + cls->getName(cache)); + noMissing = false; + } + } + +public: + bool noMissingWeakSuperclasses(ContentAccessor* cache, + std::vector*> dylibs) + { + noMissing = true; + ClassWalker> classes(*this); + for (auto mh : dylibs) { + classes.walk(cache, mh); + } + return noMissing; + } +}; + + +// Sort methods in place by selector. +template +class MethodListSorter { + + typedef typename P::uint_t pint_t; + + uint32_t _optimized; + + friend class MethodListWalker >; + void visitMethodList(objc_method_list_t

*mlist) + { + typename objc_method_t

::SortBySELAddress sorter; + std::stable_sort(mlist->begin(), mlist->end(), sorter); + mlist->setFixedUp(); + _optimized++; + } + + void visitProtocolMethodList(objc_method_list_t

*mlist, pint_t *typelist) + { + typename objc_method_t

::SortBySELAddress sorter; + // can't easily use std::stable_sort here + for (uint32_t i = 0; i < mlist->getCount(); i++) { + for (uint32_t j = i+1; j < mlist->getCount(); j++) { + objc_method_t

& mi = mlist->get(i); + objc_method_t

& mj = mlist->get(j); + if (! sorter(mi, mj)) { + std::swap(mi, mj); + if (typelist) std::swap(typelist[i], typelist[j]); + } + } + } + + mlist->setFixedUp(); + _optimized++; + } + +public: + MethodListSorter() : _optimized(0) { } + + size_t optimized() const { return _optimized; } + + void optimize(ContentAccessor* cache, const macho_header

* header) + { + MethodListWalker > mw(*this); + mw.walk(cache, header); + } +}; + + +template +class HeaderInfoOptimizer { +public: + + typedef typename P::uint_t pint_t; + + HeaderInfoOptimizer() : _hInfos(0), _count(0) { } + + const char* init(uint32_t count, uint8_t*& buf, size_t& bufSize) { + if (count == 0) + return nullptr; + + size_t requiredSize = + 2*sizeof(uint32_t) + count*sizeof(InfoT); + if (bufSize < requiredSize) { + return "libobjc's read/write section is too small (metadata not optimized)"; + } + + uint32_t *buf32 = (uint32_t *)buf; + P::E::set32(buf32[0], count); + P::E::set32(buf32[1], sizeof(InfoT)); + _hInfos = (InfoT*)(buf32+2); + + buf += requiredSize; + bufSize -= requiredSize; + + return nullptr; + } + + void update(ContentAccessor* cache, const macho_header

* mh, std::vector& pointersInData) { + InfoT* hi = new(&_hInfos[_count++]) InfoT(cache, mh); + (void)hi; + } + + InfoT* hinfoForHeader(ContentAccessor* cache, const macho_header

* mh) { + // FIXME: could be binary search + uint64_t mh_vmaddr = cache->vmAddrForContent((void*)mh); + for (size_t i = 0; i < _count; i++) { + InfoT* hi = &_hInfos[i]; + if (hi->header_vmaddr(cache) == mh_vmaddr) return hi; + } + return nullptr; + } +private: + InfoT* _hInfos; + size_t _count; +}; diff --git a/src/dyld-apple/interlinked-dylibs/OptimizerBranches.cpp b/src/dyld-apple/interlinked-dylibs/OptimizerBranches.cpp new file mode 100644 index 000000000..c982e9509 --- /dev/null +++ b/src/dyld-apple/interlinked-dylibs/OptimizerBranches.cpp @@ -0,0 +1,1472 @@ +/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- + * + * Copyright (c) 2015 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@ + */ + +#include "OptimizerBranches.h" +#include "Trie.hpp" +#include "MachOFileAbstraction.hpp" +#include "Logging.h" + +#include "dyld_cache_config.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + + +static const bool verbose = false; + +// These are functions that are interposed by Instruments.app or ASan +static const char* sNeverStubEliminateSymbols[] = { + "___bzero", + "___cxa_atexit", + "___cxa_throw", + "__longjmp", + "__objc_autoreleasePoolPop", + "_accept", + "_access", + "_asctime", + "_asctime_r", + "_asprintf", + "_atoi", + "_atol", + "_atoll", + "_calloc", + "_chmod", + "_chown", + "_close", + "_confstr", + "_ctime", + "_ctime_r", + "_dispatch_after", + "_dispatch_after_f", + "_dispatch_async", + "_dispatch_async_f", + "_dispatch_barrier_async_f", + "_dispatch_group_async", + "_dispatch_group_async_f", + "_dispatch_source_set_cancel_handler", + "_dispatch_source_set_event_handler", + "_dispatch_sync_f", + "_dlclose", + "_dlopen", + "_dup", + "_dup2", + "_endgrent", + "_endpwent", + "_ether_aton", + "_ether_hostton", + "_ether_line", + "_ether_ntoa", + "_ether_ntohost", + "_fchmod", + "_fchown", + "_fclose", + "_fdopen", + "_fflush", + "_fopen", + "_fork", + "_fprintf", + "_free", + "_freopen", + "_frexp", + "_frexpf", + "_frexpl", + "_fscanf", + "_fstat", + "_fstatfs", + "_fstatfs64", + "_fsync", + "_ftime", + "_getaddrinfo", + "_getattrlist", + "_getcwd", + "_getgrent", + "_getgrgid", + "_getgrgid_r", + "_getgrnam", + "_getgrnam_r", + "_getgroups", + "_gethostbyaddr", + "_gethostbyname", + "_gethostbyname2", + "_gethostent", + "_getifaddrs", + "_getitimer", + "_getnameinfo", + "_getpass", + "_getpeername", + "_getpwent", + "_getpwnam", + "_getpwnam_r", + "_getpwuid", + "_getpwuid_r", + "_getsockname", + "_getsockopt", + "_gmtime", + "_gmtime_r", + "_if_indextoname", + "_if_nametoindex", + "_index", + "_inet_aton", + "_inet_ntop", + "_inet_pton", + "_initgroups", + "_ioctl", + "_lchown", + "_lgamma", + "_lgammaf", + "_lgammal", + "_link", + "_listxattr", + "_localtime", + "_localtime_r", + "_longjmp", + "_lseek", + "_lstat", + "_malloc", + "_malloc_create_zone", + "_malloc_default_purgeable_zone", + "_malloc_default_zone", + "_malloc_good_size", + "_malloc_make_nonpurgeable", + "_malloc_make_purgeable", + "_malloc_set_zone_name", + "_mbsnrtowcs", + "_mbsrtowcs", + "_mbstowcs", + "_memchr", + "_memcmp", + "_memcpy", + "_memmove", + "_memset", + "_mktime", + "_mlock", + "_mlockall", + "_modf", + "_modff", + "_modfl", + "_munlock", + "_munlockall", + "_objc_autoreleasePoolPop", + "_objc_setProperty", + "_objc_setProperty_atomic", + "_objc_setProperty_atomic_copy", + "_objc_setProperty_nonatomic", + "_objc_setProperty_nonatomic_copy", + "_objc_storeStrong", + "_open", + "_opendir", + "_poll", + "_posix_memalign", + "_pread", + "_printf", + "_pthread_attr_getdetachstate", + "_pthread_attr_getguardsize", + "_pthread_attr_getinheritsched", + "_pthread_attr_getschedparam", + "_pthread_attr_getschedpolicy", + "_pthread_attr_getscope", + "_pthread_attr_getstack", + "_pthread_attr_getstacksize", + "_pthread_condattr_getpshared", + "_pthread_create", + "_pthread_getschedparam", + "_pthread_join", + "_pthread_mutex_lock", + "_pthread_mutex_unlock", + "_pthread_mutexattr_getprioceiling", + "_pthread_mutexattr_getprotocol", + "_pthread_mutexattr_getpshared", + "_pthread_mutexattr_gettype", + "_pthread_rwlockattr_getpshared", + "_pwrite", + "_rand_r", + "_read", + "_readdir", + "_readdir_r", + "_readv", + "_readv$UNIX2003", + "_realloc", + "_realpath", + "_recv", + "_recvfrom", + "_recvmsg", + "_remquo", + "_remquof", + "_remquol", + "_scanf", + "_send", + "_sendmsg", + "_sendto", + "_setattrlist", + "_setgrent", + "_setitimer", + "_setlocale", + "_setpwent", + "_shm_open", + "_shm_unlink", + "_sigaction", + "_sigemptyset", + "_sigfillset", + "_siglongjmp", + "_signal", + "_sigpending", + "_sigprocmask", + "_sigwait", + "_snprintf", + "_sprintf", + "_sscanf", + "_stat", + "_statfs", + "_statfs64", + "_strcasecmp", + "_strcat", + "_strchr", + "_strcmp", + "_strcpy", + "_strdup", + "_strerror", + "_strerror_r", + "_strlen", + "_strncasecmp", + "_strncat", + "_strncmp", + "_strncpy", + "_strptime", + "_strtoimax", + "_strtol", + "_strtoll", + "_strtoumax", + "_tempnam", + "_time", + "_times", + "_tmpnam", + "_tsearch", + "_unlink", + "_valloc", + "_vasprintf", + "_vfprintf", + "_vfscanf", + "_vprintf", + "_vscanf", + "_vsnprintf", + "_vsprintf", + "_vsscanf", + "_wait", + "_wait$UNIX2003", + "_wait3", + "_wait4", + "_waitid", + "_waitid$UNIX2003", + "_waitpid", + "_waitpid$UNIX2003", + "_wcslen", + "_wcsnrtombs", + "_wcsrtombs", + "_wcstombs", + "_wordexp", + "_write", + "_writev", + "_writev$UNIX2003", + // always use stubs for C++ symbols that can be overridden + "__ZdaPv", + "__ZdlPv", + "__Znam", + "__Znwm", + + nullptr +}; + + +// These are dylibs that are interposed and stubs calling into them should never be bypassed +static const char* sNeverStubEliminateDylibs[] = { + "/usr/lib/system/libdispatch.dylib", + nullptr +}; + + + +uint64_t branchPoolTextSize(ArchPair arch) +{ + if ( arch.arch == CPU_TYPE_ARM64 ) + return 0x0000C000; // 48KB + else + return 0; +} + +uint64_t branchPoolLinkEditSize(ArchPair arch) +{ + if ( arch.arch == CPU_TYPE_ARM64 ) + return 0x00100000; // 1MB + else + return 0; +} + + +uint64_t branchReach(ArchPair arch) +{ + if ( arch.arch == CPU_TYPE_ARM64 ) + return 0x08000000 - (2*branchPoolTextSize(arch)); // 128MB + else + return 0; +} + +template +class BranchPoolDylib { +public: + BranchPoolDylib(ArchPair arch, void* cacheBuffer, uint64_t cacheSize, uint64_t startAddr, + uint64_t textRegionStartAddr, uint64_t poolLinkEditStartAddr, uint64_t poolLinkEditStartOffset); + + uint64_t addr() { return _startAddr; } + uint64_t getForwardBranch(uint64_t finalTargetAddr, const char* name, std::vector*>& branchIslandPools); + uint64_t getBackBranch(uint64_t finalTargetAddr, const char* name, std::vector*>& branchIslandPools); + void finalizeLoadCommands(); + void printStats(); + +private: + uint64_t indexToAddr(uint32_t index) { return _startAddr + _firstStubOffset + sizeof(uint32_t)*index; } + + static const int64_t b128MegLimit = 0x07FFFFFF; + + typedef typename P::uint_t pint_t; + typedef typename P::E E; + + ArchPair _arch; + void* _cacheBuffer; + uint64_t _startAddr; + std::unordered_map _targetToIslandIndex; + std::unordered_map _islandIndexToName; + macho_symtab_command

* _symbolTableCmd; + macho_dysymtab_command

* _dynamicSymbolTableCmd; + macho_uuid_command

* _uuidCmd; + uint32_t _maxStubs; + uint32_t _nextIndex; + uint32_t _firstStubOffset; + uint32_t* _stubInstructions; + macho_nlist

* _symbolTable; + char* _nextString; + char* _stringPoolStart; + char* _stringPoolEnd; +}; + + +template +BranchPoolDylib

::BranchPoolDylib(ArchPair arch, void* cacheBuffer, uint64_t cacheSize, uint64_t poolStartAddr, + uint64_t textRegionStartAddr, uint64_t poolLinkEditStartAddr, uint64_t poolLinkEditStartOffset) + : _arch(arch), _cacheBuffer(cacheBuffer),_startAddr(poolStartAddr), _nextIndex(0), _firstStubOffset(0x280) +{ + bool is64 = (sizeof(typename P::uint_t) == 8); + + const uint64_t textSegSize = branchPoolTextSize(arch); + const uint64_t linkEditSegSize = branchPoolLinkEditSize(arch); + const unsigned stubCount = (unsigned)((textSegSize - _firstStubOffset)/4); + const uint32_t linkeditOffsetSymbolTable = 0; + const uint32_t linkeditOffsetIndirectSymbolTable = stubCount*sizeof(macho_nlist

); + const uint32_t linkeditOffsetSymbolPoolOffset = linkeditOffsetIndirectSymbolTable + stubCount*sizeof(uint32_t); + _maxStubs = stubCount; + + // write mach_header and load commands for pseudo dylib + macho_header

* mh = (macho_header

*)((uint8_t*)cacheBuffer + poolStartAddr - textRegionStartAddr); + mh->set_magic(is64 ? MH_MAGIC_64 : MH_MAGIC); + mh->set_cputype(arch.arch); + mh->set_cpusubtype(arch.subtype); + mh->set_filetype(MH_DYLIB); + mh->set_ncmds(6); + mh->set_sizeofcmds(is64 ? 0x210 : 100); // FIXME: 32-bit size + mh->set_flags(0x80000000); + // LC_SEGMENT + macho_load_command

* cmd = (macho_load_command

*)((uint8_t*)mh + sizeof(macho_header

)); + macho_segment_command

* textSegCmd = (macho_segment_command

*)cmd; + textSegCmd->set_cmd(is64 ? LC_SEGMENT_64 : LC_SEGMENT); + textSegCmd->set_cmdsize(sizeof(macho_segment_command

)*2+sizeof(macho_section

)); + textSegCmd->set_segname("__TEXT"); + textSegCmd->set_vmaddr(poolStartAddr); + textSegCmd->set_vmsize(textSegSize); + textSegCmd->set_fileoff(poolStartAddr - textRegionStartAddr); + textSegCmd->set_filesize(branchPoolTextSize(arch)); + textSegCmd->set_maxprot(PROT_READ|PROT_EXEC); + textSegCmd->set_initprot(PROT_READ|PROT_EXEC); + textSegCmd->set_nsects(1); + textSegCmd->set_flags(0); + macho_section

* stubSection = (macho_section

*)((uint8_t*)textSegCmd + sizeof(macho_segment_command

)); + stubSection->set_sectname("__stubs"); + stubSection->set_segname("__TEXT"); + stubSection->set_addr(poolStartAddr + _firstStubOffset); + stubSection->set_size(textSegSize - _firstStubOffset); + stubSection->set_offset((uint32_t)(poolStartAddr + _firstStubOffset - textRegionStartAddr)); + stubSection->set_align(2); + stubSection->set_reloff(0); + stubSection->set_nreloc(0); + stubSection->set_flags(S_SYMBOL_STUBS | S_ATTR_SOME_INSTRUCTIONS | S_ATTR_PURE_INSTRUCTIONS); + stubSection->set_reserved1(0); // start index in indirect table + stubSection->set_reserved2(4); // size of stubs + // LC_SEGMENT + cmd = (macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); + macho_segment_command

* linkEditSegCmd = (macho_segment_command

*)cmd; + linkEditSegCmd->set_cmd(is64 ? LC_SEGMENT_64 : LC_SEGMENT); + linkEditSegCmd->set_cmdsize(sizeof(macho_segment_command

)); + linkEditSegCmd->set_segname("__LINKEDIT"); + linkEditSegCmd->set_vmaddr(poolLinkEditStartAddr); + linkEditSegCmd->set_vmsize(linkEditSegSize); + linkEditSegCmd->set_fileoff(poolLinkEditStartOffset); + linkEditSegCmd->set_filesize(linkEditSegSize); + linkEditSegCmd->set_maxprot(PROT_READ); + linkEditSegCmd->set_initprot(PROT_READ); + linkEditSegCmd->set_nsects(0); + linkEditSegCmd->set_flags(0); + // LC_ID_DYLIB + cmd = (macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); + macho_dylib_command

* installNameCmd = (macho_dylib_command

*)cmd; + installNameCmd->set_cmd(LC_ID_DYLIB); + installNameCmd->set_cmdsize(sizeof(macho_dylib_command

) + 48); + installNameCmd->set_timestamp(2); + installNameCmd->set_current_version(0x10000); + installNameCmd->set_compatibility_version(0x10000); + installNameCmd->set_name_offset(); + strcpy((char*)cmd + sizeof(macho_dylib_command

), "dyld_shared_cache_branch_islands"); + // LC_SYMTAB + cmd = (macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); + _symbolTableCmd = (macho_symtab_command

*)cmd; + _symbolTableCmd->set_cmd(LC_SYMTAB); + _symbolTableCmd->set_cmdsize(sizeof(macho_symtab_command

)); + _symbolTableCmd->set_nsyms(stubCount); + _symbolTableCmd->set_symoff((uint32_t)(poolLinkEditStartOffset + linkeditOffsetSymbolTable)); + _symbolTableCmd->set_stroff((uint32_t)(poolLinkEditStartOffset + linkeditOffsetSymbolPoolOffset)); + _symbolTableCmd->set_strsize((uint32_t)(linkEditSegSize - linkeditOffsetSymbolPoolOffset)); + // LC_DYSYMTAB + cmd = (macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); + _dynamicSymbolTableCmd = (macho_dysymtab_command

*)cmd; + _dynamicSymbolTableCmd->set_cmd(LC_DYSYMTAB); + _dynamicSymbolTableCmd->set_cmdsize(sizeof(macho_dysymtab_command

)); + _dynamicSymbolTableCmd->set_ilocalsym(0); + _dynamicSymbolTableCmd->set_nlocalsym(0); + _dynamicSymbolTableCmd->set_iextdefsym(0); + _dynamicSymbolTableCmd->set_nextdefsym(0); + _dynamicSymbolTableCmd->set_iundefsym(0); + _dynamicSymbolTableCmd->set_nundefsym(stubCount); + _dynamicSymbolTableCmd->set_tocoff(0); + _dynamicSymbolTableCmd->set_ntoc(0); + _dynamicSymbolTableCmd->set_modtaboff(0); + _dynamicSymbolTableCmd->set_nmodtab(0); + _dynamicSymbolTableCmd->set_extrefsymoff(0); + _dynamicSymbolTableCmd->set_nextrefsyms(0); + _dynamicSymbolTableCmd->set_indirectsymoff((uint32_t)(poolLinkEditStartOffset + linkeditOffsetIndirectSymbolTable)); + _dynamicSymbolTableCmd->set_nindirectsyms(stubCount); + _dynamicSymbolTableCmd->set_extreloff(0); + _dynamicSymbolTableCmd->set_nextrel(0); + _dynamicSymbolTableCmd->set_locreloff(0); + _dynamicSymbolTableCmd->set_nlocrel(0); + cmd = (macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); + // LC_UUID + _uuidCmd = (macho_uuid_command

*)cmd; + _uuidCmd->set_cmd(LC_UUID); + _uuidCmd->set_cmdsize(sizeof(macho_uuid_command

)); + cmd = (macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); + + // write stubs section content + _stubInstructions = (uint32_t*)((uint8_t*)mh + _firstStubOffset); + for (int i=0; i < stubCount; ++i) { + E::set32(_stubInstructions[i], 0xD4200000); + } + + // write linkedit content + uint8_t* linkeditBufferStart = (uint8_t*)cacheBuffer + poolLinkEditStartOffset; + // write symbol table + _symbolTable = (macho_nlist

*)(linkeditBufferStart); + for (int i=0; i < stubCount; ++i) { + _symbolTable[i].set_n_strx(1); + _symbolTable[i].set_n_type(N_UNDF | N_EXT); + _symbolTable[i].set_n_sect(0); + _symbolTable[i].set_n_desc(0); + _symbolTable[i].set_n_value(0); + } + // write indirect symbol table + uint32_t* indirectSymboTable = (uint32_t*)(linkeditBufferStart + linkeditOffsetIndirectSymbolTable); + for (int i=0; i < stubCount; ++i) { + P::E::set32(indirectSymboTable[i], i); + } + // write string pool + _stringPoolStart = (char*)(linkeditBufferStart + linkeditOffsetSymbolPoolOffset); + _stringPoolEnd = _stringPoolStart + linkEditSegSize - linkeditOffsetSymbolPoolOffset; + _stringPoolStart[0] = '\0'; + strcpy(&_stringPoolStart[1], ""); + _nextString = &_stringPoolStart[10]; +} + + +template +void BranchPoolDylib

::finalizeLoadCommands() +{ + _symbolTableCmd->set_nsyms(_nextIndex); + _symbolTableCmd->set_strsize((uint32_t)(_nextString - _stringPoolStart)); + _dynamicSymbolTableCmd->set_nundefsym(_nextIndex); + + uint8_t digest[CC_MD5_DIGEST_LENGTH]; + CC_MD5(_stubInstructions, _maxStubs*sizeof(uint64_t), digest); + _uuidCmd->set_uuid(digest); + + if ( verbose ) { + verboseLog("branch islands in image at 0x%0llX:", _startAddr); + for (uint32_t i=0; i < _nextIndex; ++i) { + verboseLog(" 0x%llX %s", indexToAddr(i), _islandIndexToName[i]); + } + } +} + +template +uint64_t BranchPoolDylib

::getForwardBranch(uint64_t finalTargetAddr, const char* name, std::vector*>& branchIslandPools) +{ + // check if we can re-used existing branch island + const auto& pos = _targetToIslandIndex.find(finalTargetAddr); + if ( pos != _targetToIslandIndex.end() ) + return indexToAddr(pos->second); + + // skip if instruction pool is full + if ( _nextIndex >= _maxStubs ) + return 0; + + // skip if string pool is full + if ( (_nextString + strlen(name)+1) >= _stringPoolEnd ) + return 0; + + uint64_t branchIslandTargetAddr = finalTargetAddr; + // if final target is too far, we need to use branch island in next pool + if ( (finalTargetAddr - _startAddr) > b128MegLimit ) { + BranchPoolDylib

* nextPool = nullptr; + for (size_t i=0; i < branchIslandPools.size()-1; ++i) { + if ( branchIslandPools[i] == this ) { + nextPool = branchIslandPools[i+1]; + break; + } + } + + if (nextPool == nullptr) { + warning("BranchPoolDylib

::getForwardBranch: nextPool unreachable"); + return 0; + } + + branchIslandTargetAddr = nextPool->getForwardBranch(finalTargetAddr, name, branchIslandPools); + if ( branchIslandTargetAddr == 0 ) + return 0; // next pool is full + } + + // write branch instruction in stubs section + uint32_t index = _nextIndex++; + int64_t branchDelta = branchIslandTargetAddr - indexToAddr(index); + uint32_t branchInstr = 0x14000000 + ((branchDelta/4) & 0x03FFFFFF); + E::set32(_stubInstructions[index], branchInstr); + + // update symbol table + _symbolTable[index].set_n_strx((uint32_t)(_nextString - _stringPoolStart)); + strcpy(_nextString, name); + _nextString += (strlen(name) +1); + + // record island + _targetToIslandIndex[finalTargetAddr] = index; + _islandIndexToName[index] = name; + return indexToAddr(index); +} + +template +uint64_t BranchPoolDylib

::getBackBranch(uint64_t finalTargetAddr, const char* name, std::vector*>& branchIslandPools) +{ + // check if we can re-used existing branch island + const auto& pos = _targetToIslandIndex.find(finalTargetAddr); + if ( pos != _targetToIslandIndex.end() ) + return indexToAddr(pos->second); + + // skip if instruction pool is full + if ( _nextIndex >= _maxStubs ) + return 0; + + // skip if string pool is full + if ( (_nextString + strlen(name)+1) >= _stringPoolEnd ) + return 0; + + uint64_t branchIslandTargetAddr = finalTargetAddr; + // if final target is too far, we need to use branch island in next pool + if ( (indexToAddr(_nextIndex) - finalTargetAddr) > b128MegLimit ) { + BranchPoolDylib

* nextPool = nullptr; + for (long i=branchIslandPools.size()-1; i > 0; --i) { + if ( branchIslandPools[i] == this ) { + nextPool = branchIslandPools[i-1]; + break; + } + } + + if (nextPool == nullptr) { + warning("BranchPoolDylib

::getBackBranch: nextPool unreachable"); + return 0; + } + + branchIslandTargetAddr = nextPool->getBackBranch(finalTargetAddr, name, branchIslandPools); + if ( branchIslandTargetAddr == 0 ) + return 0; // next pool is full + } + + // write branch instruction in stubs section + uint32_t index = _nextIndex++; + int64_t branchDelta = branchIslandTargetAddr - indexToAddr(index); + uint32_t branchInstr = 0x14000000 + ((branchDelta/4) & 0x03FFFFFF); + E::set32(_stubInstructions[index], branchInstr); + + // update symbol table + _symbolTable[index].set_n_strx((uint32_t)(_nextString - _stringPoolStart)); + strcpy(_nextString, name); + _nextString += (strlen(name) +1); + + // record island + _targetToIslandIndex[finalTargetAddr] = index; + _islandIndexToName[index] = name; + return indexToAddr(index); +} + +template +void BranchPoolDylib

::printStats() +{ + verboseLog(" island pool at 0x%0llX has %u stubs and stringPool size=%lu", _startAddr, _nextIndex, _nextString-_stringPoolStart); +} + + + +template +class StubOptimizer { +public: + StubOptimizer(void* cacheBuffer, macho_header

* mh); + void buildStubMap(const std::unordered_set& neverStubEliminate); + void optimizeStubs(std::unordered_map>& targetToBranchIslands); + void bypassStubs(std::unordered_map>& targetToBranchIslands); + void optimizeCallSites(std::vector*>& branchIslandPools); + const char* installName() { return _installName; } + const uint8_t* exportsTrie() { return (uint8_t*)_cacheBuffer + _dyldInfo->export_off(); } + uint32_t exportsTrieSize() { return _dyldInfo->export_size(); } + + uint32_t _stubCount = 0; + uint32_t _stubOptimizedCount = 0; + uint32_t _branchesCount = 0; + uint32_t _branchesModifiedCount = 0; + uint32_t _branchesDirectCount = 0; + uint32_t _branchesIslandCount = 0; + +private: + + typedef std::function CallSiteHandler; + typedef typename P::uint_t pint_t; + typedef typename P::E E; + + void forEachCallSiteToAStub(CallSiteHandler); + void optimizeArm64CallSites(std::vector*>& branchIslandPools); + void optimizeArmCallSites(); + void optimizeArmStubs(); + uint64_t lazyPointerAddrFromArm64Stub(const uint8_t* stubInstructions, uint64_t stubVMAddr); + uint32_t lazyPointerAddrFromArmStub(const uint8_t* stubInstructions, uint32_t stubVMAddr); + int32_t getDisplacementFromThumbBranch(uint32_t instruction, uint32_t instrAddr); + uint32_t setDisplacementInThumbBranch(uint32_t instruction, uint32_t instrAddr, + int32_t displacement, bool targetIsThumb); + + + struct AddressAndName { pint_t targetVMAddr; const char* targetName; }; + typedef std::unordered_map StubVMAddrToTarget; + + static const int64_t b128MegLimit = 0x07FFFFFF; + static const int64_t b16MegLimit = 0x00FFFFFF; + + + macho_header

* _mh; + void* _cacheBuffer; + uint32_t _linkeditSize = 0; + uint32_t _linkeditCacheOffset = 0; + uint64_t _linkeditAddr = 0; + const uint8_t* _linkeditBias = nullptr; + const char* _installName = nullptr; + const macho_symtab_command

* _symTabCmd = nullptr; + const macho_dysymtab_command

* _dynSymTabCmd = nullptr; + const macho_dyld_info_command

* _dyldInfo = nullptr; + macho_linkedit_data_command

* _splitSegInfoCmd = nullptr; + const macho_section

* _textSection = nullptr; + const macho_section

* _stubSection = nullptr; + uint32_t _textSectionIndex = 0; + uint32_t _stubSectionIndex = 0; + pint_t _textSegStartAddr = 0; + uint32_t _textSegCacheOffset = 0; + std::vector*> _segCmds; + std::unordered_map _stubAddrToLPAddr; + std::unordered_map _lpAddrToTargetAddr; + std::unordered_map _targetAddrToName; +}; + + + +template +StubOptimizer

::StubOptimizer(void* cacheBuffer, macho_header

* mh) +: _mh(mh), _cacheBuffer(cacheBuffer) +{ + _linkeditBias = (uint8_t*)cacheBuffer; + const macho_load_command

* const cmds = (macho_load_command

*)((uint8_t*)mh + sizeof(macho_header

)); + const uint32_t cmd_count = mh->ncmds(); + macho_segment_command

* segCmd; + uint32_t sectionIndex = 0; + const macho_load_command

* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd()) { + case LC_ID_DYLIB: + _installName = ((macho_dylib_command

*)cmd)->name(); + break; + case LC_SYMTAB: + _symTabCmd = (macho_symtab_command

*)cmd; + break; + case LC_DYSYMTAB: + _dynSymTabCmd = (macho_dysymtab_command

*)cmd; + break; + case LC_SEGMENT_SPLIT_INFO: + _splitSegInfoCmd = (macho_linkedit_data_command

*)cmd; + break; + case LC_DYLD_INFO: + case LC_DYLD_INFO_ONLY: + _dyldInfo = (macho_dyld_info_command

*)cmd; + break; + case macho_segment_command

::CMD: + segCmd =( macho_segment_command

*)cmd; + _segCmds.push_back(segCmd); + if ( strcmp(segCmd->segname(), "__LINKEDIT") == 0 ) { + _linkeditSize = (uint32_t)segCmd->vmsize(); + _linkeditCacheOffset = (uint32_t)segCmd->fileoff(); + _linkeditAddr = segCmd->vmaddr(); + } + else if ( strcmp(segCmd->segname(), "__TEXT") == 0 ) { + _textSegStartAddr = (pint_t)segCmd->vmaddr(); + _textSegCacheOffset = (uint32_t)((uint8_t*)mh - (uint8_t*)cacheBuffer); + const macho_section

* const sectionsStart = (macho_section

*)((char*)segCmd + sizeof(macho_segment_command

)); + const macho_section

* const sectionsEnd = §ionsStart[segCmd->nsects()]; + for (const macho_section

* sect = sectionsStart; sect < sectionsEnd; ++sect) { + ++sectionIndex; + if ( strcmp(sect->sectname(), "__text") == 0 ) { + _textSection = sect; + _textSectionIndex = sectionIndex; + } + else if ( ((sect->flags() & SECTION_TYPE) == S_SYMBOL_STUBS) && (sect->size() != 0) ) { + _stubSection = sect; + _stubSectionIndex = sectionIndex; + } + } + } + break; + } + cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); + } +} + + + +template +uint32_t StubOptimizer

::lazyPointerAddrFromArmStub(const uint8_t* stubInstructions, uint32_t stubVMAddr) +{ + uint32_t stubInstr1 = E::get32(*(uint32_t*)stubInstructions); + uint32_t stubInstr2 = E::get32(*(uint32_t*)(stubInstructions+4)); + uint32_t stubInstr3 = E::get32(*(uint32_t*)(stubInstructions+8)); + int32_t stubData = E::get32(*(uint32_t*)(stubInstructions+12)); + if ( stubInstr1 != 0xe59fc004 ) { + warning("first instruction of stub (0x%08X) is not 'ldr ip, pc + 12' for stub at addr 0x%0llX in %s", + stubInstr1, (uint64_t)stubVMAddr, _installName); + return 0; + } + if ( stubInstr2 != 0xe08fc00c ) { + warning("second instruction of stub (0x%08X) is not 'add ip, pc, ip' for stub at addr 0x%0llX in %s", + stubInstr1, (uint64_t)stubVMAddr, _installName); + return 0; + } + if ( stubInstr3 != 0xe59cf000 ) { + warning("third instruction of stub (0x%08X) is not 'ldr pc, [ip]' for stub at addr 0x%0llX in %s", + stubInstr1, (uint64_t)stubVMAddr, _installName); + return 0; + } + return stubVMAddr + 12 + stubData; +} + + +template +uint64_t StubOptimizer

::lazyPointerAddrFromArm64Stub(const uint8_t* stubInstructions, uint64_t stubVMAddr) +{ + uint32_t stubInstr1 = E::get32(*(uint32_t*)stubInstructions); + if ( (stubInstr1 & 0x9F00001F) != 0x90000010 ) { + warning("first instruction of stub (0x%08X) is not ADRP for stub at addr 0x%0llX in %s", + stubInstr1, (uint64_t)stubVMAddr, _installName); + return 0; + } + int32_t adrpValue = ((stubInstr1 & 0x00FFFFE0) >> 3) | ((stubInstr1 & 0x60000000) >> 29); + if ( stubInstr1 & 0x00800000 ) + adrpValue |= 0xFFF00000; + uint32_t stubInstr2 = E::get32(*(uint32_t*)(stubInstructions + 4)); + if ( (stubInstr2 & 0xFFC003FF) != 0xF9400210 ) { + warning("second instruction of stub (0x%08X) is not LDR for stub at addr 0x%0llX in %s", + stubInstr2, (uint64_t)stubVMAddr, _installName); + return 0; + } + uint32_t ldrValue = ((stubInstr2 >> 10) & 0x00000FFF); + return (stubVMAddr & (-4096)) + adrpValue*4096 + ldrValue*8; +} + + + +template +void StubOptimizer

::buildStubMap(const std::unordered_set& neverStubEliminate) +{ + // find all stubs and lazy pointers + const macho_nlist

* symbolTable = (const macho_nlist

*)(((uint8_t*)_cacheBuffer) + _symTabCmd->symoff()); + const char* symbolStrings = (char*)_cacheBuffer + _symTabCmd->stroff(); + const uint32_t* const indirectTable = (uint32_t*)(((uint8_t*)_cacheBuffer) + _dynSymTabCmd->indirectsymoff()); + const macho_load_command

* const cmds = (macho_load_command

*)((uint8_t*)_mh + sizeof(macho_header

)); + const uint32_t cmd_count = _mh->ncmds(); + const macho_load_command

* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + if ( cmd->cmd() == macho_segment_command

::CMD ) { + macho_segment_command

* seg = (macho_segment_command

*)cmd; + macho_section

* const sectionsStart = (macho_section

*)((char*)seg + sizeof(macho_segment_command

)); + macho_section

* const sectionsEnd = §ionsStart[seg->nsects()]; + for(macho_section

* sect = sectionsStart; sect < sectionsEnd; ++sect) { + if ( sect->size() == 0 ) + continue; + unsigned sectionType = (sect->flags() & SECTION_TYPE); + const uint32_t indirectTableOffset = sect->reserved1(); + if ( sectionType == S_SYMBOL_STUBS ) { + const uint32_t stubSize = sect->reserved2(); + _stubCount = (uint32_t)(sect->size() / stubSize); + pint_t stubVMAddr = (pint_t)sect->addr(); + for (uint32_t j=0; j < _stubCount; ++j, stubVMAddr += stubSize) { + uint32_t symbolIndex = E::get32(indirectTable[indirectTableOffset + j]); + switch ( symbolIndex ) { + case INDIRECT_SYMBOL_ABS: + case INDIRECT_SYMBOL_LOCAL: + break; + default: + if ( symbolIndex >= _symTabCmd->nsyms() ) { + warning("symbol index out of range (%d of %d) for stub at addr 0x%0llX in %s", + symbolIndex, _symTabCmd->nsyms(), (uint64_t)stubVMAddr, _installName); + continue; + } + const macho_nlist

* sym = &symbolTable[symbolIndex]; + uint32_t stringOffset = sym->n_strx(); + if ( stringOffset > _symTabCmd->strsize() ) { + warning("symbol string offset out of range (%u of %u) for stub at addr 0x%0llX in %s", + stringOffset, sym->n_strx(), (uint64_t)stubVMAddr, _installName); + continue; + } + const char* symName = &symbolStrings[stringOffset]; + if ( neverStubEliminate.count(symName) ) { + //verboseLog("not bypassing stub to %s in %s because target is interposable\n", symName, _installName); + continue; + } + const uint8_t* stubInstrs = (uint8_t*)_cacheBuffer + sect->offset() + stubVMAddr - sect->addr(); + pint_t targetLPAddr = 0; + switch ( _mh->cputype() ) { + case CPU_TYPE_ARM64: + targetLPAddr = (pint_t)lazyPointerAddrFromArm64Stub(stubInstrs, stubVMAddr); + break; + case CPU_TYPE_ARM: + targetLPAddr = (pint_t)lazyPointerAddrFromArmStub(stubInstrs, (uint32_t)stubVMAddr); + break; + } + if ( targetLPAddr != 0 ) + _stubAddrToLPAddr[stubVMAddr] = targetLPAddr; + break; + } + } + } + else if ( sectionType == S_LAZY_SYMBOL_POINTERS ) { + pint_t lpVMAddr = (pint_t)sect->addr(); + pint_t* lpContent = (pint_t*)(((uint8_t*)_cacheBuffer) + sect->offset()); + uint32_t elementCount = (uint32_t)(sect->size() / sizeof(pint_t)); + uint64_t textSegStartAddr = _segCmds[0]->vmaddr(); + uint64_t textSegEndAddr = _segCmds[0]->vmaddr() + _segCmds[0]->vmsize(); + pint_t lpValue; + for (uint32_t j=0; j < elementCount; ++j) { + uint32_t symbolIndex = E::get32(indirectTable[indirectTableOffset + j]); + switch ( symbolIndex ) { + case INDIRECT_SYMBOL_ABS: + case INDIRECT_SYMBOL_LOCAL: + break; + default: + lpValue = (pint_t)P::getP(lpContent[j]); + if ( symbolIndex >= _symTabCmd->nsyms() ) { + warning("symbol index out of range (%d of %d) for lazy pointer at addr 0x%0llX in %s", + symbolIndex, _symTabCmd->nsyms(), (uint64_t)lpVMAddr, _installName); + continue; + } + const macho_nlist

* sym = &symbolTable[symbolIndex]; + uint32_t stringOffset = sym->n_strx(); + if ( stringOffset > _symTabCmd->strsize() ) { + warning("symbol string offset out of range (%u of %u) for lazy pointer at addr 0x%0llX in %s", + stringOffset, sym->n_strx(), (uint64_t)lpVMAddr, _installName); + continue; + } + const char* symName = &symbolStrings[stringOffset]; + if ( (lpValue > textSegStartAddr) && (lpValue< textSegEndAddr) ) { + //verboseLog("skipping lazy pointer at 0x%0lX to %s in %s because target is within dylib\n", lpVMAddr, symName, _installName); + } + else if ( (sizeof(pint_t) == 8) && ((lpValue % 4) != 0) ) { + warning("lazy pointer at 0x%0llX does not point to 4-byte aligned address(0x%0llX) in %s", + (uint64_t)lpVMAddr, (uint64_t)lpValue, _installName); + } + else { + _lpAddrToTargetAddr[lpVMAddr] = lpValue; + _targetAddrToName[lpValue] = symName; + } + break; + } + lpVMAddr += sizeof(pint_t); + } + } + } + } + cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); + } +} + + +template +void StubOptimizer

::forEachCallSiteToAStub(CallSiteHandler handler) +{ + const uint8_t* infoStart = &_linkeditBias[_splitSegInfoCmd->dataoff()]; + const uint8_t* infoEnd = &infoStart[_splitSegInfoCmd->datasize()]; + if ( *infoStart++ != DYLD_CACHE_ADJ_V2_FORMAT ) + terminate("malformed split seg info in %s", _installName); + + uint8_t* textSectionContent = (uint8_t*)_cacheBuffer + _textSegCacheOffset + _textSection->addr() -_textSegStartAddr; + + // Whole :== FromToSection+ + // FromToSection :== ToOffset+ + // ToOffset :== FromOffset+ + // FromOffset :== + const uint8_t* p = infoStart; + uint64_t sectionCount = read_uleb128(p, infoEnd); + for (uint64_t i=0; i < sectionCount; ++i) { + uint64_t fromSectionIndex = read_uleb128(p, infoEnd); + uint64_t toSectionIndex = read_uleb128(p, infoEnd); + uint64_t toOffsetCount = read_uleb128(p, infoEnd); + uint64_t toSectionOffset = 0; + for (uint64_t j=0; j < toOffsetCount; ++j) { + uint64_t toSectionDelta = read_uleb128(p, infoEnd); + uint64_t fromOffsetCount = read_uleb128(p, infoEnd); + toSectionOffset += toSectionDelta; + for (uint64_t k=0; k < fromOffsetCount; ++k) { + uint64_t kind = read_uleb128(p, infoEnd); + if ( kind > 12 ) + terminate("bad kind (%llu) value in %s", kind, _installName); + uint64_t fromSectDeltaCount = read_uleb128(p, infoEnd); + uint64_t fromSectionOffset = 0; + for (uint64_t l=0; l < fromSectDeltaCount; ++l) { + uint64_t delta = read_uleb128(p, infoEnd); + fromSectionOffset += delta; + if ( (fromSectionIndex == _textSectionIndex) && (toSectionIndex == _stubSectionIndex) ) { + uint32_t* instrPtr = (uint32_t*)(textSectionContent + fromSectionOffset); + uint64_t instrAddr = _textSection->addr() + fromSectionOffset; + uint64_t stubAddr = _stubSection->addr() + toSectionOffset; + uint32_t instruction = E::get32(*instrPtr); + _branchesCount++; + if ( handler(kind, instrAddr, stubAddr, instruction) ) { + _branchesModifiedCount++; + E::set32(*instrPtr, instruction); + } + } + } + } + } + } +} + + +/// Extract displacement from a thumb b/bl/blx instruction. +template +int32_t StubOptimizer

::getDisplacementFromThumbBranch(uint32_t instruction, uint32_t instrAddr) +{ + bool is_blx = ((instruction & 0xD000F800) == 0xC000F000); + uint32_t s = (instruction >> 10) & 0x1; + uint32_t j1 = (instruction >> 29) & 0x1; + uint32_t j2 = (instruction >> 27) & 0x1; + uint32_t imm10 = instruction & 0x3FF; + uint32_t imm11 = (instruction >> 16) & 0x7FF; + uint32_t i1 = (j1 == s); + uint32_t i2 = (j2 == s); + uint32_t dis = (s << 24) | (i1 << 23) | (i2 << 22) | (imm10 << 12) | (imm11 << 1); + int32_t sdis = dis; + int32_t result = s ? (sdis | 0xFE000000) : sdis; + if ( is_blx && (instrAddr & 0x2) ) { + // The thumb blx instruction always has low bit of imm11 as zero. The way + // a 2-byte aligned blx can branch to a 4-byte aligned ARM target is that + // the blx instruction always 4-byte aligns the pc before adding the + // displacement from the blx. We must emulate that when decoding this. + result -= 2; + } + return result; +} + +/// Update a thumb b/bl/blx instruction, switching bl <-> blx as needed. +template +uint32_t StubOptimizer

::setDisplacementInThumbBranch(uint32_t instruction, uint32_t instrAddr, + int32_t displacement, bool targetIsThumb) { + if ( (displacement > 16777214) || (displacement < (-16777216)) ) + terminate("thumb branch out of range at 0x%0X in %s", instrAddr, _installName); + bool is_bl = ((instruction & 0xD000F800) == 0xD000F000); + bool is_blx = ((instruction & 0xD000F800) == 0xC000F000); + bool is_b = ((instruction & 0xD000F800) == 0x9000F000); + uint32_t newInstruction = (instruction & 0xD000F800); + if (is_bl || is_blx) { + if (targetIsThumb) { + newInstruction = 0xD000F000; // Use bl + } + else { + newInstruction = 0xC000F000; // Use blx + // See note in getDisplacementFromThumbBranch() about blx. + if (instrAddr & 0x2) + displacement += 2; + } + } + else if (is_b) { + if ( !targetIsThumb ) + terminate("no pc-rel thumb branch instruction that switches to arm mode at 0x%0X in %s", instrAddr, _installName); + } + else { + terminate("not b/bl/blx at 0x%0X in %s", instrAddr, _installName); + } + uint32_t s = (uint32_t)(displacement >> 24) & 0x1; + uint32_t i1 = (uint32_t)(displacement >> 23) & 0x1; + uint32_t i2 = (uint32_t)(displacement >> 22) & 0x1; + uint32_t imm10 = (uint32_t)(displacement >> 12) & 0x3FF; + uint32_t imm11 = (uint32_t)(displacement >> 1) & 0x7FF; + uint32_t j1 = (i1 == s); + uint32_t j2 = (i2 == s); + uint32_t nextDisp = (j1 << 13) | (j2 << 11) | imm11; + uint32_t firstDisp = (s << 10) | imm10; + newInstruction |= (nextDisp << 16) | firstDisp; + return newInstruction; +} + + +template +void StubOptimizer

::optimizeArmCallSites() +{ + forEachCallSiteToAStub([&](uint8_t kind, uint64_t callSiteAddr, uint64_t stubAddr, uint32_t& instruction) -> bool { + if ( kind == DYLD_CACHE_ADJ_V2_THUMB_BR22 ) { + bool is_bl = ((instruction & 0xD000F800) == 0xD000F000); + bool is_blx = ((instruction & 0xD000F800) == 0xC000F000); + bool is_b = ((instruction & 0xD000F800) == 0x9000F000); + if ( !is_bl && !is_blx && !is_b ){ + warning("non-branch instruction at 0x%0llX in %s", callSiteAddr, _installName); + return false; + } + int32_t brDelta = getDisplacementFromThumbBranch(instruction, (uint32_t)callSiteAddr); + pint_t targetAddr = (pint_t)callSiteAddr + 4 + brDelta; + if ( targetAddr != stubAddr ) { + warning("stub target mismatch at callsite 0x%0llX in %s", callSiteAddr, _installName); + return false; + } + // ignore branch if not to a known stub + const auto& pos = _stubAddrToLPAddr.find(targetAddr); + if ( pos == _stubAddrToLPAddr.end() ) + return false; + // ignore branch if lazy pointer is not known (could be resolver based) + pint_t lpAddr = pos->second; + const auto& pos2 = _lpAddrToTargetAddr.find(lpAddr); + if ( pos2 == _lpAddrToTargetAddr.end() ) + return false; + uint64_t finalTargetAddr = pos2->second; + int64_t deltaToFinalTarget = finalTargetAddr - (callSiteAddr + 4); + // if final target within range, change to branch there directly + if ( (deltaToFinalTarget > -b16MegLimit) && (deltaToFinalTarget < b16MegLimit) ) { + bool targetIsThumb = finalTargetAddr & 1; + instruction = setDisplacementInThumbBranch(instruction, (uint32_t)callSiteAddr, (int32_t)deltaToFinalTarget, targetIsThumb); + _branchesDirectCount++; + return true; + } + } + else if ( kind == DYLD_CACHE_ADJ_V2_ARM_BR24 ) { + // too few of these to be worth trying to optimize + } + + return false; + }); +} + + +template +void StubOptimizer

::optimizeArmStubs() +{ + for (const auto& stubEntry : _stubAddrToLPAddr) { + pint_t stubVMAddr = stubEntry.first; + pint_t lpVMAddr = stubEntry.second; + const auto& pos = _lpAddrToTargetAddr.find(lpVMAddr); + if ( pos == _lpAddrToTargetAddr.end() ) + return; + pint_t targetVMAddr = pos->second; + + int32_t delta = (int32_t)(targetVMAddr - (stubVMAddr + 12)); + const uint32_t* stubInstructions = (uint32_t*)((uint8_t*)_cacheBuffer + _stubSection->offset() + stubVMAddr - _stubSection->addr()); + E::set32(*(uint32_t*)&stubInstructions[0], 0xe59fc000); // ldr ip, L0 + E::set32(*(uint32_t*)&stubInstructions[1], 0xe08ff00c); // add pc, pc, ip + E::set32(*(uint32_t*)&stubInstructions[2], delta); // L0: .long xxxx + E::set32(*(uint32_t*)&stubInstructions[3], 0xe7ffdefe); // trap + _stubOptimizedCount++; + } +} + + + + +template +void StubOptimizer

::optimizeArm64CallSites(std::vector*>& branchIslandPools) +{ + forEachCallSiteToAStub([&](uint8_t kind, uint64_t callSiteAddr, uint64_t stubAddr, uint32_t& instruction) -> bool { + if ( kind != DYLD_CACHE_ADJ_V2_ARM64_BR26 ) + return false; + // skip all but BL or B + if ( (instruction & 0x7C000000) != 0x14000000 ) + return false; + // compute target of branch instruction + int32_t brDelta = (instruction & 0x03FFFFFF) << 2; + if ( brDelta & 0x08000000 ) + brDelta |= 0xF0000000; + uint64_t targetAddr = callSiteAddr + (int64_t)brDelta; + if ( targetAddr != stubAddr ) { + warning("stub target mismatch"); + return false; + } + // ignore branch if not to a known stub + const auto& pos = _stubAddrToLPAddr.find((pint_t)targetAddr); + if ( pos == _stubAddrToLPAddr.end() ) + return false; + // ignore branch if lazy pointer is not known (could be resolver based) + uint64_t lpAddr = pos->second; + const auto& pos2 = _lpAddrToTargetAddr.find((pint_t)lpAddr); + if ( pos2 == _lpAddrToTargetAddr.end() ) + return false; + uint64_t finalTargetAddr = pos2->second; + int64_t deltaToFinalTarget = finalTargetAddr - callSiteAddr; + // if final target within range, change to branch there directly + if ( (deltaToFinalTarget > -b128MegLimit) && (deltaToFinalTarget < b128MegLimit) ) { + instruction= (instruction & 0xFC000000) | ((deltaToFinalTarget >> 2) & 0x03FFFFFF); + _branchesDirectCount++; + return true; + } + // find closest branch island pool between instruction and target and get island + const auto& pos3 = _targetAddrToName.find((pint_t)finalTargetAddr); + if ( pos3 == _targetAddrToName.end() ) + return false; + const char* targetName = pos3->second; + if ( finalTargetAddr > callSiteAddr ) { + // target is after branch so find first pool after branch + for ( BranchPoolDylib

* pool : branchIslandPools ) { + if ( (pool->addr() > callSiteAddr) && (pool->addr() < finalTargetAddr) ) { + uint64_t brIslandAddr = pool->getForwardBranch(finalTargetAddr, targetName, branchIslandPools); + if ( brIslandAddr == 0 ) { + // branch island pool full + warning("pool full. Can't optimizer branch to %s from 0x%llX in %s\n", targetName, callSiteAddr, _installName); + break; + } + int64_t deltaToTarget = brIslandAddr - callSiteAddr; + instruction = (instruction & 0xFC000000) | ((deltaToTarget >> 2) & 0x03FFFFFF); + _branchesIslandCount++; + return true; + } + } + } + else { + // target is before branch so find closest pool before branch + for (size_t j = branchIslandPools.size(); j > 0; --j) { + BranchPoolDylib

* pool = branchIslandPools[j-1]; + if ( (pool->addr() < callSiteAddr) && (pool->addr() > finalTargetAddr) ) { + uint64_t brIslandAddr = pool->getBackBranch(finalTargetAddr, targetName, branchIslandPools); + if ( brIslandAddr == 0 ) { + // branch island pool full + warning("pool full. Can't optimizer branch to %s from 0x%llX in %s\n", targetName, callSiteAddr, _installName); + break; + } + int64_t deltaToTarget = brIslandAddr - callSiteAddr; + instruction = (instruction & 0xFC000000) | ((deltaToTarget >> 2) & 0x03FFFFFF); + _branchesIslandCount++; + return true; + } + } + } + return false; + }); +} + + +template +void StubOptimizer

::optimizeCallSites(std::vector*>& branchIslandPools) +{ + if ( _textSection == NULL ) + return; + if ( _stubSection == NULL ) + return; + + + switch ( _mh->cputype() ) { + case CPU_TYPE_ARM64: + optimizeArm64CallSites(branchIslandPools); + if ( verbose ) { + verboseLog("%5u branches in __text, %5u changed to direct branches, %5u changed to use islands for %s", + _branchesCount, _branchesDirectCount, _branchesIslandCount, _installName); + } + break; + case CPU_TYPE_ARM: + optimizeArmCallSites(); + optimizeArmStubs(); + if ( verbose ) { + verboseLog("%3u of %3u stubs optimized. %5u branches in __text, %5u changed to direct branches for %s", + _stubOptimizedCount, _stubCount, _branchesCount, _branchesDirectCount, _installName); + } + break; + } +} + + +template +void SharedCache::bypassStubs(const std::vector& branchPoolStartAddrs) +{ + verboseLog("Stub elimination optimization:"); + + // construct a StubOptimizer for each image + std::vector*> optimizers; + forEachImage([&](const void* mh, const char*, time_t, ino_t, const std::vector&) { + optimizers.push_back(new StubOptimizer

(_buffer.get(), (macho_header

*)mh)); + }); + + // construct a BranchPoolDylib for each pool + std::vector*> pools; + + if ( _arch.arch == CPU_TYPE_ARM64 ) { + // Find hole at end of linkedit region for branch pool linkedits + uint64_t textRegionStartAddr = 0; + uint64_t linkEditRegionStartAddr = 0; + uint64_t linkEditRegionEndAddr = 0; + uint64_t linkEditRegionStartCacheOffset = 0; + forEachRegion([&] (void* content, uint64_t vmAddr, uint64_t size, uint32_t permissions) { + if ( permissions == (PROT_READ|PROT_EXEC) ) { + textRegionStartAddr = vmAddr; + } + else if ( permissions == PROT_READ ) { + linkEditRegionStartAddr = vmAddr; + linkEditRegionEndAddr = vmAddr + size; + linkEditRegionStartCacheOffset = (char*)content - (char*)_buffer.get(); + } + }); + uint64_t lastLinkEditRegionUsedOffset = 0; + forEachImage([&](const void* mh, const char*, time_t, ino_t, const std::vector& segs) { + for (MachOProxy::Segment seg : segs) { + if ( seg.name != "__LINKEDIT" ) + continue; + if ( seg.fileOffset >= lastLinkEditRegionUsedOffset ) + lastLinkEditRegionUsedOffset = seg.fileOffset + seg.size; + } + }); + uint64_t allPoolsLinkEditStartOffset = lastLinkEditRegionUsedOffset; + uint64_t allPoolsLinkEditStartAddr = linkEditRegionStartAddr + allPoolsLinkEditStartOffset - linkEditRegionStartCacheOffset; + uint64_t allPoolsLinkEditSize = linkEditRegionEndAddr - allPoolsLinkEditStartAddr; + if ( !branchPoolStartAddrs.empty() ) { + uint64_t poolLinkEditStartAddr = allPoolsLinkEditStartAddr; + uint64_t poolLinkEditStartOffset = allPoolsLinkEditStartOffset; + const uint64_t poolSize = (allPoolsLinkEditSize/branchPoolStartAddrs.size()) & (-4096); + for (uint64_t poolAddr : branchPoolStartAddrs) { + pools.push_back(new BranchPoolDylib

(_arch, _buffer.get(), _fileSize, poolAddr, + textRegionStartAddr, poolLinkEditStartAddr, poolLinkEditStartOffset)); + poolLinkEditStartAddr += poolSize; + poolLinkEditStartOffset += poolSize; + } + } + } + + // build set of functions to never stub-eliminate because tools may need to override them + std::unordered_set neverStubEliminate; + for (const char** p=sNeverStubEliminateSymbols; *p != nullptr; ++p) { + neverStubEliminate.insert(*p); + } + for (const char** d=sNeverStubEliminateDylibs; *d != nullptr; ++d) { + for (StubOptimizer

* op : optimizers) { + if ( strcmp(op->installName(), *d) == 0 ) { + // add all exports + const uint8_t* exportsStart = op->exportsTrie(); + const uint8_t* exportsEnd = exportsStart + op->exportsTrieSize(); + std::vector exports; + if ( !ExportInfoTrie::parseTrie(exportsStart, exportsEnd, exports) ) { + terminate("malformed exports trie in %s", *d); + } + for(const ExportInfoTrie::Entry& entry : exports) { + neverStubEliminate.insert(entry.name); + } + } + } + } + + // build maps of stubs-to-lp and lp-to-target + for (StubOptimizer

* op : optimizers) + op->buildStubMap(neverStubEliminate); + + // optimize call sites to by-pass stubs or jump through island + for (StubOptimizer

* op : optimizers) + op->optimizeCallSites(pools); + + // final fix ups in branch pools + for (BranchPoolDylib

* pool : pools) { + pool->finalizeLoadCommands(); + pool->printStats(); + } + + // write total optimization info + uint32_t callSiteCount = 0; + uint32_t callSiteDirectOptCount = 0; + uint32_t callSiteOneHopOptCount = 0; + for (StubOptimizer

* op : optimizers) { + callSiteCount += op->_branchesCount; + callSiteDirectOptCount += op->_branchesDirectCount; + callSiteOneHopOptCount += op->_branchesIslandCount; + } + verboseLog(" cache contains %u call sites of which %u were direct bound and %u were bound through islands", callSiteCount, callSiteDirectOptCount, callSiteOneHopOptCount); + + // clean up + for (StubOptimizer

* op : optimizers) + delete op; + for (BranchPoolDylib

* p : pools) + delete p; + +} + + +void SharedCache::bypassStubs(const std::vector& branchPoolStartAddrs) { + switch( _arch.arch ) { + case CPU_TYPE_ARM: + bypassStubs>(branchPoolStartAddrs); + break; + case CPU_TYPE_ARM64: + bypassStubs>(branchPoolStartAddrs); + break; + default: + // no stub optimization done for other arches + break; + } +} + + +/* +template +void StubOptimizer

::optimizeStubs(std::unordered_map>& targetToBranchIslands) +{ + for (const auto& stubEntry : _stubAddrToLPAddr) { + pint_t stubVMAddr = stubEntry.first; + pint_t lpVMAddr = stubEntry.second; + const auto& pos = _lpAddrToTargetAddr.find(lpVMAddr); + if ( pos == _lpAddrToTargetAddr.end() ) + continue; + pint_t targetVMAddr = pos->second; + int64_t delta = targetVMAddr - stubVMAddr; + if ( (delta > -b128MegLimit) && (delta < b128MegLimit) ) { + // target within reach, change stub to direct branch + uint32_t* stubInstructions = (uint32_t*)((uint8_t*)_cacheBuffer + _textSegCacheOffset + stubVMAddr -_textSegStartAddr); + uint32_t stubInstr1 = E::get32(stubInstructions[0]); + if ( (stubInstr1 & 0x9F00001F) != 0x90000010 ) { + warning("first instruction of stub (0x%08X) is no longer ADRP for stub at addr 0x%0X in %s\n", + stubInstr1, stubVMAddr, _installName); + continue; + } + uint32_t directBranchInstr = 0x14000000 + ((delta/4) & 0x03FFFFFF); + E::set32(stubInstructions[0], directBranchInstr); + uint32_t brkInstr = 0xD4200000; + E::set32(stubInstructions[1], brkInstr); + E::set32(stubInstructions[2], brkInstr); + _stubOptimizedCount++; + targetToBranchIslands[targetVMAddr].push_back(stubVMAddr); + } + } + verboseLog("%3u of %3u stubs optimized for %s\n", _stubOptimizedCount, _stubCount, _installName); +} + + +template +void StubOptimizer

::bypassStubs(std::unordered_map>& targetToBranchIslands) +{ + if ( _textSection == NULL ) + return; + + // scan __text section looking for B(L) instructions that branch to a stub + unsigned instructionCount = (unsigned)(_textSection->size() / 4); + uint32_t* instructions = (uint32_t*)((uint8_t*)_cacheBuffer + _textSegCacheOffset + _textSection->addr() -_textSegStartAddr); + for (unsigned i=0; i < instructionCount; ++i) { + uint32_t instr = E::get32(instructions[i]); + // skip all but BL or B + if ( (instr & 0x7C000000) != 0x14000000 ) + continue; + // compute target of branch instruction + int32_t brDelta = (instr & 0x03FFFFFF) << 2; + if ( brDelta & 0x08000000 ) + brDelta |= 0xF0000000; + uint64_t branchAddr = _textSection->addr() + i*4; + uint64_t targetAddr = branchAddr + (int64_t)brDelta; + // ignore branch if not to a known stub + const auto& pos = _stubAddrToLPAddr.find(targetAddr); + if ( pos == _stubAddrToLPAddr.end() ) + continue; + _branchesCount++; + // ignore branch if lazy pointer is not known (could be resolver based) + const auto& pos2 = _lpAddrToTargetAddr.find(pos->second); + if ( pos2 == _lpAddrToTargetAddr.end() ) + continue; + uint64_t finalTargetAddr = pos2->second; + int64_t deltaToFinalTarget = finalTargetAddr - branchAddr; + // if final target within range, change to branch there directly + if ( (deltaToFinalTarget > -b128MegLimit) && (deltaToFinalTarget < b128MegLimit) ) { + uint32_t newInstr = (instr & 0xFC000000) | ((deltaToFinalTarget >> 2) & 0x03FFFFFF); + E::set32(instructions[i], newInstr); + _branchesDirectCount++; + continue; + } + // see if there is an existing branch island in range that can be used + std::vector& existingBranchIslands = targetToBranchIslands[finalTargetAddr]; + for (uint64_t branchIslandAddr : existingBranchIslands) { + int64_t deltaToBranchIsland = branchIslandAddr - branchAddr; + // if final target within range, change to branch deltaToBranchIsland directly + if ( (deltaToBranchIsland > -b128MegLimit) && (deltaToFinalTarget < b128MegLimit) ) { + uint32_t newInstr = (instr & 0xFC000000) | ((deltaToBranchIsland >> 2) & 0x03FFFFFF); + E::set32(instructions[i], newInstr); + _branchesIslandCount++; + break; + } + } + } + if ( verbose ) { + verboseLog("%5u branches in __text, %5u changed to direct branches, %5u changed to indirect for %s\n", + _branchesCount, _branchesDirectCount, _branchesIslandCount, _installName); + } +} +*/ + diff --git a/src/dyld-apple/interlinked-dylibs/OptimizerBranches.h b/src/dyld-apple/interlinked-dylibs/OptimizerBranches.h new file mode 100644 index 000000000..ae50c5390 --- /dev/null +++ b/src/dyld-apple/interlinked-dylibs/OptimizerBranches.h @@ -0,0 +1,36 @@ +/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- + * + * Copyright (c) 2014 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@ + */ + +#ifndef __OPTIMIZER_BRANCHES_H__ +#define __OPTIMIZER_BRANCHES_H__ + +#include "mega-dylib-utils.h" + +//FIXME: merge these into the SharedCache object and delete this header +uint64_t branchPoolTextSize(ArchPair arch); +uint64_t branchPoolLinkEditSize(ArchPair arch); +uint64_t branchReach(ArchPair arch); + +#endif // __OPTIMIZER_BRANCHES_H__ + diff --git a/src/dyld-apple/interlinked-dylibs/OptimizerLinkedit.cpp b/src/dyld-apple/interlinked-dylibs/OptimizerLinkedit.cpp new file mode 100644 index 000000000..e446c438f --- /dev/null +++ b/src/dyld-apple/interlinked-dylibs/OptimizerLinkedit.cpp @@ -0,0 +1,1185 @@ +/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- + * + * Copyright (c) 2014 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@ + */ + +#include "mega-dylib-utils.h" +#include "MachOFileAbstraction.hpp" +#include "Logging.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "dyld_cache_config.h" +#include "Trie.hpp" + +#if !NEW_CACHE_FILE_FORMAT + #include "CacheFileAbstraction.hpp" +#endif + +#define ALIGN_AS_TYPE(value, type) \ + ((value + alignof(type) - 1) & (-alignof(type))) + +namespace { + +template +class SortedStringPool +{ +public: + // add a string and symbol table entry index to be updated later + void add(uint32_t symbolIndex, const char* symbolName) { + _map[symbolName].push_back(symbolIndex); + } + + // copy sorted strings to buffer and update all symbol's string offsets + uint32_t copyPoolAndUpdateOffsets(char* dstStringPool, macho_nlist

* symbolTable) { + // make sorted list of strings + std::vector allStrings; + allStrings.reserve(_map.size()); + for (auto& entry : _map) { + allStrings.push_back(entry.first); + } + std::sort(allStrings.begin(), allStrings.end()); + // walk sorted list of strings + dstStringPool[0] = '\0'; // tradition for start of pool to be empty string + uint32_t poolOffset = 1; + for (const std::string& symName : allStrings) { + // append string to pool + strcpy(&dstStringPool[poolOffset], symName.c_str()); + // set each string offset of each symbol using it + for (uint32_t symbolIndex : _map[symName]) { + symbolTable[symbolIndex].set_n_strx(poolOffset); + } + poolOffset += symName.size() + 1; + } + // return size of pool + return poolOffset; + } + +private: + std::unordered_map> _map; +}; + + + + +struct LocalSymbolInfo +{ + uint32_t dylibOffset; + uint32_t nlistStartIndex; + uint32_t nlistCount; +}; + + +template +class LinkeditOptimizer { +public: + LinkeditOptimizer(void* cacheBuffer, macho_header

* mh); + + uint32_t linkeditSize() { return _linkeditSize; } + uint32_t linkeditOffset() { return _linkeditCacheOffset; } + uint64_t linkeditAddr() { return _linkeditAddr; } + const char* installName() { return _installName; } + void copyWeakBindingInfo(uint8_t* newLinkEditContent, uint32_t& offset); + void copyLazyBindingInfo(uint8_t* newLinkEditContent, uint32_t& offset); + void copyBindingInfo(uint8_t* newLinkEditContent, uint32_t& offset); + void copyExportInfo(uint8_t* newLinkEditContent, uint32_t& offset); + void copyExportedSymbols(uint8_t* newLinkEditContent, SortedStringPool

& stringPool, uint32_t& offset, uint32_t& symbolIndex); + void copyImportedSymbols(uint8_t* newLinkEditContent, SortedStringPool

& stringPool, uint32_t& offset, uint32_t& symbolIndex); + void copyLocalSymbols(uint8_t* newLinkEditContent, SortedStringPool

& stringPool, uint32_t& offset, uint32_t& symbolIndex, + bool redact, std::vector& localSymbolInfos, + std::vector>& unmappedLocalSymbols, SortedStringPool

& localSymbolsStringPool); + void copyFunctionStarts(uint8_t* newLinkEditContent, uint32_t& offset); + void copyDataInCode(uint8_t* newLinkEditContent, uint32_t& offset); + void copyIndirectSymbolTable(uint8_t* newLinkEditContent, uint32_t& offset); + void updateLoadCommands(uint32_t linkeditStartOffset, uint64_t mergedLinkeditAddr, uint64_t newLinkeditSize, + uint32_t sharedSymbolTableStartOffset, uint32_t sharedSymbolTableCount, + uint32_t sharedSymbolStringsOffset, uint32_t sharedSymbolStringsSize); + + macho_header

* machHeader() { return _mh; } + const std::vector getDownwardDependents() { return _downDependentPaths; } + const std::vector getAllDependents() { return _allDependentPaths; } + const std::vector getReExportPaths() { return _reExportPaths; } + const std::vector initializerAddresses() { return _initializerAddresses; } + const std::vector*> dofSections() { return _dofSections; } + uint32_t exportsTrieLinkEditOffset() { return _newExportInfoOffset; } + uint32_t exportsTrieLinkEditSize() { return _exportInfoSize; } + uint32_t weakBindingLinkEditOffset() { return _newWeakBindingInfoOffset; } + uint32_t weakBindingLinkEditSize() { return _newWeakBindingSize; } + uint64_t dyldSectionAddress() { return _dyldSectionAddr; } + const std::vector*>& segCmds() { return _segCmds; } + + +private: + + typedef typename P::uint_t pint_t; + typedef typename P::E E; + + macho_header

* _mh; + void* _cacheBuffer; + uint32_t _linkeditSize = 0; + uint32_t _linkeditCacheOffset = 0; + uint64_t _linkeditAddr = 0; + const uint8_t* _linkeditBias = nullptr; + const char* _installName = nullptr; + macho_symtab_command

* _symTabCmd = nullptr; + macho_dysymtab_command

* _dynSymTabCmd = nullptr; + macho_dyld_info_command

* _dyldInfo = nullptr; + macho_linkedit_data_command

* _functionStartsCmd = nullptr; + macho_linkedit_data_command

* _dataInCodeCmd = nullptr; + std::vector*> _segCmds; + std::unordered_map _oldToNewSymbolIndexes; + std::vector _reExportPaths; + std::vector _downDependentPaths; + std::vector _allDependentPaths; + std::vector _initializerAddresses; + std::vector*> _dofSections; + uint32_t _newWeakBindingInfoOffset = 0; + uint32_t _newLazyBindingInfoOffset = 0; + uint32_t _newBindingInfoOffset = 0; + uint32_t _newExportInfoOffset = 0; + uint32_t _exportInfoSize = 0; + uint32_t _newWeakBindingSize = 0; + uint32_t _newExportedSymbolsStartIndex = 0; + uint32_t _newExportedSymbolCount = 0; + uint32_t _newImportedSymbolsStartIndex = 0; + uint32_t _newImportedSymbolCount = 0; + uint32_t _newLocalSymbolsStartIndex = 0; + uint32_t _newLocalSymbolCount = 0; + uint32_t _newFunctionStartsOffset = 0; + uint32_t _newDataInCodeOffset = 0; + uint32_t _newIndirectSymbolTableOffset = 0; + uint64_t _dyldSectionAddr = 0; +}; + + + +template +class AcceleratorTables { +public: + AcceleratorTables(void* cacheBuffer, uint64_t linkeditStartAddr, const std::vector*>& optimizers); + + uint32_t totalSize() const; + void copyTo(uint8_t* buffer); + +private: + typedef typename P::E E; + + struct NodeChain; + + struct DepNode { + std::vector _dependents; + unsigned _depth; + const char* _installName; + + DepNode() : _depth(0), _installName(nullptr) { } + void computeDepth(); + static void verifyUnreachable(DepNode* target, NodeChain& chain, std::unordered_set& visitedNodes, const std::vector& from); + }; + + struct NodeChain { + NodeChain* prev; + DepNode* node; + }; + + std::unordered_map*, DepNode> _depDAG; + std::vector> _extraInfo; + std::vector _trieBytes; + std::vector _reExportArray; + std::vector _dependencyArray; + std::vector _bottomUpArray; + std::vector> _initializers; + std::vector> _dofSections; + std::vector> _rangeTable; + std::unordered_map*, uint32_t> _machHeaderToImageIndex; + std::unordered_map*> _dylibPathToMachHeader; + std::unordered_map*, LinkeditOptimizer

*> _machHeaderToOptimizer; + dyldCacheAcceleratorInfo _acceleratorInfoHeader; +}; + + +template +void AcceleratorTables

::AcceleratorTables::DepNode::verifyUnreachable(AcceleratorTables

::DepNode* target, struct AcceleratorTables

::NodeChain& chain, + std::unordered_set& visitedNodes, const std::vector::DepNode*>& from) { + for (DepNode* node : from) { + bool foundCycle = (node == target); + for (NodeChain* c = &chain; c->prev != nullptr; c = c->prev) { + if ( c->node == node ) + foundCycle = true; + } + if ( foundCycle ) { + NodeChain* chp = &chain; + std::string msg = std::string("found cycle for ") + target->_installName; + while (chp != nullptr) { + msg = msg + "\n " + chp->node->_installName; + chp = chp->prev; + } + warning("%s", msg.c_str()); + return; + } + + if ( visitedNodes.count(node) ) + continue; + NodeChain nextChain; + nextChain.prev = &chain; + nextChain.node = node; + verifyUnreachable(target, nextChain, visitedNodes, node->_dependents); + visitedNodes.insert(node); + } +} + +const uint16_t kBranchIslandDylibIndex = 0x7FFF; + +template +AcceleratorTables

::AcceleratorTables(void* cacheBuffer, uint64_t linkeditStartAddr, const std::vector*>& optimizers) +{ + // build table mapping tables to map between mach_header, index, and optimizer + for ( LinkeditOptimizer

* op : optimizers ) { + _machHeaderToOptimizer[op->machHeader()] = op; + } + uint64_t cacheStartAddress; +#if NEW_CACHE_FILE_FORMAT + #error new format support not implemented +#else + typedef typename P::E E; + const dyldCacheHeader* header = (dyldCacheHeader*)cacheBuffer; + const dyldCacheFileMapping* mappings = (dyldCacheFileMapping*)((uint8_t*)cacheBuffer + header->mappingOffset()); + cacheStartAddress = mappings[0].address(); + const dyldCacheImageInfo* images = (dyldCacheImageInfo*)((uint8_t*)cacheBuffer + header->imagesOffset()); + const unsigned imageCount = header->imagesCount(); + for (unsigned i=0; i < imageCount; ++i) { + uint64_t segCacheFileOffset = images[i].address() - cacheStartAddress; + macho_header

* mhMapped = (macho_header

*)((uint8_t*)cacheBuffer+segCacheFileOffset); + const char* path = (char*)cacheBuffer + images[i].pathFileOffset(); + _dylibPathToMachHeader[path] = mhMapped; + // don't add alias entries (path offset in pool near start of cache) to header->index map + if ( images[i].pathFileOffset() > segCacheFileOffset ) + _machHeaderToImageIndex[mhMapped] = i; + } +#endif + + // build DAG of image dependencies + for (LinkeditOptimizer

* op : optimizers) { + _depDAG[op->machHeader()]._installName = op->installName(); + } + for (LinkeditOptimizer

* op : optimizers) { + DepNode& node = _depDAG[op->machHeader()]; + for (const char* depPath : op->getDownwardDependents()) { + macho_header

* depMH = _dylibPathToMachHeader[depPath]; + assert(depMH != NULL); + DepNode* depNode = &_depDAG[depMH]; + node._dependents.push_back(depNode); + } + } + + // check for cycles in DAG + for (auto& entry : _depDAG) { + DepNode* node = &entry.second; + NodeChain chain; + chain.prev = nullptr; + chain.node = node; + std::unordered_set visitedNodes; + DepNode::verifyUnreachable(node, chain, visitedNodes, node->_dependents); + } + + // compute depth for each DAG node + for (auto& entry : _depDAG) { + entry.second.computeDepth(); + } + + // build sorted (bottom up) list of images + std::vector*> sortedMachHeaders; + sortedMachHeaders.reserve(optimizers.size()); + for (LinkeditOptimizer

* op : optimizers) { + if ( strcmp(op->installName(), "dyld_shared_cache_branch_islands") != 0 ) + sortedMachHeaders.push_back(op->machHeader()); + else + _machHeaderToImageIndex[op->machHeader()] = kBranchIslandDylibIndex; + } + std::sort(sortedMachHeaders.begin(), sortedMachHeaders.end(), + [&](macho_header

* lmh, macho_header

* rmh) -> bool { + if ( _depDAG[lmh]._depth != _depDAG[rmh]._depth ) + return (_depDAG[lmh]._depth < _depDAG[rmh]._depth); + else + return (lmh < rmh); + }); + + // build zeroed array of extra infos + dyldCacheImageInfoExtra emptyExtra; + emptyExtra.set_exportsTrieAddr(0); + emptyExtra.set_weakBindingsAddr(0); + emptyExtra.set_exportsTrieSize(0); + emptyExtra.set_weakBindingsSize(0); + emptyExtra.set_dependentsStartArrayIndex(0); + emptyExtra.set_reExportsStartArrayIndex(0); + _extraInfo.insert(_extraInfo.begin(), sortedMachHeaders.size(), emptyExtra); + + //for ( macho_header

* mh : sortedMachHeaders ) { + // fprintf(stderr, "depth: %3d mh: %p path: %s\n", _depDAG[mh]._depth, mh, _machHeaderToOptimizer[mh]->installName()); + //} + + // build dependency table + _dependencyArray.push_back(0xFFFF); // reserve 0 slot to be "no-dependencies" + for (macho_header

* mh : sortedMachHeaders) { + LinkeditOptimizer

* op = _machHeaderToOptimizer[mh]; + unsigned index = _machHeaderToImageIndex[mh]; + auto depPaths = op->getAllDependents(); + if ( depPaths.empty() ) { + _extraInfo[index].set_dependentsStartArrayIndex(0); + } + else { + _extraInfo[index].set_dependentsStartArrayIndex((uint32_t)_dependencyArray.size()); + auto downPaths = op->getDownwardDependents(); + for (const char* depPath : depPaths) { + macho_header

* depMH = _dylibPathToMachHeader[depPath]; + uint16_t depIndex = _machHeaderToImageIndex[depMH]; + if ( std::find(downPaths.begin(), downPaths.end(), depPath) == downPaths.end()) + depIndex |= 0x8000; + _dependencyArray.push_back(depIndex); + } + _dependencyArray.push_back(0xFFFF); // mark end of list + } + } + + // build re-exports table + _reExportArray.push_back(0xFFFF); // reserve 0 slot to be "no-re-exports" + for (macho_header

* mh : sortedMachHeaders) { + LinkeditOptimizer

* op = _machHeaderToOptimizer[mh]; + unsigned index = _machHeaderToImageIndex[mh]; + auto reExPaths = op->getReExportPaths(); + if ( reExPaths.empty() ) { + _extraInfo[index].set_reExportsStartArrayIndex(0); + } + else { + _extraInfo[index].set_reExportsStartArrayIndex((uint32_t)_reExportArray.size()); + for (const char* reExPath : reExPaths) { + macho_header

* reExMH = _dylibPathToMachHeader[reExPath]; + uint32_t reExIndex = _machHeaderToImageIndex[reExMH]; + _reExportArray.push_back(reExIndex); + } + _reExportArray.push_back(0xFFFF); // mark end of list + } + } + + // build ordered list of initializers + for (macho_header

* mh : sortedMachHeaders) { + LinkeditOptimizer

* op = _machHeaderToOptimizer[mh]; + unsigned index = _machHeaderToImageIndex[mh]; + _bottomUpArray.push_back(index); + for (uint64_t initializer : op->initializerAddresses()) { + //fprintf(stderr, "0x%08llX %s\n", initializer, op->installName()); + dyldCacheAcceleratorInitializer entry; + entry.set_functionOffset((uint32_t)(initializer-cacheStartAddress)); + entry.set_imageIndex(_machHeaderToImageIndex[mh]); + _initializers.push_back(entry); + } + } + + // build ordered list of DOF sections + for (macho_header

* mh : sortedMachHeaders) { + LinkeditOptimizer

* op = _machHeaderToOptimizer[mh]; + assert(op != NULL); + unsigned imageIndex = _machHeaderToImageIndex[mh]; + for (auto& sect : op->dofSections()) { + //fprintf(stderr, "0x%08llX %s\n", initializer, op->installName()); + dyldCacheAcceleratorDOFEntry entry; + entry.set_sectionAddress(sect->addr()); + entry.set_sectionSize((uint32_t)sect->size()); + entry.set_imageIndex(imageIndex); + _dofSections.push_back(entry); + } + } + + // register exports trie and weak binding info in each dylib with image extra info + for (macho_header

* mh : sortedMachHeaders) { + LinkeditOptimizer

* op = _machHeaderToOptimizer[mh]; + unsigned index = _machHeaderToImageIndex[mh]; + _extraInfo[index].set_exportsTrieAddr(op->exportsTrieLinkEditOffset() + linkeditStartAddr); + _extraInfo[index].set_exportsTrieSize(op->exportsTrieLinkEditSize()); + _extraInfo[index].set_weakBindingsAddr(op->weakBindingLinkEditOffset() + linkeditStartAddr); + _extraInfo[index].set_weakBindingsSize(op->weakBindingLinkEditSize()); + } + + // record location of __DATA/__dyld section in libdyld.dylib + macho_header

* libdyldMH = _dylibPathToMachHeader["/usr/lib/system/libdyld.dylib"]; + LinkeditOptimizer

* libdyldOp = _machHeaderToOptimizer[libdyldMH]; + uint64_t dyldSectionAddr = libdyldOp->dyldSectionAddress(); + + // build range table for fast address->image lookups + for (macho_header

* mh : sortedMachHeaders) { + LinkeditOptimizer

* op = _machHeaderToOptimizer[mh]; + unsigned imageIndex = _machHeaderToImageIndex[mh]; + for ( const macho_segment_command

* segCmd : op->segCmds() ) { + if ( strcmp(segCmd->segname(), "__LINKEDIT") == 0 ) + continue; + dyldCacheAcceleratorRangeEntry entry; + entry.set_startAddress(segCmd->vmaddr()); + entry.set_size((uint32_t)segCmd->vmsize()); + entry.set_imageIndex(imageIndex); + _rangeTable.push_back(entry); + } + } + std::sort(_rangeTable.begin(), _rangeTable.end(), + [&](const dyldCacheAcceleratorRangeEntry& lRange, const dyldCacheAcceleratorRangeEntry& rRange) -> bool { + return (lRange.startAddress() < rRange.startAddress()); + }); + + // build trie that maps install names to image index + std::vector dylibEntrys; + for (auto &x : _dylibPathToMachHeader) { + const std::string& path = x.first; + unsigned index = _machHeaderToImageIndex[x.second]; + dylibEntrys.push_back(DylibIndexTrie::Entry(path, DylibIndex(index))); + } + DylibIndexTrie dylibsTrie(dylibEntrys); + dylibsTrie.emit(_trieBytes); + while ( (_trieBytes.size() % 4) != 0 ) + _trieBytes.push_back(0); + + // fill out header + dyldCacheAcceleratorInfo& h = _acceleratorInfoHeader; + h.set_version(1); + h.set_imageExtrasCount((uint32_t)_extraInfo.size()); + h.set_imagesExtrasOffset(ALIGN_AS_TYPE(sizeof(dyld_cache_accelerator_info), dyld_cache_image_info_extra)); + h.set_bottomUpListOffset(h.imagesExtrasOffset() + h.imageExtrasCount()*sizeof(dyld_cache_image_info_extra)); + h.set_dylibTrieOffset(h.bottomUpListOffset() + h.imageExtrasCount()*sizeof(uint16_t)); + h.set_dylibTrieSize((uint32_t)_trieBytes.size()); + h.set_initializersOffset(ALIGN_AS_TYPE(h.dylibTrieOffset() + h.dylibTrieSize(), dyld_cache_accelerator_initializer)); + h.set_initializersCount((uint32_t)_initializers.size()); + h.set_dofSectionsOffset(ALIGN_AS_TYPE(h.initializersOffset() + h.initializersCount()*sizeof(dyld_cache_accelerator_initializer), dyld_cache_accelerator_initializer)); + h.set_dofSectionsCount((uint32_t)_dofSections.size()); + h.set_reExportListOffset(ALIGN_AS_TYPE(h.dofSectionsOffset() + h.dofSectionsCount()*sizeof(dyld_cache_accelerator_dof), dyld_cache_accelerator_dof)); + h.set_reExportCount((uint32_t)_reExportArray.size()); + h.set_depListOffset(ALIGN_AS_TYPE(h.reExportListOffset() + h.reExportCount()*sizeof(uint16_t), uint16_t)); + h.set_depListCount((uint32_t)_dependencyArray.size()); + h.set_rangeTableOffset(ALIGN_AS_TYPE(h.depListOffset() + h.depListCount()*sizeof(uint16_t), dyld_cache_range_entry)); + h.set_rangeTableCount((uint32_t)_rangeTable.size()); + h.set_dyldSectionAddr(dyldSectionAddr); +} + + +template +void AcceleratorTables

::DepNode::computeDepth() +{ + if ( _depth != 0 ) + return; + _depth = 1; + for (DepNode* node : _dependents) { + node->computeDepth(); + if ( node->_depth >= _depth ) + _depth = node->_depth + 1; + } +} + +template +uint32_t AcceleratorTables

::totalSize() const +{ + return (uint32_t)align(_acceleratorInfoHeader.rangeTableOffset() + _acceleratorInfoHeader.rangeTableCount()*sizeof(dyld_cache_range_entry), 14); +} + +template +void AcceleratorTables

::copyTo(uint8_t* buffer) +{ + memcpy(buffer, &_acceleratorInfoHeader, sizeof(dyld_cache_accelerator_info)); + memcpy(&buffer[_acceleratorInfoHeader.imagesExtrasOffset()], &_extraInfo[0], _extraInfo.size()*sizeof(dyld_cache_image_info_extra)); + memcpy(&buffer[_acceleratorInfoHeader.bottomUpListOffset()], &_bottomUpArray[0], _bottomUpArray.size()*sizeof(uint16_t)); + memcpy(&buffer[_acceleratorInfoHeader.initializersOffset()], &_initializers[0], _initializers.size()*sizeof(dyld_cache_accelerator_initializer)); + memcpy(&buffer[_acceleratorInfoHeader.reExportListOffset()], &_reExportArray[0], _reExportArray.size()*sizeof(uint16_t)); + memcpy(&buffer[_acceleratorInfoHeader.dofSectionsOffset()], &_dofSections[0], _dofSections.size()*sizeof(dyld_cache_accelerator_dof)); + memcpy(&buffer[_acceleratorInfoHeader.depListOffset()], &_dependencyArray[0], _dependencyArray.size()*sizeof(uint16_t)); + memcpy(&buffer[_acceleratorInfoHeader.rangeTableOffset()], &_rangeTable[0], _rangeTable.size()*sizeof(dyld_cache_range_entry)); + memcpy(&buffer[_acceleratorInfoHeader.dylibTrieOffset()], &_trieBytes[0], _trieBytes.size()); +} + + + +template +LinkeditOptimizer

::LinkeditOptimizer(void* cacheBuffer, macho_header

* mh) +: _mh(mh), _cacheBuffer(cacheBuffer) +{ + _linkeditBias = (uint8_t*)cacheBuffer; + const unsigned origLoadCommandsSize = mh->sizeofcmds(); + unsigned bytesRemaining = origLoadCommandsSize; + unsigned removedCount = 0; + const macho_load_command

* const cmds = (macho_load_command

*)((uint8_t*)mh + sizeof(macho_header

)); + const uint32_t cmdCount = mh->ncmds(); + const macho_load_command

* cmd = cmds; + const macho_dylib_command

* dylibCmd; + const macho_routines_command

* routinesCmd; + macho_segment_command

* segCmd; + for (uint32_t i = 0; i < cmdCount; ++i) { + bool remove = false; + switch (cmd->cmd()) { + case LC_ID_DYLIB: + _installName = ((macho_dylib_command

*)cmd)->name(); + break; + case LC_SYMTAB: + _symTabCmd = (macho_symtab_command

*)cmd; + break; + case LC_DYSYMTAB: + _dynSymTabCmd = (macho_dysymtab_command

*)cmd; + break; + case LC_DYLD_INFO: + case LC_DYLD_INFO_ONLY: + _dyldInfo = (macho_dyld_info_command

*)cmd; + _exportInfoSize = _dyldInfo->export_size(); + break; + case LC_FUNCTION_STARTS: + _functionStartsCmd = (macho_linkedit_data_command

*)cmd; + break; + case LC_DATA_IN_CODE: + _dataInCodeCmd = (macho_linkedit_data_command

*)cmd; + break; + case LC_ROUTINES: + case LC_ROUTINES_64: + routinesCmd = (macho_routines_command

*)cmd; + _initializerAddresses.push_back(routinesCmd->init_address()); + break; + case LC_REEXPORT_DYLIB: + case LC_LOAD_DYLIB: + case LC_LOAD_WEAK_DYLIB: + case LC_LOAD_UPWARD_DYLIB: + dylibCmd = (macho_dylib_command

*)cmd; + _allDependentPaths.push_back(dylibCmd->name()); + if ( cmd->cmd() != LC_LOAD_UPWARD_DYLIB ) + _downDependentPaths.push_back(dylibCmd->name()); + if ( cmd->cmd() == LC_REEXPORT_DYLIB ) + _reExportPaths.push_back(dylibCmd->name()); + break; + case macho_segment_command

::CMD: + segCmd = (macho_segment_command

*)cmd; + _segCmds.push_back(segCmd); + if ( strcmp(segCmd->segname(), "__LINKEDIT") == 0 ) { + _linkeditSize = (uint32_t)segCmd->vmsize(); + _linkeditCacheOffset = (uint32_t)segCmd->fileoff(); + _linkeditAddr = segCmd->vmaddr(); + } + else if ( segCmd->nsects() > 0 ) { + macho_section

* const sectionsStart = (macho_section

*)((uint8_t*)segCmd + sizeof(macho_segment_command

)); + macho_section

* const sectionsEnd = §ionsStart[segCmd->nsects()]; + for (macho_section

* sect=sectionsStart; sect < sectionsEnd; ++sect) { + const uint8_t type = sect->flags() & SECTION_TYPE; + if ( type == S_MOD_INIT_FUNC_POINTERS ) { + const pint_t* inits = (pint_t*)((char*)cacheBuffer + sect->offset()); + const size_t count = sect->size() / sizeof(pint_t); + for (size_t j=0; j < count; ++j) { + uint64_t func = P::getP(inits[j]); + _initializerAddresses.push_back(func); + } + } + else if ( type == S_DTRACE_DOF ) { + _dofSections.push_back(sect); + } + else if ( (strcmp(sect->sectname(), "__dyld") == 0) && (strncmp(sect->segname(), "__DATA", 6) == 0) ) { + _dyldSectionAddr = sect->addr(); + } + } + } + break; + case LC_SEGMENT_SPLIT_INFO: + remove = true; + break; + } + uint32_t cmdSize = cmd->cmdsize(); + macho_load_command

* nextCmd = (macho_load_command

*)(((uint8_t*)cmd)+cmdSize); + if ( remove ) { + ::memmove((void*)cmd, (void*)nextCmd, bytesRemaining); + ++removedCount; + } + else { + bytesRemaining -= cmdSize; + cmd = nextCmd; + } + } + // zero out stuff removed + ::bzero((void*)cmd, bytesRemaining); + // update header + mh->set_ncmds(cmdCount - removedCount); + mh->set_sizeofcmds(origLoadCommandsSize - bytesRemaining); +} + +/* +static void dumpLoadCommands(const uint8_t* mheader) +{ + const mach_header* const mh = (mach_header*)mheader; + const uint32_t cmd_count = mh->ncmds; + bool is64 = (mh->magic == MH_MAGIC_64); + const load_command* cmds = (load_command*)(mheader + (is64 ? sizeof(mach_header_64) : sizeof(mach_header))); + const load_command* cmd = cmds; + const segment_command* segCmd; + const segment_command_64* seg64Cmd; + const symtab_command* symTab; + const linkedit_data_command* leData; + const uint8_t* linkEditBias = NULL; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd) { + case LC_SEGMENT: + segCmd = (const segment_command*)cmd; + printf("LC_SEGMENT\n"); + printf(" segname = %s\n", segCmd->segname); + printf(" vmaddr = 0x%08X\n", segCmd->vmaddr); + printf(" vmsize = 0x%08X\n", segCmd->vmsize); + printf(" fileoff = 0x%08X\n", segCmd->fileoff); + printf(" filesize = 0x%08X\n", segCmd->filesize); + if ( strcmp(segCmd->segname, "__TEXT") == 0 ) { + linkEditBias = mheader - segCmd->fileoff; + } + break; + case LC_SEGMENT_64: + seg64Cmd = (const segment_command_64*)cmd; + printf("LC_SEGMENT_64\n"); + printf(" segname = %s\n", seg64Cmd->segname); + printf(" vmaddr = 0x%09llX\n", seg64Cmd->vmaddr); + printf(" vmsize = 0x%09llX\n", seg64Cmd->vmsize); + printf(" fileoff = 0x%09llX\n", seg64Cmd->fileoff); + printf(" filesize = 0x%09llX\n", seg64Cmd->filesize); + if ( strcmp(seg64Cmd->segname, "__TEXT") == 0 ) { + linkEditBias = mheader - seg64Cmd->fileoff; + } + break; + case LC_SYMTAB: + symTab = (const symtab_command*)cmd; + printf("LC_SYMTAB\n"); + printf(" symoff = 0x%08X\n", symTab->symoff); + printf(" nsyms = 0x%08X\n", symTab->nsyms); + printf(" stroff = 0x%08X\n", symTab->stroff); + printf(" strsize = 0x%08X\n", symTab->strsize); + { + const char* strPool = (char*)&linkEditBias[symTab->stroff]; + const nlist_64* sym0 = (nlist_64*)(&linkEditBias[symTab->symoff]); + printf(" sym[0].n_strx = 0x%08X (%s)\n", sym0->n_un.n_strx, &strPool[sym0->n_un.n_strx]); + printf(" sym[0].n_type = 0x%02X\n", sym0->n_type); + printf(" sym[0].n_sect = 0x%02X\n", sym0->n_sect); + printf(" sym[0].n_desc = 0x%04X\n", sym0->n_desc); + printf(" sym[0].n_value = 0x%llX\n", sym0->n_value); + const nlist_64* sym1 = (nlist_64*)(&linkEditBias[symTab->symoff+16]); + printf(" sym[1].n_strx = 0x%08X (%s)\n", sym1->n_un.n_strx, &strPool[sym1->n_un.n_strx]); + printf(" sym[1].n_type = 0x%02X\n", sym1->n_type); + printf(" sym[1].n_sect = 0x%02X\n", sym1->n_sect); + printf(" sym[1].n_desc = 0x%04X\n", sym1->n_desc); + printf(" sym[1].n_value = 0x%llX\n", sym1->n_value); + } + break; + case LC_FUNCTION_STARTS: + leData = (const linkedit_data_command*)cmd; + printf("LC_FUNCTION_STARTS\n"); + printf(" dataoff = 0x%08X\n", leData->dataoff); + printf(" datasize = 0x%08X\n", leData->datasize); + default: + //printf("0x%08X\n", cmd->cmd); + break; + } + cmd = (const load_command*)(((uint8_t*)cmd)+cmd->cmdsize); + } +} +*/ + +template +void LinkeditOptimizer

::updateLoadCommands(uint32_t mergedLinkeditStartOffset, uint64_t mergedLinkeditAddr, uint64_t newLinkeditSize, + uint32_t sharedSymbolTableStartOffset, uint32_t sharedSymbolTableCount, + uint32_t sharedSymbolStringsOffset, uint32_t sharedSymbolStringsSize) +{ + // update __LINKEDIT segment in all dylibs to overlap the same shared region + for (macho_segment_command

* segCmd : _segCmds) { + if ( strcmp(segCmd->segname(), "__LINKEDIT") == 0 ) { + segCmd->set_vmaddr(mergedLinkeditAddr); + segCmd->set_vmsize(newLinkeditSize); + segCmd->set_fileoff(mergedLinkeditStartOffset); + segCmd->set_filesize(newLinkeditSize); + } + else if ( strcmp(segCmd->segname(), "__TEXT") == 0 ) { + // HACK until lldb fixed in: DynamicLoaderMacOSXDYLD fixes for Monarch dyld shared cache + segCmd->set_fileoff(0); + + } + } + + // update symbol table to point to shared symbol table + _symTabCmd->set_symoff(mergedLinkeditStartOffset + sharedSymbolTableStartOffset + _newLocalSymbolsStartIndex*sizeof(macho_nlist

)); + _symTabCmd->set_nsyms(_newLocalSymbolCount+_newExportedSymbolCount+_newImportedSymbolCount); + _symTabCmd->set_stroff(mergedLinkeditStartOffset + sharedSymbolStringsOffset); + _symTabCmd->set_strsize(sharedSymbolStringsSize); + + // update dynamic symbol table to have proper offsets into shared symbol table + _dynSymTabCmd->set_ilocalsym(0); + _dynSymTabCmd->set_nlocalsym(_newLocalSymbolCount); + _dynSymTabCmd->set_iextdefsym(_newExportedSymbolsStartIndex-_newLocalSymbolsStartIndex); + _dynSymTabCmd->set_nextdefsym(_newExportedSymbolCount); + _dynSymTabCmd->set_iundefsym(_newImportedSymbolsStartIndex-_newLocalSymbolsStartIndex); + _dynSymTabCmd->set_nundefsym(_newImportedSymbolCount); + _dynSymTabCmd->set_tocoff(0); + _dynSymTabCmd->set_ntoc(0); + _dynSymTabCmd->set_modtaboff(0); + _dynSymTabCmd->set_nmodtab(0); + _dynSymTabCmd->set_indirectsymoff(mergedLinkeditStartOffset + _newIndirectSymbolTableOffset); + _dynSymTabCmd->set_extreloff(0); + _dynSymTabCmd->set_locreloff(0); + _dynSymTabCmd->set_nlocrel(0); + + // update dyld info + if ( _dyldInfo != nullptr ) { + _dyldInfo->set_rebase_off(0); + _dyldInfo->set_rebase_size(0); + _dyldInfo->set_bind_off(_dyldInfo->bind_size() ? mergedLinkeditStartOffset + _newBindingInfoOffset : 0); + _dyldInfo->set_weak_bind_off(_dyldInfo->weak_bind_size() ? mergedLinkeditStartOffset + _newWeakBindingInfoOffset : 0 ); + _dyldInfo->set_lazy_bind_off(_dyldInfo->lazy_bind_size() ? mergedLinkeditStartOffset + _newLazyBindingInfoOffset : 0 ); + _dyldInfo->set_export_off(mergedLinkeditStartOffset + _newExportInfoOffset); + } + + // update function-starts + if ( _functionStartsCmd != nullptr ) + _functionStartsCmd->set_dataoff(mergedLinkeditStartOffset+_newFunctionStartsOffset); + + // update data-in-code + if ( _dataInCodeCmd != nullptr ) + _dataInCodeCmd->set_dataoff(mergedLinkeditStartOffset+_newDataInCodeOffset); +} + +template +void LinkeditOptimizer

::copyWeakBindingInfo(uint8_t* newLinkEditContent, uint32_t& offset) +{ + if ( _dyldInfo == nullptr ) + return; + unsigned size = _dyldInfo->weak_bind_size(); + if ( size != 0 ) { + ::memcpy(&newLinkEditContent[offset], &_linkeditBias[_dyldInfo->weak_bind_off()], size); + _newWeakBindingInfoOffset = offset; + _newWeakBindingSize = size; + offset += size; + } +} + + +template +void LinkeditOptimizer

::copyLazyBindingInfo(uint8_t* newLinkEditContent, uint32_t& offset) +{ + if ( _dyldInfo == nullptr ) + return; + unsigned size = _dyldInfo->lazy_bind_size(); + if ( size != 0 ) { + ::memcpy(&newLinkEditContent[offset], &_linkeditBias[_dyldInfo->lazy_bind_off()], size); + _newLazyBindingInfoOffset = offset; + offset += size; + } +} + +template +void LinkeditOptimizer

::copyBindingInfo(uint8_t* newLinkEditContent, uint32_t& offset) +{ + if ( _dyldInfo == nullptr ) + return; + unsigned size = _dyldInfo->bind_size(); + if ( size != 0 ) { + ::memcpy(&newLinkEditContent[offset], &_linkeditBias[_dyldInfo->bind_off()], size); + _newBindingInfoOffset = offset; + offset += size; + } +} + +template +void LinkeditOptimizer

::copyExportInfo(uint8_t* newLinkEditContent, uint32_t& offset) +{ + if ( _dyldInfo == nullptr ) + return; + unsigned size = _dyldInfo->export_size(); + if ( size != 0 ) { + ::memcpy(&newLinkEditContent[offset], &_linkeditBias[_dyldInfo->export_off()], size); + _newExportInfoOffset = offset; + offset += size; + } +} + + +template +void LinkeditOptimizer

::copyFunctionStarts(uint8_t* newLinkEditContent, uint32_t& offset) +{ + if ( _functionStartsCmd == nullptr ) + return; + unsigned size = _functionStartsCmd->datasize(); + ::memcpy(&newLinkEditContent[offset], &_linkeditBias[_functionStartsCmd->dataoff()], size); + _newFunctionStartsOffset = offset; + offset += size; +} + +template +void LinkeditOptimizer

::copyDataInCode(uint8_t* newLinkEditContent, uint32_t& offset) +{ + if ( _dataInCodeCmd == nullptr ) + return; + unsigned size = _dataInCodeCmd->datasize(); + ::memcpy(&newLinkEditContent[offset], &_linkeditBias[_dataInCodeCmd->dataoff()], size); + _newDataInCodeOffset = offset; + offset += size; +} + + +template +void LinkeditOptimizer

::copyLocalSymbols(uint8_t* newLinkEditContent, SortedStringPool

& stringPool, uint32_t& offset, uint32_t& symbolIndex, + bool redact, std::vector& localSymbolInfos, + std::vector>& unmappedLocalSymbols, SortedStringPool

& localSymbolsStringPool) +{ + LocalSymbolInfo localInfo; + localInfo.dylibOffset = (uint32_t)(((uint8_t*)_mh) - (uint8_t*)_cacheBuffer); + localInfo.nlistStartIndex = (uint32_t)unmappedLocalSymbols.size(); + localInfo.nlistCount = 0; + _newLocalSymbolsStartIndex = symbolIndex; + const char* strings = (char*)&_linkeditBias[_symTabCmd->stroff()]; + const macho_nlist

* const symbolTable = (macho_nlist

*)(&_linkeditBias[_symTabCmd->symoff()]); + const macho_nlist

* const firstExport = &symbolTable[_dynSymTabCmd->ilocalsym()]; + const macho_nlist

* const lastExport = &symbolTable[_dynSymTabCmd->ilocalsym()+_dynSymTabCmd->nlocalsym()]; + for (const macho_nlist

* entry = firstExport; entry < lastExport; ++entry) { + if ( (entry->n_type() & N_TYPE) != N_SECT) + continue; + if ( (entry->n_type() & N_STAB) != 0) + continue; + const char* name = &strings[entry->n_strx()]; + macho_nlist

* newSymbolEntry = (macho_nlist

*)&newLinkEditContent[offset]; + *newSymbolEntry = *entry; + if ( redact ) { + // if removing local symbols, change __text symbols to "" so backtraces don't have bogus names + if ( entry->n_sect() == 1 ) { + stringPool.add(symbolIndex, ""); + ++symbolIndex; + offset += sizeof(macho_nlist

); + } + // copy local symbol to unmmapped locals area + localSymbolsStringPool.add((uint32_t)unmappedLocalSymbols.size(), name); + unmappedLocalSymbols.push_back(*entry); + unmappedLocalSymbols.back().set_n_strx(0); + } + else { + stringPool.add(symbolIndex, name); + ++symbolIndex; + offset += sizeof(macho_nlist

); + } + } + _newLocalSymbolCount = symbolIndex - _newLocalSymbolsStartIndex; + localInfo.nlistCount = (uint32_t)unmappedLocalSymbols.size() - localInfo.nlistStartIndex; + localSymbolInfos.push_back(localInfo); +} + + +template +void LinkeditOptimizer

::copyExportedSymbols(uint8_t* newLinkEditContent, SortedStringPool

& stringPool, uint32_t& offset, uint32_t& symbolIndex) +{ + _newExportedSymbolsStartIndex = symbolIndex; + const char* strings = (char*)&_linkeditBias[_symTabCmd->stroff()]; + const macho_nlist

* const symbolTable = (macho_nlist

*)(&_linkeditBias[_symTabCmd->symoff()]); + const macho_nlist

* const firstExport = &symbolTable[_dynSymTabCmd->iextdefsym()]; + const macho_nlist

* const lastExport = &symbolTable[_dynSymTabCmd->iextdefsym()+_dynSymTabCmd->nextdefsym()]; + uint32_t oldSymbolIndex = _dynSymTabCmd->iextdefsym(); + for (const macho_nlist

* entry = firstExport; entry < lastExport; ++entry, ++oldSymbolIndex) { + if ( (entry->n_type() & N_TYPE) != N_SECT) + continue; + const char* name = &strings[entry->n_strx()]; + if ( strncmp(name, ".objc_", 6) == 0 ) + continue; + if ( strncmp(name, "$ld$", 4) == 0 ) + continue; + macho_nlist

* newSymbolEntry = (macho_nlist

*)&newLinkEditContent[offset]; + *newSymbolEntry = *entry; + newSymbolEntry->set_n_strx(0); + stringPool.add(symbolIndex, name); + _oldToNewSymbolIndexes[oldSymbolIndex] = symbolIndex - _newLocalSymbolsStartIndex; + ++symbolIndex; + offset += sizeof(macho_nlist

); + } + _newExportedSymbolCount = symbolIndex - _newExportedSymbolsStartIndex; +} + +template +void LinkeditOptimizer

::copyImportedSymbols(uint8_t* newLinkEditContent, SortedStringPool

& stringPool, uint32_t& offset, uint32_t& symbolIndex) +{ + _newImportedSymbolsStartIndex = symbolIndex; + const char* strings = (char*)&_linkeditBias[_symTabCmd->stroff()]; + const macho_nlist

* const symbolTable = (macho_nlist

*)(&_linkeditBias[_symTabCmd->symoff()]); + const macho_nlist

* const firstImport = &symbolTable[_dynSymTabCmd->iundefsym()]; + const macho_nlist

* const lastImport = &symbolTable[_dynSymTabCmd->iundefsym()+_dynSymTabCmd->nundefsym()]; + uint32_t oldSymbolIndex = _dynSymTabCmd->iundefsym(); + for (const macho_nlist

* entry = firstImport; entry < lastImport; ++entry, ++oldSymbolIndex) { + if ( (entry->n_type() & N_TYPE) != N_UNDF) + continue; + const char* name = &strings[entry->n_strx()]; + macho_nlist

* newSymbolEntry = (macho_nlist

*)&newLinkEditContent[offset]; + *newSymbolEntry = *entry; + newSymbolEntry->set_n_strx(0); + stringPool.add(symbolIndex, name); + _oldToNewSymbolIndexes[oldSymbolIndex] = symbolIndex - _newLocalSymbolsStartIndex; + ++symbolIndex; + offset += sizeof(macho_nlist

); + } + _newImportedSymbolCount = symbolIndex - _newImportedSymbolsStartIndex; +} + +template +void LinkeditOptimizer

::copyIndirectSymbolTable(uint8_t* newLinkEditContent, uint32_t& offset) +{ + _newIndirectSymbolTableOffset = offset; + const uint32_t* const indirectTable = (uint32_t*)&_linkeditBias[_dynSymTabCmd->indirectsymoff()]; + uint32_t* newIndirectTable = (uint32_t*)&newLinkEditContent[offset]; + for (int i=0; i < _dynSymTabCmd->nindirectsyms(); ++i) { + uint32_t symbolIndex = E::get32(indirectTable[i]); + if ( (symbolIndex == INDIRECT_SYMBOL_ABS) || (symbolIndex == INDIRECT_SYMBOL_LOCAL) ) + E::set32(newIndirectTable[i], symbolIndex); + else + E::set32(newIndirectTable[i], _oldToNewSymbolIndexes[symbolIndex]); + offset += sizeof(uint32_t); + } +} + +template +uint64_t mergeLinkedits(SharedCache& cache, bool dontMapLocalSymbols, bool addAcceleratorTables, std::vector*>& optimizers) +{ + // allocate space for new linkedit data + uint32_t linkeditStartOffset = 0xFFFFFFFF; + uint32_t linkeditEndOffset = 0; + uint64_t linkeditStartAddr = 0; + for (LinkeditOptimizer

* op : optimizers) { + uint32_t leOffset = op->linkeditOffset(); + if ( leOffset < linkeditStartOffset ) { + linkeditStartOffset = leOffset; + linkeditStartAddr = op->linkeditAddr(); + } + uint32_t leEndOffset = op->linkeditOffset() + op->linkeditSize(); + if ( leEndOffset > linkeditEndOffset ) + linkeditEndOffset = leEndOffset; + } + uint64_t totalUnoptLinkeditsSize = linkeditEndOffset - linkeditStartOffset; + uint8_t* newLinkEdit = (uint8_t*)calloc(totalUnoptLinkeditsSize, 1); + SortedStringPool

stringPool; + uint32_t offset = 0; + + verboseLog("Merged LINKEDIT:"); + + // copy weak binding info + uint32_t startWeakBindInfosOffset = offset; + for (LinkeditOptimizer

* op : optimizers) { + op->copyWeakBindingInfo(newLinkEdit, offset); + } + verboseLog(" weak bindings size: %5uKB", (offset-startWeakBindInfosOffset)/1024); + + // copy export info + uint32_t startExportInfosOffset = offset; + for (LinkeditOptimizer

* op : optimizers) { + op->copyExportInfo(newLinkEdit, offset); + } + verboseLog(" exports info size: %5uKB", (offset-startExportInfosOffset)/1024); + + // in theory, an optimized cache can drop the binding info + if ( true ) { + // copy binding info + uint32_t startBindingsInfosOffset = offset; + for (LinkeditOptimizer

* op : optimizers) { + op->copyBindingInfo(newLinkEdit, offset); + } + verboseLog(" bindings size: %5uKB", (offset-startBindingsInfosOffset)/1024); + + // copy lazy binding info + uint32_t startLazyBindingsInfosOffset = offset; + for (LinkeditOptimizer

* op : optimizers) { + op->copyLazyBindingInfo(newLinkEdit, offset); + } + verboseLog(" lazy bindings size: %5uKB", (offset-startLazyBindingsInfosOffset)/1024); + } + + // copy symbol table entries + std::vector> unmappedLocalSymbols; + if ( dontMapLocalSymbols ) + unmappedLocalSymbols.reserve(0x01000000); + std::vector localSymbolInfos; + localSymbolInfos.reserve(optimizers.size()); + SortedStringPool

localSymbolsStringPool; + uint32_t symbolIndex = 0; + const uint32_t sharedSymbolTableStartOffset = offset; + uint32_t sharedSymbolTableExportsCount = 0; + uint32_t sharedSymbolTableImportsCount = 0; + for (LinkeditOptimizer

* op : optimizers) { + op->copyLocalSymbols(newLinkEdit, stringPool, offset, symbolIndex, dontMapLocalSymbols, + localSymbolInfos, unmappedLocalSymbols, localSymbolsStringPool); + uint32_t x = symbolIndex; + op->copyExportedSymbols(newLinkEdit, stringPool, offset, symbolIndex); + sharedSymbolTableExportsCount += (symbolIndex-x); + uint32_t y = symbolIndex; + op->copyImportedSymbols(newLinkEdit, stringPool, offset, symbolIndex); + sharedSymbolTableImportsCount += (symbolIndex-y); + } + uint32_t sharedSymbolTableCount = symbolIndex; + const uint32_t sharedSymbolTableEndOffset = offset; + + // copy function starts + uint32_t startFunctionStartsOffset = offset; + for (LinkeditOptimizer

* op : optimizers) { + op->copyFunctionStarts(newLinkEdit, offset); + } + verboseLog(" function starts size: %5uKB", (offset-startFunctionStartsOffset)/1024); + + // copy data-in-code info + uint32_t startDataInCodeOffset = offset; + for (LinkeditOptimizer

* op : optimizers) { + op->copyDataInCode(newLinkEdit, offset); + } + verboseLog(" data in code size: %5uB", offset-startDataInCodeOffset); + + // copy indirect symbol tables + for (LinkeditOptimizer

* op : optimizers) { + op->copyIndirectSymbolTable(newLinkEdit, offset); + } + // if indirect table has odd number of entries, end will not be 8-byte aligned + if ( (offset % sizeof(typename P::uint_t)) != 0 ) + offset += 4; + + // copy string pool + uint32_t sharedSymbolStringsOffset = offset; + uint32_t sharedSymbolStringsSize = stringPool.copyPoolAndUpdateOffsets((char*)&newLinkEdit[sharedSymbolStringsOffset], (macho_nlist

*)&newLinkEdit[sharedSymbolTableStartOffset]); + offset += sharedSymbolStringsSize; + uint32_t newLinkeditUnalignedSize = offset; + uint64_t newLinkeditEnd = align(linkeditStartOffset+newLinkeditUnalignedSize, 14); + verboseLog(" symbol table size: %5uKB (%d exports, %d imports)", (sharedSymbolTableEndOffset-sharedSymbolTableStartOffset)/1024, sharedSymbolTableExportsCount, sharedSymbolTableImportsCount); + verboseLog(" symbol string pool size: %5uKB", sharedSymbolStringsSize/1024); + + // overwrite mapped LINKEDIT area in cache with new merged LINKEDIT content + verboseLog("LINKEDITS optimized from %uMB to %uMB", (uint32_t)totalUnoptLinkeditsSize/(1024*1024), (uint32_t)newLinkeditUnalignedSize/(1024*1024)); + ::memcpy((char*)cache.buffer().get()+linkeditStartOffset, newLinkEdit, newLinkeditUnalignedSize); + ::bzero((char*)cache.buffer().get()+linkeditStartOffset+newLinkeditUnalignedSize, totalUnoptLinkeditsSize-newLinkeditUnalignedSize); + ::free(newLinkEdit); + + // If making cache for customers, add extra accelerator tables for dyld + if ( addAcceleratorTables ) { + AcceleratorTables

tables(cache.buffer().get(), linkeditStartAddr, optimizers); + uint32_t tablesSize = tables.totalSize(); + if ( tablesSize < (totalUnoptLinkeditsSize-newLinkeditUnalignedSize) ) { + tables.copyTo((uint8_t*)cache.buffer().get()+newLinkeditEnd); + newLinkeditEnd += tablesSize; + uint64_t accelInfoAddr = align(linkeditStartAddr + newLinkeditUnalignedSize, 14); + cache.setAcceleratorInfoRange(accelInfoAddr, tablesSize); + } + else { + warning("not enough room to add dyld accelerator tables"); + } + } + + // update mapping to reduce linkedit size + cache.setLinkeditsMappingEndFileOffset(newLinkeditEnd); + + // overwrite end of un-opt linkedits to create a new unmapped region for local symbols + uint64_t newFileSize = newLinkeditEnd; + if ( dontMapLocalSymbols ) { + typedef typename P::E E; + uint32_t localSymbolsOffset = (uint32_t)align(newFileSize, 14); + uint32_t spaceAtEnd = linkeditEndOffset - (uint32_t)newLinkeditEnd; + dyldCacheLocalSymbolsInfo* infoHeader = (dyldCacheLocalSymbolsInfo*)((uint8_t*)cache.buffer().get()+localSymbolsOffset); + const uint32_t entriesOffset = sizeof(dyldCacheLocalSymbolsInfo); + const uint32_t entriesCount = (uint32_t)localSymbolInfos.size(); + const uint32_t nlistOffset = (uint32_t)align(entriesOffset + entriesCount * sizeof(dyldCacheLocalSymbolEntry), 4); // 16-byte align start + const uint32_t nlistCount = (uint32_t)unmappedLocalSymbols.size(); + const uint32_t stringsOffset = nlistOffset + nlistCount * sizeof(macho_nlist

); + // copy info for each dylib + dyldCacheLocalSymbolEntry* entries = (dyldCacheLocalSymbolEntry*)(((uint8_t*)infoHeader)+entriesOffset); + for (int i=0; i < entriesCount; ++i) { + entries[i].set_dylibOffset(localSymbolInfos[i].dylibOffset); + entries[i].set_nlistStartIndex(localSymbolInfos[i].nlistStartIndex); + entries[i].set_nlistCount(localSymbolInfos[i].nlistCount); + } + // copy nlists + macho_nlist

* newLocalsSymbolTable = (macho_nlist

*)(((uint8_t*)infoHeader)+nlistOffset); + ::memcpy(newLocalsSymbolTable, &unmappedLocalSymbols[0], nlistCount*sizeof(macho_nlist

)); + // copy string pool + const uint32_t stringsSize = localSymbolsStringPool.copyPoolAndUpdateOffsets(((char*)infoHeader)+stringsOffset, newLocalsSymbolTable); + const uint32_t localsRegionSize = (uint32_t)align(stringsOffset+stringsSize, 14); + if ( localsRegionSize > spaceAtEnd ) + terminate("not enough room to store local symbols"); + // fill in local symbols info + infoHeader->set_nlistOffset(nlistOffset); + infoHeader->set_nlistCount(nlistCount); + infoHeader->set_stringsOffset(stringsOffset); + infoHeader->set_stringsSize(stringsSize); + infoHeader->set_entriesOffset(entriesOffset); + infoHeader->set_entriesCount(entriesCount); + // update cache size + newFileSize += localsRegionSize; + verboseLog("Unmapped local symbol info: %uMB", localsRegionSize/(1024*1024)); + cache.setUnmappedLocalsRange(localSymbolsOffset, localsRegionSize); + } + + // update all load commands to new merged layout + for (LinkeditOptimizer

* op : optimizers) { + op->updateLoadCommands(linkeditStartOffset, linkeditStartAddr, newLinkeditEnd-linkeditStartOffset, + sharedSymbolTableStartOffset, sharedSymbolTableCount, + sharedSymbolStringsOffset, sharedSymbolStringsSize); + } + + return newFileSize; +} + +} // anonymous namespace + +template +void SharedCache::optimizeLinkedit(bool dontMapLocalSymbols, bool addAcceleratorTables, const std::vector& branchPoolOffsets) +{ + // construct a LinkeditOptimizer for each image + std::vector*> optimizers; + forEachImage([&](const void* mh, const char*, time_t, ino_t, const std::vector&) { + optimizers.push_back(new LinkeditOptimizer

(_buffer.get(), (macho_header

*)mh)); + }); + // add optimizer for each branch pool + for (uint64_t poolOffset : branchPoolOffsets) { + macho_header

* mh = (macho_header

*)((char*)_buffer.get() + poolOffset); + optimizers.push_back(new LinkeditOptimizer

(_buffer.get(), mh)); + } + + // merge linkedit info + _fileSize = mergeLinkedits(*this, dontMapLocalSymbols, addAcceleratorTables, optimizers); + + // delete optimizers + for (LinkeditOptimizer

* op : optimizers) + delete op; +} + +void SharedCache::optimizeLinkedit(bool dontMapLocalSymbols, bool addAcceleratorTables, const std::vector& branchPoolOffsets) +{ + switch ( _arch.arch ) { + case CPU_TYPE_ARM: + case CPU_TYPE_I386: + optimizeLinkedit>(dontMapLocalSymbols, addAcceleratorTables, branchPoolOffsets); + break; + case CPU_TYPE_X86_64: + case CPU_TYPE_ARM64: + optimizeLinkedit>(dontMapLocalSymbols, addAcceleratorTables, branchPoolOffsets); + break; + default: + terminate("unsupported arch 0x%08X", _arch.arch); + } +} + + + diff --git a/src/dyld-apple/interlinked-dylibs/OptimizerObjC.cpp b/src/dyld-apple/interlinked-dylibs/OptimizerObjC.cpp new file mode 100644 index 000000000..23c44748e --- /dev/null +++ b/src/dyld-apple/interlinked-dylibs/OptimizerObjC.cpp @@ -0,0 +1,826 @@ +/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- + * + * Copyright (c) 2014 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@ + */ + + +#include "mega-dylib-utils.h" +#include "Logging.h" +#include "MachOFileAbstraction.hpp" + + +#include +#include +#include +#include +#include +#include + + +// Scan a C++ or Swift length-mangled field. +static bool scanMangledField(const char *&string, const char *end, + const char *&field, int& length) +{ + // Leading zero not allowed. + if (*string == '0') return false; + + length = 0; + field = string; + while (field < end) { + char c = *field; + if (!isdigit(c)) break; + field++; + if (__builtin_smul_overflow(length, 10, &length)) return false; + if (__builtin_sadd_overflow(length, c - '0', &length)) return false; + } + + string = field + length; + return length > 0 && string <= end; +} + + +// copySwiftDemangledName +// Returns the pretty form of the given Swift-mangled class or protocol name. +// Returns nullptr if the string doesn't look like a mangled Swift name. +// The result must be freed with free(). +static char *copySwiftDemangledName(const char *string, bool isProtocol = false) +{ + if (!string) return nullptr; + + // Swift mangling prefix. + if (strncmp(string, isProtocol ? "_TtP" : "_TtC", 4) != 0) return nullptr; + string += 4; + + const char *end = string + strlen(string); + + // Module name. + const char *prefix; + int prefixLength; + if (string[0] == 's') { + // "s" is the Swift module. + prefix = "Swift"; + prefixLength = 5; + string += 1; + } else { + if (! scanMangledField(string, end, prefix, prefixLength)) return nullptr; + } + + // Class or protocol name. + const char *suffix; + int suffixLength; + if (! scanMangledField(string, end, suffix, suffixLength)) return nullptr; + + if (isProtocol) { + // Remainder must be "_". + if (strcmp(string, "_") != 0) return nullptr; + } else { + // Remainder must be empty. + if (string != end) return nullptr; + } + + char *result; + asprintf(&result, "%.*s.%.*s", prefixLength,prefix, suffixLength,suffix); + return result; +} + + +class ContentAccessor { +public: + ContentAccessor(SharedCache& cache) { + cache.forEachRegion([&] (void* content, uint64_t vmAddr, uint64_t size, uint32_t permissions) { + Info info = { (uint8_t*)content, (uint8_t*)content+size, vmAddr, vmAddr+size }; + _regions.push_back(info); + }); + } + + void* contentForVMAddr(uint64_t vmaddr) { + for (Info& info : _regions) { + if ( (info.startAddr <= vmaddr) && (vmaddr < info.endAddr) ) + return (void*)(info.contentStart + vmaddr - info.startAddr); + } + if ( vmaddr == 0 ) + return nullptr; + terminate("contentForVMAddr(0x%0llX) invalid vmaddr in ObjC data", vmaddr); + } + + uint64_t vmAddrForContent(const void* content) { + for (Info& info : _regions) { + if ( (info.contentStart <= content) && (content < info.contentEnd) ) + return info.startAddr + ((uint8_t*)content - (uint8_t*)info.contentStart); + } + terminate("vmAddrForContent(%p) invalid content pointer in ObjC data", content); + } + +private: + struct Info { uint8_t* contentStart; uint8_t* contentEnd; uint64_t startAddr; uint64_t endAddr; }; + std::vector _regions; +}; + + +// Access a section containing a list of pointers +template +class PointerSection +{ + typedef typename P::uint_t pint_t; +public: + PointerSection(ContentAccessor* cache, const macho_header

* mh, + const char* segname, const char* sectname) + : _cache(cache), + _section(mh->getSection(segname, sectname)), + _base(_section ? (pint_t*)cache->contentForVMAddr(_section->addr()) : 0), + _count(_section ? (pint_t)(_section->size() / sizeof(pint_t)) : 0) { + } + + pint_t count() const { return _count; } + + pint_t getVMAddress(pint_t index) const { + if ( index >= _count ) + terminate("index out of range in section %s", _section->sectname()); + return (pint_t)P::getP(_base[index]); + } + + T get(pint_t index) const { + return (T)_cache->contentForVMAddr(getVMAddress(index)); + } + + void setVMAddress(pint_t index, pint_t value) { + if (index >= _count) + terminate("index out of range in section %s", _section->sectname()); + P::setP(_base[index], value); + } + + void removeNulls() { + pint_t shift = 0; + for (pint_t i = 0; i < _count; i++) { + pint_t value = _base[i]; + if (value) { + _base[i-shift] = value; + } else { + shift++; + } + } + _count -= shift; + const_cast*>(_section)->set_size(_count * sizeof(pint_t)); + } + +private: + ContentAccessor* const _cache; + const macho_section

* const _section; + pint_t* const _base; + pint_t const _count; +}; + + +// Access a section containing an array of structures +template +class ArraySection +{ +public: + ArraySection(ContentAccessor* cache, const macho_header

* mh, + const char *segname, const char *sectname) + : _cache(cache), + _section(mh->getSection(segname, sectname)), + _base(_section ? (T *)cache->contentForVMAddr(_section->addr()) : 0), + _count(_section ? _section->size() / sizeof(T) : 0) { + } + + uint64_t count() const { return _count; } + + T& get(uint64_t index) const { + if (index >= _count) + terminate("index out of range in section %s", _section->sectname()); + return _base[index]; + } + +private: + ContentAccessor* const _cache; + const macho_section

* const _section; + T * const _base; + uint64_t const _count; +}; + + +#define SELOPT_WRITE +#include "objc-shared-cache.h" +#include "ObjC1Abstraction.hpp" +#include "ObjC2Abstraction.hpp" + + +namespace { + + + +template +class ObjCSelectorUniquer +{ +public: + typedef typename P::uint_t pint_t; + + ObjCSelectorUniquer(ContentAccessor* cache) : _cache(cache) { } + + pint_t visit(pint_t oldValue) + { + _count++; + const char *s = (const char *)_cache->contentForVMAddr(oldValue); + objc_opt::string_map::iterator element = + _selectorStrings.insert(objc_opt::string_map::value_type(s, oldValue)).first; + return (pint_t)element->second; + } + + objc_opt::string_map& strings() { + return _selectorStrings; + } + + size_t count() const { return _count; } + +private: + objc_opt::string_map _selectorStrings; + ContentAccessor* _cache; + size_t _count = 0; +}; + + +template +class ClassListBuilder +{ +private: + objc_opt::string_map _classNames; + objc_opt::class_map _classes; + size_t _count = 0; + HeaderInfoOptimizer>& _hInfos; + +public: + + ClassListBuilder(HeaderInfoOptimizer>& hinfos) : _hInfos(hinfos) { } + + void visitClass(ContentAccessor* cache, + const macho_header

* header, + objc_class_t

* cls) + { + if (cls->isMetaClass(cache)) return; + + const char *name = cls->getName(cache); + uint64_t name_vmaddr = cache->vmAddrForContent((void*)name); + uint64_t cls_vmaddr = cache->vmAddrForContent(cls); + uint64_t hinfo_vmaddr = cache->vmAddrForContent(_hInfos.hinfoForHeader(cache, header)); + _classNames.insert(objc_opt::string_map::value_type(name, name_vmaddr)); + _classes.insert(objc_opt::class_map::value_type(name, std::pair(cls_vmaddr, hinfo_vmaddr))); + _count++; + } + + objc_opt::string_map& classNames() { + return _classNames; + } + + objc_opt::class_map& classes() { + return _classes; + } + + size_t count() const { return _count; } +}; + +template +class ProtocolOptimizer +{ +private: + typedef typename P::uint_t pint_t; + + objc_opt::string_map _protocolNames; + objc_opt::protocol_map _protocols; + size_t _protocolCount; + size_t _protocolReferenceCount; + + friend class ProtocolReferenceWalker>; + + pint_t visitProtocolReference(ContentAccessor* cache, pint_t oldValue) + { + objc_protocol_t

* proto = (objc_protocol_t

*) + cache->contentForVMAddr(oldValue); + pint_t newValue = (pint_t)_protocols[proto->getName(cache)]; + if (oldValue != newValue) _protocolReferenceCount++; + return newValue; + } + +public: + + ProtocolOptimizer() + : _protocolNames() + , _protocols() + , _protocolCount(0) + , _protocolReferenceCount(0) + { } + + void addProtocols(ContentAccessor* cache, + const macho_header

* header) + { + PointerSection *> + protocols(cache, header, "__DATA", "__objc_protolist"); + + for (pint_t i = 0; i < protocols.count(); i++) { + objc_protocol_t

*proto = protocols.get(i); + + const char *name = proto->getName(cache); + if (_protocolNames.count(name) == 0) { + if (proto->getSize() > sizeof(objc_protocol_t

)) { + terminate("objc protocol is too big"); + } + + uint64_t name_vmaddr = cache->vmAddrForContent((void*)name); + uint64_t proto_vmaddr = cache->vmAddrForContent(proto); + _protocolNames.insert(objc_opt::string_map::value_type(name, name_vmaddr)); + _protocols.insert(objc_opt::protocol_map::value_type(name, proto_vmaddr)); + _protocolCount++; + } + } + } + + const char *writeProtocols(ContentAccessor* cache, + uint8_t *& rwdest, size_t& rwremaining, + uint8_t *& rodest, size_t& roremaining, + std::vector& pointersInData, + pint_t protocolClassVMAddr) + { + if (_protocolCount == 0) return NULL; + + if (protocolClassVMAddr == 0) { + return "libobjc's Protocol class symbol not found (metadata not optimized)"; + } + + size_t rwrequired = _protocolCount * sizeof(objc_protocol_t

); + if (rwremaining < rwrequired) { + return "libobjc's read-write section is too small (metadata not optimized)"; + } + + for (objc_opt::protocol_map::iterator iter = _protocols.begin(); + iter != _protocols.end(); + ++iter) + { + objc_protocol_t

* oldProto = (objc_protocol_t

*) + cache->contentForVMAddr(iter->second); + + // Create a new protocol object. + objc_protocol_t

* proto = (objc_protocol_t

*)rwdest; + rwdest += sizeof(*proto); + rwremaining -= sizeof(*proto); + + // Initialize it. + uint32_t oldSize = oldProto->getSize(); + memcpy(proto, oldProto, oldSize); + if (!proto->getIsaVMAddr()) { + proto->setIsaVMAddr(protocolClassVMAddr); + } + if (oldSize < sizeof(*proto)) { + // Protocol object is old. Populate new fields. + proto->setSize(sizeof(objc_protocol_t

)); + // missing extendedMethodTypes is already nil + } + // Some protocol objects are big enough to have the + // demangledName field but don't initialize it. + // Initialize it here if it is not already set. + if (!proto->getDemangledName(cache)) { + const char *roName = proto->getName(cache); + char *demangledName = copySwiftDemangledName(roName, true); + if (demangledName) { + size_t length = 1 + strlen(demangledName); + if (roremaining < length) { + return "libobjc's read-only section is too small (metadata not optimized)"; + } + + memmove(rodest, demangledName, length); + roName = (const char *)rodest; + rodest += length; + roremaining -= length; + + free(demangledName); + } + proto->setDemangledName(cache, roName); + } + proto->setFixedUp(); + + // Redirect the protocol table at our new object. + iter->second = cache->vmAddrForContent(proto); + + // Add new rebase entries. + proto->addPointers(pointersInData); + } + + return NULL; + } + + void updateReferences(ContentAccessor* cache, const macho_header

* header) + { + ProtocolReferenceWalker> refs(*this); + refs.walk(cache, header); + } + + objc_opt::string_map& protocolNames() { + return _protocolNames; + } + + objc_opt::protocol_map& protocols() { + return _protocols; + } + + size_t protocolCount() const { return _protocolCount; } + size_t protocolReferenceCount() const { return _protocolReferenceCount; } +}; + + +static int percent(size_t num, size_t denom) { + if (denom) + return (int)(num / (double)denom * 100); + else + return 100; +} + + +template +void optimizeObjC(SharedCache& cache, std::vector& pointersForASLR, bool forProduction) +{ + typedef typename P::E E; + typedef typename P::uint_t pint_t; + + verboseLog("Optimizing objc metadata:"); + verboseLog(" cache type is %s", + forProduction ? "production" : "development"); + + ContentAccessor cacheAccessor(cache); + + size_t headerSize = P::round_up(sizeof(objc_opt::objc_opt_t)); + if (headerSize != sizeof(objc_opt::objc_opt_t)) { + warning("libobjc's optimization structure size is wrong (metadata not optimized)"); + } + + // + // Find libobjc's empty sections and build list of images with objc metadata + // + const macho_section

*optROSection = nullptr; + const macho_section

*optRWSection = nullptr; + const macho_section

*optPointerListSection = nullptr; + std::vector*> objcDylibs; + cache.forEachImage([&](const void* machHeader, const char* installName, + time_t, ino_t, const std::vector& segments) { + const macho_header

* mh = (const macho_header

*)machHeader; + if ( strstr(installName, "/libobjc.") != nullptr ) { + optROSection = mh->getSection("__TEXT", "__objc_opt_ro"); + optRWSection = mh->getSection("__DATA", "__objc_opt_rw"); + optPointerListSection = mh->getSection("__DATA", "__objc_opt_ptrs"); + } + if ( mh->getSection("__DATA", "__objc_imageinfo") || mh->getSection("__OBJC", "__image_info") ) { + objcDylibs.push_back(mh); + } + // log("installName %s at mhdr 0x%016lx", installName, (uintptr_t)cacheAccessor.vmAddrForContent((void*)mh)); + }); + if ( optROSection == nullptr ) { + warning("libobjc's read-only section missing (metadata not optimized)"); + return; + } + if ( optRWSection == nullptr ) { + warning("libobjc's read/write section missing (metadata not optimized)"); + return; + } + if ( optPointerListSection == nullptr ) { + warning("libobjc's pointer list section missing (metadata not optimized)"); + return; + } + + uint8_t* optROData = (uint8_t*)cacheAccessor.contentForVMAddr(optROSection->addr()); + size_t optRORemaining = optROSection->size(); + uint8_t* optRWData = (uint8_t*)cacheAccessor.contentForVMAddr(optRWSection->addr()); + size_t optRWRemaining = optRWSection->size(); + if (optRORemaining < headerSize) { + warning("libobjc's read-only section is too small (metadata not optimized)"); + return; + } + objc_opt::objc_opt_t* optROHeader = (objc_opt::objc_opt_t *)optROData; + optROData += headerSize; + optRORemaining -= headerSize; + if (E::get32(optROHeader->version) != objc_opt::VERSION) { + warning("libobjc's read-only section version is unrecognized (metadata not optimized)"); + return; + } + + if (optPointerListSection->size() < sizeof(objc_opt::objc_opt_pointerlist_tt)) { + warning("libobjc's pointer list section is too small (metadata not optimized)"); + return; + } + const objc_opt::objc_opt_pointerlist_tt *optPointerList = (const objc_opt::objc_opt_pointerlist_tt *)cacheAccessor.contentForVMAddr(optPointerListSection->addr()); + + // Write nothing to optROHeader until everything else is written. + // If something fails below, libobjc will not use the section. + + + // + // Make copy of objcList and sort that list. + // + std::vector*> addressSortedDylibs = objcDylibs; + std::sort(addressSortedDylibs.begin(), addressSortedDylibs.end(), [](const macho_header

* lmh, const macho_header

* rmh) -> bool { + return lmh < rmh; + }); + + // + // Build HeaderInfo list in cache + // + // First the RO header info + // log("writing out %d RO dylibs at offset %d", (uint32_t)objcDylibs.size(), (uint32_t)(optROSection->size() - optRORemaining)); + uint64_t hinfoROVMAddr = optROSection->addr() + optROSection->size() - optRORemaining; + HeaderInfoOptimizer> hinfoROOptimizer; + const char* err = hinfoROOptimizer.init((uint32_t)objcDylibs.size(), optROData, optRORemaining); + if (err) { + warning("%s", err); + return; + } + else { + for (const macho_header

* mh : addressSortedDylibs) { + hinfoROOptimizer.update(&cacheAccessor, mh, pointersForASLR); + } + } + + // Then the RW header info + // log("writing out %d RW dylibs at offset %d", (uint32_t)objcDylibs.size(), (uint32_t)(optRWSection->size() - optRWRemaining)); + uint64_t hinfoRWVMAddr = (uint64_t)optRWSection->addr() + (uint64_t)optRWSection->size() - optRWRemaining; + HeaderInfoOptimizer> hinfoRWOptimizer; + err = hinfoRWOptimizer.init((uint32_t)objcDylibs.size(), optRWData, optRWRemaining); + if (err) { + warning("%s", err); + return; + } + else { + for (const macho_header

* mh : addressSortedDylibs) { + hinfoRWOptimizer.update(&cacheAccessor, mh, pointersForASLR); + } + } + + // + // Update selector references and build selector list + // + // This is SAFE: if we run out of room for the selector table, + // the modified binaries are still usable. + // + // Heuristic: choose selectors from libraries with more selector cstring data first. + // This tries to localize selector cstring memory. + // + ObjCSelectorUniquer

uniq(&cacheAccessor); + std::vector*> sizeSortedDylibs = objcDylibs; + std::sort(sizeSortedDylibs.begin(), sizeSortedDylibs.end(), [](const macho_header

* lmh, const macho_header

* rmh) -> bool { + const macho_section

* lSection = lmh->getSection("__TEXT", "__objc_methname"); + const macho_section

* rSection = rmh->getSection("__TEXT", "__objc_methname"); + uint64_t lSelectorSize = (lSection ? lSection->size() : 0); + uint64_t rSelectorSize = (rSection ? rSection->size() : 0); + return lSelectorSize > rSelectorSize; + }); + + SelectorOptimizer > selOptimizer(uniq); + for (const macho_header

* mh : sizeSortedDylibs) { + LegacySelectorUpdater>::update(&cacheAccessor, mh, uniq); + selOptimizer.optimize(&cacheAccessor, mh); + } + + verboseLog(" uniqued % 6ld selectors", + uniq.strings().size()); + verboseLog(" updated % 6ld selector references", + uniq.count()); + + uint64_t seloptVMAddr = optROSection->addr() + optROSection->size() - optRORemaining; + objc_opt::objc_selopt_t *selopt = new(optROData) objc_opt::objc_selopt_t; + err = selopt->write(seloptVMAddr, optRORemaining, uniq.strings()); + if (err) { + warning("%s", err); + return; + } + optROData += selopt->size(); + optRORemaining -= selopt->size(); + uint32_t seloptCapacity = selopt->capacity; + uint32_t seloptOccupied = selopt->occupied; + selopt->byteswap(E::little_endian), selopt = nullptr; + + verboseLog(" selector table occupancy %u/%u (%u%%)", + seloptOccupied, seloptCapacity, + (unsigned)(seloptOccupied/(double)seloptCapacity*100)); + + + // + // Detect classes that have missing weak-import superclasses. + // + // Production only. Development cache does not do this: a replacement + // library could omit a class at runtime that was present during + // cache construction. + // + // This is SAFE: the binaries themselves are unmodified. + bool noMissingWeakSuperclasses = false; // dev cache can't promise otherwise + if (forProduction) { + WeakClassDetector

weakopt; + noMissingWeakSuperclasses = + weakopt.noMissingWeakSuperclasses(&cacheAccessor, sizeSortedDylibs); + + // Shared cache does not currently support unbound weak references. + // Here we assert that there are none. If support is added later then + // this assertion needs to be removed and this path needs to be tested. + if (!noMissingWeakSuperclasses) { + terminate("Some Objective-C class has a superclass that is " + "weak-import and missing from the cache."); + } + } + + + // + // Build class table. + // + // This is SAFE: the binaries themselves are unmodified. + ClassListBuilder

classes(hinfoROOptimizer); + ClassWalker> classWalker(classes); + for (const macho_header

* mh : sizeSortedDylibs) { + classWalker.walk(&cacheAccessor, mh); + } + + verboseLog(" recorded % 6ld classes", + classes.classNames().size()); + + uint64_t clsoptVMAddr = optROSection->addr() + optROSection->size() - optRORemaining; + objc_opt::objc_clsopt_t *clsopt = new(optROData) objc_opt::objc_clsopt_t; + err = clsopt->write(clsoptVMAddr, optRORemaining, + classes.classNames(), classes.classes(), false); + if (err) { + warning("%s", err); + return; + } + optROData += clsopt->size(); + optRORemaining -= clsopt->size(); + size_t duplicateCount = clsopt->duplicateCount(); + uint32_t clsoptCapacity = clsopt->capacity; + uint32_t clsoptOccupied = clsopt->occupied; + clsopt->byteswap(E::little_endian); + clsopt = nullptr; + + verboseLog(" found % 6ld duplicate classes", + duplicateCount); + verboseLog(" class table occupancy %u/%u (%u%%)", + clsoptOccupied, clsoptCapacity, + (unsigned)(clsoptOccupied/(double)clsoptCapacity*100)); + + + // + // Sort method lists. + // + // This is SAFE: modified binaries are still usable as unsorted lists. + // This must be done AFTER uniquing selectors. + MethodListSorter

methodSorter; + for (const macho_header

* mh : sizeSortedDylibs) { + methodSorter.optimize(&cacheAccessor, mh); + } + + verboseLog(" sorted % 6ld method lists", + methodSorter.optimized()); + + + // Unique protocols and build protocol table. + + // This is SAFE: no protocol references are updated yet + // This must be done AFTER updating method lists. + + ProtocolOptimizer

protocolOptimizer; + for (const macho_header

* mh : sizeSortedDylibs) { + protocolOptimizer.addProtocols(&cacheAccessor, mh); + } + + verboseLog(" uniqued % 6ld protocols", + protocolOptimizer.protocolCount()); + + pint_t protocolClassVMAddr = (pint_t)P::getP(optPointerList->protocolClass); + err = protocolOptimizer.writeProtocols(&cacheAccessor, + optRWData, optRWRemaining, + optROData, optRORemaining, + pointersForASLR, protocolClassVMAddr); + if (err) { + warning("%s", err); + return; + } + + uint64_t protocoloptVMAddr = optROSection->addr() + optROSection->size() - optRORemaining; + objc_opt::objc_protocolopt_t *protocolopt = new (optROData) objc_opt::objc_protocolopt_t; + err = protocolopt->write(protocoloptVMAddr, optRORemaining, + protocolOptimizer.protocolNames(), + protocolOptimizer.protocols(), true); + if (err) { + warning("%s", err); + return; + } + optROData += protocolopt->size(); + optRORemaining -= protocolopt->size(); + uint32_t protocoloptCapacity = protocolopt->capacity; + uint32_t protocoloptOccupied = protocolopt->occupied; + protocolopt->byteswap(E::little_endian), protocolopt = NULL; + + verboseLog(" protocol table occupancy %u/%u (%u%%)", + protocoloptOccupied, protocoloptCapacity, + (unsigned)(protocoloptOccupied/(double)protocoloptCapacity*100)); + + + // Redirect protocol references to the uniqued protocols. + + // This is SAFE: the new protocol objects are still usable as-is. + for (const macho_header

* mh : sizeSortedDylibs) { + protocolOptimizer.updateReferences(&cacheAccessor, mh); + } + + verboseLog(" updated % 6ld protocol references", + protocolOptimizer.protocolReferenceCount()); + + + // + // Repair ivar offsets. + // + // This is SAFE: the runtime always validates ivar offsets at runtime. + IvarOffsetOptimizer

ivarOffsetOptimizer; + for (const macho_header

* mh : sizeSortedDylibs) { + ivarOffsetOptimizer.optimize(&cacheAccessor, mh); + } + + verboseLog(" updated % 6ld ivar offsets", + ivarOffsetOptimizer.optimized()); + + + // Collect flags. + uint32_t headerFlags = 0; + if (forProduction) { + headerFlags |= objc_opt::IsProduction; + } + if (noMissingWeakSuperclasses) { + headerFlags |= objc_opt::NoMissingWeakSuperclasses; + } + + + // Success. Mark dylibs as optimized. + for (const macho_header

* mh : sizeSortedDylibs) { + const macho_section

* imageInfoSection = mh->getSection("__DATA", "__objc_imageinfo"); + if (!imageInfoSection) { + imageInfoSection = mh->getSection("__OBJC", "__image_info"); + } + if (imageInfoSection) { + objc_image_info

* info = (objc_image_info

*)cacheAccessor.contentForVMAddr(imageInfoSection->addr()); + info->setOptimizedByDyld(); + } + } + + + // Success. Update RO header last. + E::set32(optROHeader->flags, headerFlags); + E::set32(optROHeader->selopt_offset, (uint32_t)(seloptVMAddr - optROSection->addr())); + E::set32(optROHeader->clsopt_offset, (uint32_t)(clsoptVMAddr - optROSection->addr())); + E::set32(optROHeader->protocolopt_offset, (uint32_t)(protocoloptVMAddr - optROSection->addr())); + E::set32(optROHeader->headeropt_ro_offset, (uint32_t)(hinfoROVMAddr - optROSection->addr())); + E::set32(optROHeader->headeropt_rw_offset, (uint32_t)(hinfoRWVMAddr - optROSection->addr())); + + // Log statistics. + size_t roSize = optROSection->size() - optRORemaining; + size_t rwSize = optRWSection->size() - optRWRemaining; + verboseLog(" %zu/%llu bytes " + "(%d%%) used in libobjc read-only optimization section", + roSize, optROSection->size(), + percent(roSize, optROSection->size())); + verboseLog(" %zu/%llu bytes " + "(%d%%) used in libobjc read/write optimization section", + rwSize, optRWSection->size(), + percent(rwSize, optRWSection->size())); + verboseLog(" wrote objc metadata optimization version %d", + objc_opt::VERSION); +} + + +} // anon namespace + + +void SharedCache::optimizeObjC(bool forProduction) +{ + switch ( _arch.arch ) { + case CPU_TYPE_ARM: + case CPU_TYPE_I386: + ::optimizeObjC>(*this, _pointersForASLR, forProduction); + break; + case CPU_TYPE_X86_64: + case CPU_TYPE_ARM64: + ::optimizeObjC>(*this, _pointersForASLR, forProduction); + break; + default: + terminate("unsupported arch 0x%08X", _arch.arch); + } +} diff --git a/src/dyld-apple/interlinked-dylibs/SharedCache.cpp b/src/dyld-apple/interlinked-dylibs/SharedCache.cpp new file mode 100644 index 000000000..acabc4cce --- /dev/null +++ b/src/dyld-apple/interlinked-dylibs/SharedCache.cpp @@ -0,0 +1,1623 @@ +/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- + * + * Copyright (c) 2014 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@ + */ + +#include "mega-dylib-utils.h" +#include "MachOFileAbstraction.hpp" +#include "FileAbstraction.hpp" +#include "Logging.h" + +#include "dyld_cache_config.h" + +#import "Trie.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "OptimizerBranches.h" + +#include "CacheFileAbstraction.hpp" +#include "CodeSigningTypes.h" + +namespace { + +uint64_t sharedRegionStartExecutableAddress(ArchPair arch) +{ + switch (arch.arch) { + case CPU_TYPE_ARM: + return ARM_SHARED_REGION_START; + case CPU_TYPE_I386: + return SHARED_REGION_BASE_I386; + case CPU_TYPE_X86_64: + return SHARED_REGION_BASE_X86_64; + case CPU_TYPE_ARM64: + return ARM64_SHARED_REGION_START; + default: + terminate("unsupported arch 0x%08X", arch.arch); + } +} + +uint64_t sharedRegionStartWriteableAddress(ArchPair arch, uint64_t textEndAddress) +{ + switch (arch.arch) { + case CPU_TYPE_I386: + case CPU_TYPE_X86_64: + // more efficient if code and data never in same 2MB chunk + return textEndAddress + 0x04000000; + case CPU_TYPE_ARM: + return textEndAddress; + case CPU_TYPE_ARM64: + return textEndAddress + 32*1024*1024; // Add 32MB padding before arm64 dyld shared cache R/W region + default: + terminate("unsupported arch 0x%08X", arch.arch); + } +} + +uint64_t sharedRegionStartReadOnlyAddress(ArchPair arch, uint64_t dataEndAddress, uint64_t textEndAddress) +{ + switch (arch.arch) { + case CPU_TYPE_I386: + case CPU_TYPE_X86_64: + // more efficient if code and data never in same 2MB chunk + return dataEndAddress + 0x04000000; + case CPU_TYPE_ARM: + return dataEndAddress; + case CPU_TYPE_ARM64: + return dataEndAddress + 32*1024*1024; // Add 32MB padding before arm64 dyld shared cache R/W region + default: + terminate("unsupported arch 0x%08X", arch.arch); + } +} + +} // anon namespace + +uint8_t sharedRegionRegionAlignment(ArchPair arch) { + switch (arch.arch) { + return ARM_SHARED_REGION_SIZE; + case CPU_TYPE_I386: + case CPU_TYPE_X86_64: + return 12; // 4KB + case CPU_TYPE_ARM: + case CPU_TYPE_ARM64: + return 14; // 16KB + default: + terminate("unsupported arch 0x%08X", arch.arch); + } +} + +uint64_t sharedRegionRegionSize(ArchPair arch) { + switch ( arch.arch ) { + case CPU_TYPE_I386: + return SHARED_REGION_SIZE_I386; + case CPU_TYPE_X86_64: + return SHARED_REGION_SIZE_X86_64; + case CPU_TYPE_ARM: + return ARM_SHARED_REGION_SIZE; + case CPU_TYPE_ARM64: + return ARM64_SHARED_REGION_SIZE; + default: + terminate("unsupported arch 0x%08X", arch.arch); + } +} + +static const std::tuple gArchitectures[] = { + {"i386", nullptr, ArchPair( CPU_TYPE_I386, CPU_SUBTYPE_I386_ALL )}, + {"x86_64", nullptr, ArchPair( CPU_TYPE_X86_64, CPU_SUBTYPE_X86_64_ALL )}, + {"x86_64h", "x86_64", ArchPair( CPU_TYPE_X86_64, CPU_SUBTYPE_X86_64_H )}, + {"armv4t", nullptr, ArchPair( CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V4T )}, + {"armv5", nullptr, ArchPair( CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V5TEJ )}, + {"armv6", nullptr, ArchPair( CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V6 )}, + {"armv7", nullptr, ArchPair( CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7 )}, + {"armv7f", nullptr, ArchPair( CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7F )}, + {"armv7k", nullptr, ArchPair( CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7K )}, + {"armv7s", "armv7", ArchPair( CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7S )}, + {"arm64", nullptr, ArchPair( CPU_TYPE_ARM64, CPU_SUBTYPE_ARM64_ALL )}, +}; + +ArchPair archForString(const std::string& archStr) { + for (auto& a : gArchitectures) { + if ( std::get<0>( a ) == archStr ) return std::get<2>( a ); + } + terminate("unknown architecture %s", archStr.c_str()); +} + +std::string stringForArch(ArchPair arch, bool allowUnknown) { + for (auto& a : gArchitectures) { + // FIXME LIB64 is set on some binaries and not other + if ( std::get<2>( a ).arch == arch.arch && std::get<2>( a ).subtype == ( arch.subtype & ~CPU_SUBTYPE_MASK ) ) + return std::get<0>( a ); + } + + auto unknownString = + "unrecognized cpu type " + std::to_string(arch.arch) + + " subtype " + std::to_string(arch.subtype); + if (allowUnknown) return unknownString; + else terminate("%s", unknownString.c_str()); +} + +std::string fallbackArchStringForArchString( const std::string& archStr ) { + for ( auto& a : gArchitectures ) { + if ( std::get<0>( a ) == archStr && std::get<1>( a ) != nullptr ) { + return std::get<1>( a ); + } + } + + return ""; +} + +SharedCache::SharedCache(Manifest& manifest, + const std::string& configuration, const std::string& architecture) : + _manifest(manifest), _arch(archForString(architecture)), + _archManifest(manifest.configurations.find(configuration)->second.architectures.find(architecture)->second), _buffer(nullptr), + _fileSize(0), _vmSize(0), _aliasCount(0), _slideInfoFileOffset(0), _slideInfoBufferSize(0) { + auto maxCacheVMSize = sharedRegionRegionSize(_arch); + + for ( auto& includedDylib : _archManifest.results.dylibs ) { + if (includedDylib.second.included) { + //assert(manifest.dylibs.count(includedDylib.first) > 0); + //assert(manifest.dylibs.find(includedDylib.first)->second.proxies.count(architecture) > 0); + MachOProxy* proxy = _manifest.dylibProxy( includedDylib.first, architecture ); + assert(proxy != nullptr); + _dylibs.push_back(proxy); + } + } + + // error out instead of crash if cache has no dylibs + if ( _dylibs.size() < 30 ) // FIXME: plist should specify required vs optional dylibs + terminate("missing required minimum set of dylibs"); + + for (auto &dylib : _dylibs) { + _segmentMap[dylib].reserve(dylib->segments.size()); + for (const auto& seg : dylib->segments) + _segmentMap[dylib].push_back(&seg); + _aliasCount += dylib->installNameAliases.size(); + } + + sortDylibs(_manifest.dylibOrderFile); + if ( !_manifest.dirtyDataOrderFile.empty() ) + loadDirtyDataOrderFile(_manifest.dirtyDataOrderFile); + + assignSegmentAddresses(); + if ( _vmSize > maxCacheVMSize ) + verboseLog("%s cache overflow. %lluMB (max %lluMB)", archName().c_str(), _vmSize/1024/1024, maxCacheVMSize/1024/1024); + while (_vmSize > maxCacheVMSize) { + auto evictedDylib = manifest.removeLargestLeafDylib( configuration, architecture ); + _dylibs.erase( std::remove( _dylibs.begin(), _dylibs.end(), evictedDylib ), _dylibs.end() ); + _aliasCount -= evictedDylib->installNameAliases.size(); + assignSegmentAddresses(); + } +} + +// There is an order file specifying the order in which dylibs are laid out in +// general, as well as an order file specifying the order in which __DATA_DIRTY +// segments are laid out in particular. +// +// The syntax is one dylib (install name) per line. Blank lines are ignored. +// Comments start with the # character. + +static std::unordered_map loadOrderFile(const std::string& orderFile) { + std::unordered_map order; + + std::ifstream myfile(orderFile); + if ( myfile.is_open() ) { + uint32_t count = 0; + std::string line; + while ( std::getline(myfile, line) ) { + size_t pos = line.find('#'); + if ( pos != std::string::npos ) + line.resize(pos); + while ( !line.empty() && isspace(line.back()) ) { + line.pop_back(); + } + if ( !line.empty() ) + order[line] = count++; + } + myfile.close(); + } else { + warning("could not load orderfile '%s'", orderFile.c_str()); + } + + return order; +} + +void SharedCache::loadDirtyDataOrderFile(const std::string& dirtyDataOrderFile) { + _dataDirtySegsOrder = loadOrderFile(dirtyDataOrderFile); +} + +void SharedCache::sortDylibs(const std::string& dylibOrderFile) { + std::unordered_map dylibOrder; + if ( !dylibOrderFile.empty() ) + dylibOrder = loadOrderFile(dylibOrderFile); + + std::sort(_dylibs.begin(), _dylibs.end(), [&](const MachOProxy* a, + const MachOProxy* b) { + const std::string& pathA = a->installName; + const std::string& pathB = b->installName; + + const auto& orderA = dylibOrder.find(pathA); + const auto& orderB = dylibOrder.find(pathB); + bool foundA = (orderA != dylibOrder.end()); + bool foundB = (orderB != dylibOrder.end()); + + // Order all dylibs specified in the order file first, in the order specified in + // the file, followed by any other dylibs in lexicographic order. + if ( foundA && foundB ) + return orderA->second < orderB->second; + else if ( foundA ) + return true; + else if ( foundB ) + return false; + else + return pathA < pathB; + }); +} + +void SharedCache::buildUnoptimizedCache(void) { + _buffer = std::shared_ptr(calloc(_fileSize, 1), free); + writeCacheHeader(); + writeCacheSegments(); + rebaseAll(); + bindAll(); +} + +template +void SharedCache::buildForDevelopment(const std::string& cachePath) { + typedef typename P::E E; + std::vector emptyBranchPoolOffsets; + buildUnoptimizedCache(); + optimizeObjC(false/*not production*/); + if (_manifest.platform == "osx") { + optimizeLinkedit(false, false, emptyBranchPoolOffsets); + } else { + optimizeLinkedit(true, false, emptyBranchPoolOffsets); + } + writeSlideInfoV2(); + + dyldCacheHeader* header = (dyldCacheHeader*)_buffer.get(); + header->set_cacheType(kDyldSharedCacheTypeDevelopment); + recomputeCacheUUID(); + + // Calculate the VMSize of the resulting cache + uint64_t endAddr = 0; + + forEachRegion([&] (void* content, uint64_t vmAddr, uint64_t size, uint32_t permissions) { + if (vmAddr+size > endAddr) + endAddr = vmAddr+size; + }); + _vmSize = endAddr - sharedRegionStartExecutableAddress(_arch); + + if (_manifest.platform == "osx") { + appendCodeSignature("release"); + } else { + appendCodeSignature("development"); + } +} + +template +void SharedCache::buildForProduction(const std::string& cachePath) { + typedef typename P::E E; + buildUnoptimizedCache(); + optimizeObjC(true/*production*/); + uint64_t cacheStartAddress = sharedRegionStartExecutableAddress(_arch); + + dyldCacheHeader* header = (dyldCacheHeader*)_buffer.get(); + header->set_cacheType(kDyldSharedCacheTypeProduction); + + // build vector of branch pool addresss + std::vector branchPoolStartAddrs; + std::vector branchPoolOffsets; + const uint64_t* p = (uint64_t*)((uint8_t*)_buffer.get() + header->branchPoolsOffset()); + for (int i=0; i < header->branchPoolsCount(); ++i) { + uint64_t poolAddr = LittleEndian::get64(p[i]); + branchPoolStartAddrs.push_back(poolAddr); + branchPoolOffsets.push_back(poolAddr - cacheStartAddress); + } + + bypassStubs(branchPoolStartAddrs); + optimizeLinkedit(true, true, branchPoolOffsets); + writeSlideInfoV2(); + + recomputeCacheUUID(); + + // Calculate the VMSize of the resulting cache + uint64_t endAddr = 0; + + forEachRegion([&] (void* content, uint64_t vmAddr, uint64_t size, uint32_t permissions) { + if (vmAddr+size > endAddr) + endAddr = vmAddr+size; + }); + _vmSize = endAddr - cacheStartAddress; + + appendCodeSignature("release"); +} + +bool SharedCache::writeCacheMapFile(const std::string& mapPath) { + FILE* fmap = ::fopen(mapPath.c_str(), "w"); + if ( fmap == NULL ) + return false; + + std::vector regionStartAddresses; + std::vector regionSizes; + std::vector regionFileOffsets; + + forEachRegion([&] (void* content, uint64_t vmAddr, uint64_t size, uint32_t permissions) { + regionStartAddresses.push_back(vmAddr); + regionSizes.push_back(size); + regionFileOffsets.push_back((uint8_t*)content - (uint8_t*)_buffer.get()); + const char* prot = "RW"; + if ( permissions == (VM_PROT_EXECUTE|VM_PROT_READ) ) + prot = "EX"; + else if ( permissions == VM_PROT_READ ) + prot = "RO"; + if ( size > 1024*1024 ) + fprintf(fmap, "mapping %s %4lluMB 0x%0llX -> 0x%0llX\n", prot, size/(1024*1024), vmAddr, vmAddr+size); + else + fprintf(fmap, "mapping %s %4lluKB 0x%0llX -> 0x%0llX\n", prot, size/1024, vmAddr, vmAddr+size); + }); + + // TODO: add linkedit breakdown + fprintf(fmap, "\n\n"); + + std::unordered_set seenHeaders; + forEachImage([&](const void* machHeader, const char* installName, time_t mtime, + ino_t inode, const std::vector& segments) { + if ( !seenHeaders.count(machHeader) ) { + seenHeaders.insert(machHeader); + + fprintf(fmap, "%s\n", installName); + for (const MachOProxy::Segment& seg : segments) { + uint64_t vmAddr = 0; + for (int i=0; i < regionSizes.size(); ++i) { + if ( (seg.fileOffset >= regionFileOffsets[i]) && (seg.fileOffset < (regionFileOffsets[i]+regionSizes[i])) ) { + vmAddr = regionStartAddresses[i] + seg.fileOffset - regionFileOffsets[i]; + } + } + fprintf(fmap, "\t%16s 0x%08llX -> 0x%08llX\n", seg.name.c_str(), vmAddr, vmAddr+seg.size); + } + } + }); + + + ::fclose(fmap); + return true; +} + +template +std::vector getSegments(const void* cacheBuffer, const void* machHeader) +{ + std::vector result; + macho_header

* mh = (macho_header

*)machHeader; + const uint32_t cmd_count = mh->ncmds(); + const macho_load_command

* cmd = (macho_load_command

*)((uint8_t*)mh + sizeof(macho_header

)); + for (uint32_t i = 0; i < cmd_count; ++i) { + if ( cmd->cmd() != macho_segment_command

::CMD ) + continue; + macho_segment_command

* segCmd = (macho_segment_command

*)cmd; + MachOProxy::Segment seg; + seg.name = segCmd->segname(); + seg.size = segCmd->vmsize(); + seg.diskSize = (uint32_t)segCmd->filesize(); + seg.fileOffset = (uint32_t)segCmd->fileoff(); + seg.protection = segCmd->initprot(); + // HACK until lldb fixed in + if ( (seg.fileOffset == 0) && (strcmp(segCmd->segname(), "__TEXT") == 0) ) + seg.fileOffset = (uint32_t)((char*)machHeader - (char*)cacheBuffer); + if ( segCmd->nsects() > 0 ) { + seg.p2align = 0; + const macho_section

* const sectionsStart = (macho_section

*)((uint8_t*)segCmd + sizeof(macho_segment_command

)); + const macho_section

* const sectionsEnd = §ionsStart[segCmd->nsects()]; + for (const macho_section

* sect=sectionsStart; sect < sectionsEnd; ++sect) { + if ( sect->align() > seg.p2align ) + seg.p2align = sect->align(); + } + } + else { + seg.p2align = 12; + } + result.push_back(seg); + cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); + } + return result; +} + +template +void SharedCache::forEachImage(DylibHandler handler) +{ +#if NEW_CACHE_FILE_FORMAT + terminate("forEachImage() not implemented"); +#else + typedef typename P::E E; + const dyldCacheHeader* header = (dyldCacheHeader*)_buffer.get(); + const dyldCacheImageInfo* dylibs = (dyldCacheImageInfo*)((char*)_buffer.get() + header->imagesOffset()); + const dyldCacheFileMapping* mappings = (dyldCacheFileMapping*)((char*)_buffer.get() + header->mappingOffset()); + if ( mappings[0].file_offset() != 0 ) + terminate("malformed cache file"); + uint64_t firstImageOffset = 0; + uint64_t firstRegionAddress = mappings[0].address(); + const void* cacheEnd = (char*)_buffer.get() + _fileSize; + if ( (const void*)&dylibs[header->imagesCount()] > cacheEnd ) + return; + for (uint32_t i=0; i < header->imagesCount(); ++i) { + const char* dylibPath = (char*)_buffer.get() + dylibs[i].pathFileOffset(); + if ( dylibPath > cacheEnd ) + return; + uint64_t offset = dylibs[i].address() - firstRegionAddress; + if ( firstImageOffset == 0 ) + firstImageOffset = offset; + // skip over aliases + if ( dylibs[i].pathFileOffset() < firstImageOffset) + continue; + const void* mh = (char*)_buffer.get() + offset; + time_t inode = dylibs[i].inode(); + ino_t modTime = dylibs[i].modTime(); + handler(mh, dylibPath, modTime, inode, getSegments

(_buffer.get(), mh)); + } +#endif +} + + +template +void SharedCache::recomputeCacheUUID(void) +{ + uint8_t* uuidLoc = nullptr; +#if NEW_CACHE_FILE_FORMAT + const macho_header

* mh = (macho_header

*)cacheBuffer; + const macho_load_command

* const cmds = (macho_load_command

*)((uint8_t*)mh + sizeof(macho_header

)); + const uint32_t cmd_count = mh->ncmds(); + const macho_load_command

* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + if ( cmd->cmd() == LC_UUID ) { + const macho_uuid_command

* uuidCmd = (macho_uuid_command

*)cmd; + uuidLoc = const_cast(uuidCmd->uuid()); + break; + } + cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); + } +#else + dyldCacheHeader

* header = (dyldCacheHeader

*)_buffer.get(); + uuidLoc = const_cast(header->uuid()); +#endif + + // Clear existing UUID, then MD5 whole cache buffer. + bzero(uuidLoc, 16); + CC_MD5(_buffer.get(), (unsigned)_fileSize, uuidLoc); + // uuids should conform to RFC 4122 UUID version 4 & UUID version 5 formats + uuidLoc[6] = ( uuidLoc[6] & 0x0F ) | ( 3 << 4 ); + uuidLoc[8] = ( uuidLoc[8] & 0x3F ) | 0x80; +} + +template +void SharedCache::setLinkeditsMappingEndFileOffset(uint64_t newFileSize) +{ +#if NEW_CACHE_FILE_FORMAT + terminate("setLinkeditsMappingEndFileOffset() not implemented"); +#else + typedef typename P::E E; + dyldCacheHeader* header = (dyldCacheHeader*)_buffer.get(); + dyldCacheFileMapping* mappings = (dyldCacheFileMapping*)((char*)_buffer.get() + header->mappingOffset()); + uint64_t newReadOnlySize = newFileSize - mappings[2].file_offset(); + mappings[2].set_size(newReadOnlySize); + header->set_codeSignatureOffset(newFileSize); + _readOnlyRegion.size = (newReadOnlySize); +#endif +} + +template +void SharedCache::setUnmappedLocalsRange(uint64_t localSymbolsOffset, uint32_t unmappedSize) +{ +#if NEW_CACHE_FILE_FORMAT + terminate("setUnmappedLocalsRange() not implemented"); +#else + typedef typename P::E E; + dyldCacheHeader* header = (dyldCacheHeader*)_buffer.get(); + header->set_localSymbolsOffset(localSymbolsOffset); + header->set_localSymbolsSize(unmappedSize); + // move start of code signature to new end of file + header->set_codeSignatureOffset(localSymbolsOffset+unmappedSize); +#endif +} + +template +void SharedCache::setAcceleratorInfoRange(uint64_t accelInfoAddr, uint32_t accelInfoSize) +{ +#if NEW_CACHE_FILE_FORMAT + terminate("setUnmappedLocalsRange() not implemented"); +#else + typedef typename P::E E; + dyldCacheHeader* header = (dyldCacheHeader*)_buffer.get(); + header->set_accelerateInfoAddr(accelInfoAddr); + header->set_accelerateInfoSize(accelInfoSize); +#endif +} + +template +void SharedCache::forEachRegion(RegionHandler handler) +{ +#if NEW_CACHE_FILE_FORMAT + const macho_header

* mh = (macho_header

*)cacheBuffer; + const macho_load_command

* const cmds = (macho_load_command

*)((uint8_t*)mh + sizeof(macho_header

)); + const uint32_t cmd_count = mh->ncmds(); + const macho_load_command

* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + if ( cmd->cmd() == macho_segment_command

::CMD ) { + const macho_segment_command

* segCmd = (macho_segment_command

*)cmd; + handler((char*)cacheBuffer + segCmd->fileoff(), segCmd->vmaddr(), segCmd->vmsize(), segCmd->initprot()); + } + cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); + } +#else + typedef typename P::E E; + const dyldCacheHeader* header = (dyldCacheHeader*)_buffer.get(); + const dyldCacheFileMapping* mappings = (dyldCacheFileMapping*)((char*)_buffer.get() + header->mappingOffset()); + const dyldCacheFileMapping* mappingsEnd = &mappings[header->mappingCount()]; + for (const dyldCacheFileMapping* m=mappings; m < mappingsEnd; ++m) { + handler((char*)_buffer.get() + m->file_offset(), m->address(), m->size(), m->init_prot()); + } +#endif +} + +std::shared_ptr SharedCache::buffer(void) const { + return _buffer; +} + +std::string SharedCache::archName() { + return stringForArch(_arch); +} + +void SharedCache::assignSegmentAddresses() +{ + _branchPoolStarts.clear(); + uint64_t addr = sharedRegionStartExecutableAddress(_arch); + + // assign TEXT segment addresses + _textRegion.address = addr; + _textRegion.fileOffset = 0; + _textRegion.prot = VM_PROT_READ | VM_PROT_EXECUTE; +#if NEW_CACHE_FILE_FORMAT + addr += 0x4000; // header +#else + addr += 0x28000; // header +#endif + uint64_t brPoolTextSize = branchPoolTextSize(_arch); + uint64_t brPoolLinkEditSize = branchPoolLinkEditSize(_arch); + uint64_t brRearch = branchReach(_arch); + uint64_t lastPoolAddress = addr; + for (auto& dylib : _dylibs) { + for (auto& seg : _segmentMap[dylib]) { + if ( seg.base->protection != (VM_PROT_READ | VM_PROT_EXECUTE) ) + continue; + // Insert branch island pools every 128MB for arm64 + if ( (brPoolTextSize != 0) && ((addr + seg.base->size - lastPoolAddress) > brRearch) ) { + _branchPoolStarts.push_back(addr); + //verboseLog("adding branch pool at 0x%lX\n", addr); + lastPoolAddress = addr; + addr += brPoolTextSize; + } + // Keep __TEXT segments 4K or more aligned + uint64_t startAlignPad = align(addr, std::max(seg.base->p2align, (uint8_t)12)) - addr; + addr += startAlignPad; + seg.address = addr; + seg.cacheFileOffset = addr - _textRegion.address + _textRegion.fileOffset; + seg.cacheSegSize = align(seg.base->sizeOfSections, 12); + addr += align(seg.base->sizeOfSections, 12); + } + } + // align TEXT region end + uint64_t endTextAddress = align(addr, sharedRegionRegionAlignment(_arch)); + _textRegion.size = endTextAddress - _textRegion.address; + + std::unordered_map dataDirtySegPaths; + + // co-locate similar __DATA* segments + std::vector dataSegs; + std::vector dataConstSegs; + std::vector dataDirtySegs; + for (auto& dylib : _dylibs) { + for (auto& seg : _segmentMap[dylib]) { + if ( seg.base->protection == (VM_PROT_READ | VM_PROT_WRITE) ) { + if ( seg.base->name == "__DATA_CONST" ) { + dataConstSegs.push_back(&seg); + } + else if ( seg.base->name == "__DATA_DIRTY" ) { + dataDirtySegs.push_back(&seg); + dataDirtySegPaths[&seg] = dylib->installName; + } + else { + dataSegs.push_back(&seg); + } + } + } + } + + // assign __DATA* addresses + addr = sharedRegionStartWriteableAddress(_arch, endTextAddress); + _dataRegion.address = addr; + _dataRegion.fileOffset = _textRegion.fileOffset + _textRegion.size; + _dataRegion.prot = VM_PROT_READ | VM_PROT_WRITE; + + // layout all __DATA_CONST segments + for (SegmentInfo* seg : dataConstSegs) { + // Keep __DATA_CONST segments 4K or more aligned + uint64_t startAlignPad = align(addr, std::max(seg->base->p2align, (uint8_t)12)) - addr; + addr += startAlignPad; + seg->address = addr; + seg->cacheFileOffset = addr - _dataRegion.address + _dataRegion.fileOffset; + seg->cacheSegSize = seg->base->sizeOfSections; + addr += seg->base->sizeOfSections; + } + + // layout all __DATA segments + for (SegmentInfo* seg : dataSegs) { + // Keep __DATA segments 4K or more aligned + uint64_t startAlignPad = align(addr, std::max(seg->base->p2align, (uint8_t)12)) - addr; + addr += startAlignPad; + seg->address = addr; + seg->cacheFileOffset = addr - _dataRegion.address + _dataRegion.fileOffset; + seg->cacheSegSize = seg->base->sizeOfSections; + addr += seg->base->sizeOfSections; + } + + // layout all __DATA_DIRTY segments + addr = align(addr, 12); + std::sort(dataDirtySegs.begin(), dataDirtySegs.end(), [&](const SegmentInfo *a, const SegmentInfo *b) { + const std::string& pathA = dataDirtySegPaths[a]; + const std::string& pathB = dataDirtySegPaths[b]; + + const auto& orderA = _dataDirtySegsOrder.find(pathA); + const auto& orderB = _dataDirtySegsOrder.find(pathB); + bool foundA = (orderA != _dataDirtySegsOrder.end()); + bool foundB = (orderB != _dataDirtySegsOrder.end()); + + // Order all __DATA_DIRTY segments specified in the order file first, in + // the order specified in the file, followed by any other __DATA_DIRTY + // segments in lexicographic order. + if ( foundA && foundB ) + return orderA->second < orderB->second; + else if ( foundA ) + return true; + else if ( foundB ) + return false; + else + return pathA < pathB; + }); + for (SegmentInfo* seg : dataDirtySegs) { + // Pack __DATA_DIRTY segments + uint64_t startAlignPad = align(addr, seg->base->p2align) - addr; + addr += startAlignPad; + seg->address = addr; + seg->cacheFileOffset = addr - _dataRegion.address + _dataRegion.fileOffset; + seg->cacheSegSize = seg->base->sizeOfSections; + addr += seg->base->sizeOfSections; + } + + // align DATA region end + uint64_t endDataAddress = align(addr, sharedRegionRegionAlignment(_arch)); + _dataRegion.size = endDataAddress - _dataRegion.address; + + // start read-only region + addr = sharedRegionStartReadOnlyAddress(_arch, endDataAddress, endTextAddress); + _readOnlyRegion.address = addr; + _readOnlyRegion.fileOffset = _dataRegion.fileOffset + _dataRegion.size; + _readOnlyRegion.prot = VM_PROT_READ; + + // reserve space for kernel ASLR slide info at start of r/o region + _slideInfoBufferSize = align((_dataRegion.size/4096) * 130, 12); // bitmap entry + toc entry + _slideInfoFileOffset = _readOnlyRegion.fileOffset; + addr += _slideInfoBufferSize; + + // layout all read-only (but not LINKEDIT) segments + for (auto& dylib : _dylibs) { + for (auto& seg : _segmentMap[dylib]) { + if ( seg.base->protection != VM_PROT_READ ) + continue; + if ( seg.base->name == "__LINKEDIT" ) + continue; + // Keep segments 4K or more aligned + addr = align(addr, std::min(seg.base->p2align, (uint8_t)12)); + seg.address = addr; + seg.cacheFileOffset = addr - _readOnlyRegion.address + _readOnlyRegion.fileOffset;; + seg.cacheSegSize = seg.base->size; + addr += seg.base->size; + //verboseLog("read-only offset=0x%08X, for path=%s\n", seg.cacheFileOffset, ex->proxy->installName.c_str()); + } + } + // layout all LINKEDIT segments (after other read-only segments) + for (auto& dylib : _dylibs) { + for (auto& seg : _segmentMap[dylib]) { + if ( seg.base->protection != VM_PROT_READ ) + continue; + if ( seg.base->name != "__LINKEDIT" ) + continue; + // Keep LINKEDIT segments 4K aligned + addr = align(addr, 12); + seg.address = addr; + seg.cacheFileOffset = addr - _readOnlyRegion.address + _readOnlyRegion.fileOffset;; + seg.cacheSegSize = seg.base->diskSize; + addr += seg.base->size; + //verboseLog("linkedit offset=0x%08X, for path=%s\n", seg.cacheFileOffset, ex->proxy->installName.c_str()); + } + } + // add room for branch pool linkedits + _branchPoolsLinkEditStartAddr = addr; + addr += (_branchPoolStarts.size() * brPoolLinkEditSize); + + // align r/o region end + uint64_t endReadOnlyAddress = align(addr, sharedRegionRegionAlignment(_arch)); + _readOnlyRegion.size = endReadOnlyAddress - _readOnlyRegion.address; + _fileSize = _readOnlyRegion.fileOffset + _readOnlyRegion.size; + // assume LINKEDIT optimzation reduces LINKEDITs to %40 of original size + _vmSize = _readOnlyRegion.address+(_readOnlyRegion.size * 2/5) - _textRegion.address; +} + +uint64_t SharedCache::pathHash(const char* path) +{ + uint64_t sum = 0; + for (const char* s=path; *s != '\0'; ++s) + sum += sum*4 + *s; + return sum; +} + + + +void SharedCache::findDylibAndSegment(const void* contentPtr, std::string& dylibName, std::string& segName) +{ + uint64_t fileOffset = (uint8_t*)contentPtr - (uint8_t*)_buffer.get(); + for (const auto& entry : _segmentMap ) { + const MachOProxy* dylib = entry.first; + for (const SegmentInfo& segInfo : entry.second) { + //fprintf(stderr, " cacheFileOffset=0x%08llX, end=0x%08llX\n", segInfo.cacheFileOffset, segInfo.cacheFileOffset+segInfo.base->size); + if ( (segInfo.cacheFileOffset <= fileOffset) && (fileOffset < segInfo.cacheFileOffset+segInfo.base->size) ) { + dylibName = dylib->installName; + segName = segInfo.base->name; + return; + } + } + } + dylibName = "???"; + segName = "???"; +} + + +template +bool SharedCache::makeRebaseChain(uint8_t* pageContent, uint16_t lastLocationOffset, uint16_t offset, const dyldCacheSlideInfo2* info) +{ + typedef typename P::uint_t pint_t; + + const pint_t deltaMask = (pint_t)(info->delta_mask()); + const pint_t valueMask = ~deltaMask; + const pint_t valueAdd = (pint_t)(info->value_add()); + const unsigned deltaShift = __builtin_ctzll(deltaMask) - 2; + const uint32_t maxDelta = (uint32_t)(deltaMask >> deltaShift); + + pint_t* lastLoc = (pint_t*)&pageContent[lastLocationOffset+0]; + pint_t lastValue = (pint_t)P::getP(*lastLoc); + if ( (lastValue - valueAdd) & deltaMask ) { + std::string dylibName; + std::string segName; + findDylibAndSegment((void*)pageContent, dylibName, segName); + terminate("rebase pointer does not point within cache. lastOffset=0x%04X, seg=%s, dylib=%s\n", + lastLocationOffset, segName.c_str(), dylibName.c_str()); + } + if ( offset <= (lastLocationOffset+maxDelta) ) { + // previous location in range, make link from it + // encode this location into last value + pint_t delta = offset - lastLocationOffset; + pint_t newLastValue = ((lastValue - valueAdd) & valueMask) | (delta << deltaShift); + //warning(" add chain: delta = %d, lastOffset=0x%03X, offset=0x%03X, org value=0x%08lX, new value=0x%08lX", + // offset - lastLocationOffset, lastLocationOffset, offset, (long)lastValue, (long)newLastValue); + P::setP(*lastLoc, newLastValue); + return true; + } + //warning(" too big delta = %d, lastOffset=0x%03X, offset=0x%03X", offset - lastLocationOffset, lastLocationOffset, offset); + + // distance between rebase locations is too far + // see if we can make a chain from non-rebase locations + uint16_t nonRebaseLocationOffsets[1024]; + unsigned nrIndex = 0; + for (uint16_t i = lastLocationOffset; i < offset-maxDelta; ) { + nonRebaseLocationOffsets[nrIndex] = 0; + for (int j=maxDelta; j > 0; j -= 4) { + pint_t value = (pint_t)P::getP(*(pint_t*)&pageContent[i+j]); + if ( value == 0 ) { + // Steal values of 0 to be used in the rebase chain + nonRebaseLocationOffsets[nrIndex] = i+j; + break; + } + } + if ( nonRebaseLocationOffsets[nrIndex] == 0 ) { + lastValue = (pint_t)P::getP(*lastLoc); + pint_t newValue = ((lastValue - valueAdd) & valueMask); + //warning(" no way to make non-rebase delta chain, terminate off=0x%03X, old value=0x%08lX, new value=0x%08lX", lastLocationOffset, (long)value, (long)newValue); + P::setP(*lastLoc, newValue); + return false; + } + i = nonRebaseLocationOffsets[nrIndex]; + ++nrIndex; + } + + // we can make chain. go back and add each non-rebase location to chain + uint16_t prevOffset = lastLocationOffset; + pint_t* prevLoc = (pint_t*)&pageContent[prevOffset]; + for (int n=0; n < nrIndex; ++n) { + uint16_t nOffset = nonRebaseLocationOffsets[n]; + assert(nOffset != 0); + pint_t* nLoc = (pint_t*)&pageContent[nOffset]; + uint32_t delta2 = nOffset - prevOffset; + pint_t value = (pint_t)P::getP(*prevLoc); + pint_t newValue; + if ( value == 0 ) + newValue = (delta2 << deltaShift); + else + newValue = ((value - valueAdd) & valueMask) | (delta2 << deltaShift); + //warning(" non-rebase delta = %d, to off=0x%03X, old value=0x%08lX, new value=0x%08lX", delta2, nOffset, (long)value, (long)newValue); + P::setP(*prevLoc, newValue); + prevOffset = nOffset; + prevLoc = nLoc; + } + uint32_t delta3 = offset - prevOffset; + pint_t value = (pint_t)P::getP(*prevLoc); + pint_t newValue; + if ( value == 0 ) + newValue = (delta3 << deltaShift); + else + newValue = ((value - valueAdd) & valueMask) | (delta3 << deltaShift); + //warning(" non-rebase delta = %d, to off=0x%03X, old value=0x%08lX, new value=0x%08lX", delta3, offset, (long)value, (long)newValue); + P::setP(*prevLoc, newValue); + + return true; +} + + +template +void SharedCache::addPageStarts(uint8_t* pageContent, const bool bitmap[], const dyldCacheSlideInfo2* info, + std::vector& pageStarts, std::vector& pageExtras) +{ + typedef typename P::uint_t pint_t; + + const pint_t deltaMask = (pint_t)(info->delta_mask()); + const pint_t valueMask = ~deltaMask; + const uint32_t pageSize = info->page_size(); + const pint_t valueAdd = (pint_t)(info->value_add()); + + uint16_t startValue = DYLD_CACHE_SLIDE_PAGE_ATTR_NO_REBASE; + uint16_t lastLocationOffset = 0xFFFF; + for(int i=0; i < pageSize/4; ++i) { + unsigned offset = i*4; + if ( bitmap[i] ) { + if ( startValue == DYLD_CACHE_SLIDE_PAGE_ATTR_NO_REBASE ) { + // found first rebase location in page + startValue = i; + } + else if ( !makeRebaseChain

(pageContent, lastLocationOffset, offset, info) ) { + // can't record all rebasings in one chain + if ( (startValue & DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA) == 0 ) { + // switch page_start to "extras" which is a list of chain starts + unsigned indexInExtras = (unsigned)pageExtras.size(); + if ( indexInExtras > 0x3FFF ) + terminate("rebase overflow in page extras"); + pageExtras.push_back(startValue); + startValue = indexInExtras | DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA; + } + pageExtras.push_back(i); + } + lastLocationOffset = offset; + } + } + if ( lastLocationOffset != 0xFFFF ) { + // mark end of chain + pint_t* lastLoc = (pint_t*)&pageContent[lastLocationOffset]; + pint_t lastValue = (pint_t)P::getP(*lastLoc); + pint_t newValue = ((lastValue - valueAdd) & valueMask); + P::setP(*lastLoc, newValue); + } + if ( startValue & DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA ) { + // add end bit to extras + pageExtras.back() |= DYLD_CACHE_SLIDE_PAGE_ATTR_END; + } + pageStarts.push_back(startValue); +} + +template +void SharedCache::writeSlideInfoV2(uint64_t deltaMask, uint64_t valueAdd) +{ + // i386 cache does not support sliding because stubs use absolute addressing (text relocs) + if (_arch.arch == CPU_TYPE_I386 ) { + dyldCacheHeader* header = (dyldCacheHeader*)_buffer.get(); + header->set_slideInfoSize(0); + return; + } + + typedef typename P::E E; + const uint32_t pageSize = 4096; + + // build one 1024/4096 bool bitmap per page (4KB/16KB) of DATA + uint8_t* const dataStart = (uint8_t*)_buffer.get() + _dataRegion.fileOffset; + uint8_t* const dataEnd = dataStart + _dataRegion.size; + unsigned pageCount = (unsigned)(_dataRegion.size+pageSize-1)/pageSize; + const long bitmapSize = pageCount*(pageSize/4)*sizeof(bool); + bool* bitmap = (bool*)calloc(bitmapSize, 1); + for (void* p : _pointersForASLR) { + if ( (p < dataStart) || ( p > dataEnd) ) + terminate("DATA pointer for sliding, out of range\n"); + long byteOffset = (long)((uint8_t*)p - dataStart); + if ( (byteOffset % 4) != 0 ) + terminate("pointer not 4-byte aligned in DATA offset 0x%08lX\n", byteOffset); + long boolIndex = byteOffset / 4; + // work around by ignoring pointers to be slid that are NULL on disk + if ( *(typename P::uint_t*)p == 0 ) { + std::string dylibName; + std::string segName; + findDylibAndSegment(p, dylibName, segName); + warning("NULL pointer asked to be slid in %s of %s", segName.c_str(), dylibName.c_str()); + continue; + } + bitmap[boolIndex] = true; + } + + // fill in fixed info + dyldCacheSlideInfo2* info = (dyldCacheSlideInfo2*)((uint8_t*)_buffer.get() + _slideInfoFileOffset); + info->set_version(2); + info->set_page_size(pageSize); + info->set_delta_mask(deltaMask); + info->set_value_add(valueAdd); + + // set page starts and extras for each page + std::vector pageStarts; + std::vector pageExtras; + pageStarts.reserve(pageCount); + uint8_t* pageContent = dataStart;; + const bool* bitmapForPage = bitmap; + for (unsigned i=0; i < pageCount; ++i) { + //warning("page[%d]", i); + addPageStarts

(pageContent, bitmapForPage, info, pageStarts, pageExtras); + pageContent += pageSize; + bitmapForPage += (sizeof(bool)*(pageSize/4)); + } + free((void*)bitmap); + + // fill in computed info + info->set_page_starts_offset(sizeof(dyldCacheSlideInfo2)); + info->set_page_starts_count((unsigned)pageStarts.size()); + info->set_page_extras_offset((unsigned)(sizeof(dyldCacheSlideInfo2)+pageStarts.size()*sizeof(uint16_t))); + info->set_page_extras_count((unsigned)pageExtras.size()); + for (unsigned i=0; i < pageStarts.size(); ++i) + info->set_page_starts(i, pageStarts[i]); + for (unsigned i=0; i < pageExtras.size(); ++i) + info->set_page_extras(i, pageExtras[i]); + //warning("pageCount=%u, page_starts_count=%lu, page_extras_count=%lu", pageCount, pageStarts.size(), pageExtras.size()); + _slideInfoBufferSize = align(info->page_extras_offset() + pageExtras.size()*sizeof(uint16_t), 12); + +#if NEW_CACHE_FILE_FORMAT + +#else + unsigned long slideInfoPageSize = align(_slideInfoBufferSize, sharedRegionRegionAlignment(_arch)); + dyldCacheHeader* header = (dyldCacheHeader*)_buffer.get(); + header->set_slideInfoSize(slideInfoPageSize); +#endif +} + +void SharedCache::writeSlideInfoV2(void) +{ + switch (_arch.arch) { + case CPU_TYPE_ARM: + // linked list based slide info needs high 3 bits of pointer, won't work with > 512MB of pointable content + if ( (_textRegion.size + _dataRegion.size) > 512*1024*1024 ) { + warning("cache TEXT+DATA > 512MB, using larger slide info format"); + writeSlideInfo(); + } + else { + writeSlideInfoV2>(0xE0000000, ARM_SHARED_REGION_START); + } + break; + case CPU_TYPE_I386: + writeSlideInfoV2>(0xE0000000, 0x90000000); + break; + case CPU_TYPE_X86_64: + writeSlideInfoV2>(0xFFFF000000000000, 0); + break; + case CPU_TYPE_ARM64: + writeSlideInfoV2>(0x00FFFF0000000000, 0); + break; + default: + warning("unsupported arch 0x%08X", _arch.arch); + return; + } +} + + + +template +void SharedCache::writeSlideInfo(void) +{ + // i386 cache does not support sliding because stubs use absolute addressing (text relocs) + if (_arch.arch == CPU_TYPE_I386 ) { + dyldCacheHeader* header = (dyldCacheHeader*)_buffer.get(); + header->set_slideInfoSize(0); + return; + } + + // build one 128-byte bitmap per page (4096) of DATA + uint8_t* const dataStart = (uint8_t*)_buffer.get() + _dataRegion.fileOffset; + uint8_t* const dataEnd = dataStart + _dataRegion.size; + const long bitmapSize = (dataEnd - dataStart)/(4*8); + uint8_t* bitmap = (uint8_t*)calloc(bitmapSize, 1); + for (void* p : _pointersForASLR) { + if ( (p < dataStart) || ( p > dataEnd) ) + terminate("DATA pointer for sliding, out of range\n"); + long offset = (long)((uint8_t*)p - dataStart); + if ( (offset % 4) != 0 ) + terminate("pointer not 4-byte aligned in DATA offset 0x%08lX\n", offset); + long byteIndex = offset / (4*8); + long bitInByte = (offset % 32) >> 2; + bitmap[byteIndex] |= (1 << bitInByte); + } + + // allocate worst case size block of all slide info + const unsigned entry_size = 4096/(8*4); // 8 bits per byte, possible pointer every 4 bytes. + const unsigned toc_count = (unsigned)bitmapSize/entry_size; + dyldCacheSlideInfo* slideInfo = (dyldCacheSlideInfo*)((uint8_t*)_buffer.get() + _slideInfoFileOffset); + slideInfo->set_version(1); + slideInfo->set_toc_offset(sizeof(dyldCacheSlideInfo)); + slideInfo->set_toc_count(toc_count); + slideInfo->set_entries_offset((slideInfo->toc_offset()+2*toc_count+127)&(-128)); + slideInfo->set_entries_count(0); + slideInfo->set_entries_size(entry_size); + // append each unique entry + const dyldCacheSlideInfoEntry* bitmapAsEntries = (dyldCacheSlideInfoEntry*)bitmap; + dyldCacheSlideInfoEntry* const entriesInSlidInfo = (dyldCacheSlideInfoEntry*)((char*)slideInfo+slideInfo->entries_offset()); + int entry_count = 0; + for (int i=0; i < toc_count; ++i) { + const dyldCacheSlideInfoEntry* thisEntry = &bitmapAsEntries[i]; + // see if it is same as one already added + bool found = false; + for (int j=0; j < entry_count; ++j) { + if ( memcmp(thisEntry, &entriesInSlidInfo[j], entry_size) == 0 ) { + slideInfo->set_toc(i, j); + found = true; + break; + } + } + if ( !found ) { + // append to end + memcpy(&entriesInSlidInfo[entry_count], thisEntry, entry_size); + slideInfo->set_toc(i, entry_count++); + } + } + slideInfo->set_entries_count(entry_count); + ::free((void*)bitmap); + +#if NEW_CACHE_FILE_FORMAT + +#else + unsigned long slideInfoPageSize = align(slideInfo->entries_offset() + entry_count*entry_size, sharedRegionRegionAlignment(_arch)); + dyldCacheHeader* header = (dyldCacheHeader*)_buffer.get(); + header->set_slideInfoSize(slideInfoPageSize); +#endif +} + +template +void SharedCache::writeCacheHeader(void) +{ +#if NEW_CACHE_FILE_FORMAT + macho_header

* mh = (macho_header

*)cacheBuffer; + mh->set_magic((sizeof(typename P::uint_t) == 8) ? MH_MAGIC_64 : MH_MAGIC); + mh->set_cputype(arch.arch); + mh->set_cpusubtype(arch.subtype); + mh->set_filetype(MH_DYLIB); + mh->set_ncmds(0); + mh->set_sizeofcmds(0); + mh->set_flags(0); + + uint8_t* cmd = (uint8_t*)cacheBuffer + sizeof(macho_header

); + + // write LC_SEGMENT for each region + macho_segment_command

* rxSegCmd = (macho_segment_command

*)cmd; + rxSegCmd->set_cmd(macho_segment_command

::CMD); + rxSegCmd->set_cmdsize(sizeof(macho_segment_command

)); + rxSegCmd->set_segname("R.X"); + rxSegCmd->set_vmaddr(_textRegion.address); + rxSegCmd->set_vmsize(_textRegion.size); + rxSegCmd->set_fileoff(_textRegion.fileOffset); + rxSegCmd->set_filesize(_textRegion.size); + rxSegCmd->set_maxprot(VM_PROT_READ | VM_PROT_EXECUTE); + rxSegCmd->set_initprot(VM_PROT_READ | VM_PROT_EXECUTE); + rxSegCmd->set_nsects(0); + rxSegCmd->set_flags(0); + mh->set_ncmds(mh->ncmds()+1); + mh->set_sizeofcmds(mh->sizeofcmds()+rxSegCmd->cmdsize()); + cmd += rxSegCmd->cmdsize(); + + macho_segment_command

* rwSegCmd = (macho_segment_command

*)cmd; + rwSegCmd->set_cmd(macho_segment_command

::CMD); + rwSegCmd->set_cmdsize(sizeof(macho_segment_command

)); + rwSegCmd->set_segname("RW."); + rwSegCmd->set_vmaddr(_dataRegion.address); + rwSegCmd->set_vmsize(_dataRegion.size); + rwSegCmd->set_fileoff(_dataRegion.fileOffset); + rwSegCmd->set_filesize(_dataRegion.size); + rwSegCmd->set_maxprot(VM_PROT_READ | VM_PROT_WRITE); + rwSegCmd->set_initprot(VM_PROT_READ | VM_PROT_WRITE); + rwSegCmd->set_nsects(0); + rwSegCmd->set_flags(0); + mh->set_ncmds(mh->ncmds()+1); + mh->set_sizeofcmds(mh->sizeofcmds()+rwSegCmd->cmdsize()); + cmd += rwSegCmd->cmdsize(); + + macho_segment_command

* roSegCmd = (macho_segment_command

*)cmd; + roSegCmd->set_cmd(macho_segment_command

::CMD); + roSegCmd->set_cmdsize(sizeof(macho_segment_command

)); + roSegCmd->set_segname("R.."); + roSegCmd->set_vmaddr(_readOnlyRegion.address); + roSegCmd->set_vmsize(_readOnlyRegion.size); + roSegCmd->set_fileoff(_readOnlyRegion.fileOffset); + roSegCmd->set_filesize(_readOnlyRegion.size); + roSegCmd->set_maxprot(VM_PROT_READ); + roSegCmd->set_initprot(VM_PROT_READ); + roSegCmd->set_nsects(0); + roSegCmd->set_flags(0); + mh->set_ncmds(mh->ncmds()+1); + mh->set_sizeofcmds(mh->sizeofcmds()+roSegCmd->cmdsize()); + cmd += roSegCmd->cmdsize(); + + // Add LC_ID_DYLIB + macho_dylib_command

* dylibIdCmd = (macho_dylib_command

*)cmd; + const char* installName = "/System/Library/Frameworks/OS.framework/OS"; // FIXME + uint32_t sz = (uint32_t)align(sizeof(macho_dylib_command

) + strlen(installName) + 1, 3); + dylibIdCmd->set_cmd(LC_ID_DYLIB); + dylibIdCmd->set_cmdsize(sz); + dylibIdCmd->set_name_offset(); + dylibIdCmd->set_timestamp(1); + dylibIdCmd->set_current_version(0x10000); + dylibIdCmd->set_compatibility_version(0x10000); + strcpy((char*)&cmd[sizeof(macho_dylib_command

)], installName); + mh->set_ncmds(mh->ncmds()+1); + mh->set_sizeofcmds(mh->sizeofcmds()+sz); + cmd += dylibIdCmd->cmdsize(); + + // Add LC_UUID + macho_uuid_command

* uuidCmd = (macho_uuid_command

*)cmd; + uint8_t zeros[16]; + bzero(zeros, 16); + uuidCmd->set_cmd(LC_UUID); + uuidCmd->set_cmdsize(sizeof(macho_uuid_command

)); + uuidCmd->set_uuid(zeros); + cmd += uuidCmd->cmdsize(); + + // Build dylib trie + std::vector dylibTrieEntires; + int pathLengths = 0; + for (Extra* ex : _sortedDylibs) { + mach_o::trie::Entry entry; + entry.name = ex->proxy->installName.c_str(); + entry.address = ex->segments[0].address; + entry.flags = 0; + entry.other = 0; + entry.importName = NULL; + dylibTrieEntires.push_back(entry); + pathLengths += (strlen(entry.name) + 1); + for (const std::string& alias : ex->proxy->installNameAliases) { + mach_o::trie::Entry aliasEntry; + aliasEntry.name = alias.c_str(); + aliasEntry.address = ex->segments[0].address; + aliasEntry.flags = 0; + aliasEntry.other = 0; + aliasEntry.importName = NULL; + dylibTrieEntires.push_back(aliasEntry); + pathLengths += (strlen(aliasEntry.name) + 1); + } + } + std::vector dylibTrieBytes; + dylibTrieBytes.reserve(4096); + mach_o::trie::makeTrie(dylibTrieEntires, dylibTrieBytes); + fprintf(stderr, "dylib trie size = %lu bytes, for %lu entries, pathLength=%d\n", dylibTrieBytes.size(), dylibTrieEntires.size(), pathLengths); + + + + // Build SPI trie (optimized cache only) + + + + // add LC_CODE_SIGNATURE + macho_linkedit_data_command

* codeSigCmd = (macho_linkedit_data_command

*)cmd; + codeSigCmd->set_cmd(LC_CODE_SIGNATURE); + codeSigCmd->set_cmdsize(sizeof(macho_linkedit_data_command

)); + codeSigCmd->set_dataoff((uint32_t)(_readOnlyRegion.fileOffset + _readOnlyRegion.size)); + codeSigCmd->set_datasize(0); // FIXME + mh->set_ncmds(mh->ncmds()+1); + mh->set_sizeofcmds(mh->sizeofcmds()+codeSigCmd->cmdsize()); + cmd += codeSigCmd->cmdsize(); + +#else + typedef typename P::E E; + // fill in header + uint8_t* buffer = (uint8_t*)_buffer.get(); + dyldCacheHeader* header = (dyldCacheHeader*)_buffer.get();; + // "dyld_v1" + spaces + archName(), with enough spaces to pad to 15 bytes + std::string magic = "dyld_v1"; + magic.append(15 - magic.length() - archName().length(), ' '); + magic.append(archName()); + assert(magic.length() == 15); + header->set_magic(magic.c_str()); + header->set_mappingOffset(sizeof(dyldCacheHeader)); + header->set_mappingCount(3); + header->set_imagesOffset((uint32_t)(header->mappingOffset() + 3*sizeof(dyldCacheFileMapping) + sizeof(uint64_t)*_branchPoolStarts.size())); + header->set_imagesCount((uint32_t)_dylibs.size() + _aliasCount); + header->set_dyldBaseAddress(0); + header->set_codeSignatureOffset(_fileSize); + header->set_codeSignatureSize(0); + header->set_slideInfoOffset(_slideInfoFileOffset); + header->set_slideInfoSize(_slideInfoBufferSize); + header->set_localSymbolsOffset(0); + header->set_localSymbolsSize(0); + header->set_cacheType(kDyldSharedCacheTypeDevelopment); + header->set_accelerateInfoAddr(0); + header->set_accelerateInfoSize(0); + static const uint8_t zero_uuid[16] = { 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0 }; + header->set_uuid(zero_uuid); // overwritten later by recomputeCacheUUID() + header->set_branchPoolsOffset(header->mappingOffset() + 3*sizeof(dyldCacheFileMapping)); + header->set_branchPoolsCount((uint32_t)_branchPoolStarts.size()); + header->set_imagesTextOffset(0); + header->set_imagesTextCount(_dylibs.size()); + + // fill in mappings + dyldCacheFileMapping* mappings = (dyldCacheFileMapping*)&buffer[header->mappingOffset()]; + mappings[0].set_address(_textRegion.address); + mappings[0].set_size(_textRegion.size); + mappings[0].set_file_offset(_textRegion.fileOffset); + mappings[0].set_max_prot(_textRegion.prot); + mappings[0].set_init_prot(_textRegion.prot); + + mappings[1].set_address(_dataRegion.address); + mappings[1].set_size(_dataRegion.size); + mappings[1].set_file_offset(_dataRegion.fileOffset); + mappings[1].set_max_prot(_dataRegion.prot); + mappings[1].set_init_prot(_dataRegion.prot); + + mappings[2].set_address(_readOnlyRegion.address); + mappings[2].set_size(_readOnlyRegion.size); + mappings[2].set_file_offset(_readOnlyRegion.fileOffset); + mappings[2].set_max_prot(_readOnlyRegion.prot); + mappings[2].set_init_prot(_readOnlyRegion.prot); + + // fill in branch pool addresses + uint64_t* p = (uint64_t*)&buffer[header->branchPoolsOffset()]; + for (uint64_t pool : _branchPoolStarts) { + E::set64(*p, pool); + ++p; + } + + // fill in image table + dyldCacheImageInfo* images = (dyldCacheImageInfo*)&buffer[header->imagesOffset()]; + for (auto& dylib : _dylibs) { + auto textSeg = _segmentMap[dylib][0]; + images->set_address(textSeg.address); + if (_manifest.platform == "osx") { + images->set_modTime(dylib->lastModTime); + images->set_inode(dylib->inode); + } else { + images->set_modTime(0); + images->set_inode(pathHash(dylib->installName.c_str())); + } + images->set_pathFileOffset((uint32_t)textSeg.cacheFileOffset + dylib->installNameOffsetInTEXT); + ++images; + } + // append aliases image records and strings + uint32_t offset = header->imagesOffset() + header->imagesCount()*sizeof(dyld_cache_image_info); + for (auto &dylib : _dylibs) { + if (!dylib->installNameAliases.empty()) { + for (const std::string& alias : dylib->installNameAliases) { + images->set_address(_segmentMap[dylib][0].address); + if (_manifest.platform == "osx") { + images->set_modTime(dylib->lastModTime); + images->set_inode(dylib->inode); + } else { + images->set_modTime(0); + images->set_inode(pathHash(alias.c_str())); + } + images->set_pathFileOffset(offset); + ::strcpy((char*)&buffer[offset], alias.c_str()); + offset += alias.size() + 1; + ++images; + } + } + } + + // calculate start of text image array and trailing string pool + offset = (offset + 15) & (-16); + header->set_imagesTextOffset(offset); + dyldCacheImageTextInfo* textImages = (dyldCacheImageTextInfo*)&buffer[header->imagesTextOffset()]; + uint32_t stringOffset = offset + (uint32_t)(sizeof(dyldCacheImageTextInfo) * _dylibs.size()); + + // write text image array and image names pool at same time + for (auto& dylib : _dylibs) { + textImages->set_uuid(dylib->uuid); + textImages->set_loadAddress(_segmentMap[dylib][0].address); + textImages->set_textSegmentSize((uint32_t)dylib->segments[0].size); + textImages->set_pathOffset(stringOffset); + ::strcpy((char*)&buffer[stringOffset], dylib->installName.c_str()); + stringOffset += dylib->installName.size()+1; + ++textImages; + } + + assert(stringOffset < 0x28000); +#endif +} + +void SharedCache::rebase(MachOProxy* dylib) +{ + std::vector segNewStartAddresses; + std::vector segCacheFileOffsets; + std::vector segCacheFileSizes; + for (auto& seg : _segmentMap[dylib]) { + segNewStartAddresses.push_back(seg.address); + segCacheFileOffsets.push_back(seg.cacheFileOffset); + segCacheFileSizes.push_back(seg.cacheSegSize); + } + adjustImageForNewSegmentLocations(segNewStartAddresses, segCacheFileOffsets, segCacheFileSizes, _pointersForASLR); +} + + +void SharedCache::rebaseAll(void) +{ + for (auto& dylib : _dylibs) + rebase(dylib); +} + +void SharedCache::bindAll(void) +{ + std::unordered_map dylibPathToMachHeader; + for (auto& dylib : _dylibs) { + void* mh = (uint8_t*)_buffer.get() + _segmentMap[dylib][0].cacheFileOffset; + dylibPathToMachHeader[dylib->installName] = mh; + for (const std::string& path : dylib->installNameAliases) { + if (path != dylib->installName) { + dylibPathToMachHeader[path] = mh; + } + } + } + + bindAllImagesInCache(dylibPathToMachHeader, _pointersForASLR); +} + +void SharedCache::writeCacheSegments(void) +{ + uint8_t* cacheBytes = (uint8_t*)_buffer.get(); + for (auto& dylib : _dylibs) { + struct stat stat_buf; + const uint8_t* srcDylib; + bool rootless; + + std::tie(srcDylib, stat_buf, rootless) = fileCache.cacheLoad(dylib->path); + for (auto& seg : _segmentMap[dylib]) { + uint32_t segFileOffset = dylib->fatFileOffset + seg.base->fileOffset; + uint64_t copySize = std::min(seg.cacheSegSize, (uint64_t)seg.base->diskSize); + verboseLog("copy segment %12s (0x%08llX bytes) to %p (logical addr 0x%llX) for %s", seg.base->name.c_str(), copySize, &cacheBytes[seg.cacheFileOffset], seg.address, dylib->installName.c_str()); + ::memcpy(&cacheBytes[seg.cacheFileOffset], &srcDylib[segFileOffset], copySize); + } + } +} + + + +void SharedCache::appendCodeSignature(const std::string& suffix) +{ + // select which codesigning hash + uint8_t dscHashType = CS_HASHTYPE_SHA1; + uint8_t dscHashSize = CS_HASH_SIZE_SHA1; + uint32_t dscDigestFormat = kCCDigestSHA1; + if ( _manifest.platform == "osx" ) { + dscHashType = CS_HASHTYPE_SHA256; + dscHashSize = CS_HASH_SIZE_SHA256; + dscDigestFormat = kCCDigestSHA256; + } + + std::string cacheIdentifier = "com.apple.dyld.cache." + archName() + "." + suffix; + // get pointers into shared cache buffer + size_t inBbufferSize = _fileSize; + const uint8_t* inBuffer = (uint8_t*)_buffer.get(); + uint8_t* csBuffer = (uint8_t*)_buffer.get()+inBbufferSize; + + // layout code signature contents + size_t idSize = cacheIdentifier.size()+1; // +1 for terminating 0 + uint32_t slotCount = (uint32_t)((inBbufferSize + CS_PAGE_SIZE - 1) / CS_PAGE_SIZE); + uint32_t xSlotCount = CSSLOT_REQUIREMENTS; + size_t scatOffset = sizeof(CS_CodeDirectory); + size_t scatSize = 4*sizeof(CS_Scatter); // only 3 used?? + size_t idOffset = scatOffset+scatSize; + size_t hashOffset = idOffset+idSize + dscHashSize*xSlotCount; + size_t cdSize = hashOffset + (slotCount * dscHashSize); + size_t reqsSize = 12; + size_t cmsSize = sizeof(CS_Blob); + size_t cdOffset = sizeof(CS_SuperBlob) + 3*sizeof(CS_BlobIndex); + size_t reqsOffset = cdOffset + cdSize; + size_t cmsOffset = reqsOffset + reqsSize; + size_t sbSize = cmsOffset + cmsSize; + size_t sigSize = align(sbSize, 14); // keep whole cache 16KB aligned + + // create overall code signature which is a superblob + CS_SuperBlob* sb = reinterpret_cast(csBuffer); + sb->magic = htonl(CSMAGIC_EMBEDDED_SIGNATURE); + sb->length = htonl(sbSize); + sb->count = htonl(3); + sb->index[0].type = htonl(CSSLOT_CODEDIRECTORY); + sb->index[0].offset = htonl(cdOffset); + sb->index[1].type = htonl(CSSLOT_REQUIREMENTS); + sb->index[1].offset = htonl(reqsOffset); + sb->index[2].type = htonl(CSSLOT_CMS_SIGNATURE); + sb->index[2].offset = htonl(cmsOffset); + + // initialize fixed fields of Code Directory + CS_CodeDirectory* cd = (CS_CodeDirectory*)(((char*)sb)+cdOffset); + cd->magic = htonl(CSMAGIC_CODEDIRECTORY); + cd->length = htonl(cdSize); + cd->version = htonl(0x20100); + cd->flags = htonl(kSecCodeSignatureAdhoc); + cd->hashOffset = htonl(hashOffset); + cd->identOffset = htonl(idOffset); + cd->nSpecialSlots = htonl(xSlotCount); + cd->nCodeSlots = htonl(slotCount); + cd->codeLimit = htonl(inBbufferSize); + cd->hashSize = dscHashSize; + cd->hashType = dscHashType; + cd->platform = 0; // not platform binary + cd->pageSize = __builtin_ctz(CS_PAGE_SIZE); // log2(CS_PAGE_SIZE); + cd->spare2 = 0; // unused (must be zero) + cd->scatterOffset = htonl(scatOffset); + + // initialize dynamic fields of Code Directory + strcpy((char*)cd + idOffset, cacheIdentifier.c_str()); + + // add scatter info + CS_Scatter* scatter = reinterpret_cast((char*)cd+scatOffset); + scatter[0].count = htonl(_textRegion.size/CS_PAGE_SIZE); + scatter[0].base = htonl(_textRegion.fileOffset/CS_PAGE_SIZE); + scatter[0].targetOffset = htonll(_textRegion.address); + scatter[0].spare = 0; + scatter[1].count = htonl(_dataRegion.size/CS_PAGE_SIZE); + scatter[1].base = htonl(_dataRegion.fileOffset/CS_PAGE_SIZE); + scatter[1].targetOffset = htonll(_dataRegion.address); + scatter[1].spare = 0; + scatter[2].count = htonl(_readOnlyRegion.size/CS_PAGE_SIZE); + scatter[2].base = htonl(_readOnlyRegion.fileOffset/CS_PAGE_SIZE); + scatter[2].targetOffset = htonll(_readOnlyRegion.address); + scatter[2].spare = 0; + + // fill in empty requirements + CS_RequirementsBlob* reqs = (CS_RequirementsBlob*)(((char*)sb)+reqsOffset); + reqs->magic = htonl(CSMAGIC_REQUIREMENTS); + reqs->length = htonl(sizeof(CS_RequirementsBlob)); + reqs->data = 0; + + // fill in empty CMS blob for ad-hoc signing + CS_Blob* cms = (CS_Blob*)(((char*)sb)+cmsOffset); + cms->magic = htonl(CSMAGIC_BLOBWRAPPER); + cms->length = htonl(sizeof(CS_Blob)); + + // add special slot hashes + uint8_t* hashSlot = (uint8_t*)cd + hashOffset; + uint8_t* reqsHashSlot = &hashSlot[-CSSLOT_REQUIREMENTS*dscHashSize]; + CCDigest(dscDigestFormat, (uint8_t*)reqs, sizeof(CS_RequirementsBlob), reqsHashSlot); + + // alter header of cache to record size and location of code signature + // do this *before* hashing each page + dyldCacheHeader* header = (dyldCacheHeader*)inBuffer; + header->set_codeSignatureOffset(inBbufferSize); + header->set_codeSignatureSize(sigSize); + + // compute hashes + const uint8_t* code = inBuffer; + for (uint32_t i=0; i < slotCount; ++i) { + CCDigest(dscDigestFormat, code, CS_PAGE_SIZE, hashSlot); + hashSlot += dscHashSize; + code += CS_PAGE_SIZE; + } + + // hash of entire code directory (cdHash) uses same has hash as each page + uint8_t fullCdHash[dscHashSize]; + CCDigest(dscDigestFormat, (const uint8_t*)cd, cdSize, fullCdHash); + // Note: cdHash is defined as first 20 bytes of hash + memcpy(_cdHash, fullCdHash, 20); + + // increase file size to include newly append code signature + _fileSize += sigSize; +} + +std::string SharedCache::cdHashString() +{ + char buff[48]; + for (int i = 0; i < sizeof(_cdHash); ++i) + sprintf(&buff[2*i], "%2.2x", _cdHash[i]); + return buff; +} + + +#pragma mark - +#pragma mark Template dispatchers + +#define TEMPLATE_DISPATCHER_BODY(method,...) \ + switch( _arch.arch ) { \ + case CPU_TYPE_ARM: \ + case CPU_TYPE_I386: \ + method>(__VA_ARGS__); \ + break; \ + case CPU_TYPE_X86_64: \ + case CPU_TYPE_ARM64: \ + method>(__VA_ARGS__); \ + break; \ + default: \ + terminate("unsupported arch 0x%08X", _arch.arch); \ + } + +void SharedCache::writeCacheHeader() { + TEMPLATE_DISPATCHER_BODY(writeCacheHeader) +}; + +void SharedCache::buildForDevelopment(const std::string& cachePath) { + TEMPLATE_DISPATCHER_BODY(buildForDevelopment, cachePath) +}; + +void SharedCache::buildForProduction(const std::string& cachePath) { + TEMPLATE_DISPATCHER_BODY(buildForProduction, cachePath) +}; + +void SharedCache::setLinkeditsMappingEndFileOffset(uint64_t newFileSize) { + TEMPLATE_DISPATCHER_BODY(setLinkeditsMappingEndFileOffset, newFileSize) +} + +void SharedCache::setUnmappedLocalsRange(uint64_t localSymbolsOffset, uint32_t unmappedSize) { + TEMPLATE_DISPATCHER_BODY(setUnmappedLocalsRange, localSymbolsOffset, unmappedSize) +} + +void SharedCache::setAcceleratorInfoRange(uint64_t accelInfoAddr, uint32_t accelInfoSize) { + TEMPLATE_DISPATCHER_BODY(setAcceleratorInfoRange, accelInfoAddr, accelInfoSize) +} + +void SharedCache::recomputeCacheUUID(void) { + TEMPLATE_DISPATCHER_BODY(recomputeCacheUUID) +} + +void SharedCache::forEachImage(DylibHandler handler) { + TEMPLATE_DISPATCHER_BODY(forEachImage, handler) +} + +void SharedCache::forEachRegion(RegionHandler handler) { + TEMPLATE_DISPATCHER_BODY(forEachRegion, handler) +} diff --git a/src/dyld-apple/interlinked-dylibs/Trie.hpp b/src/dyld-apple/interlinked-dylibs/Trie.hpp new file mode 100644 index 000000000..d77a21a5c --- /dev/null +++ b/src/dyld-apple/interlinked-dylibs/Trie.hpp @@ -0,0 +1,498 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2008-2010 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@ + */ + +/* + //This is the exposed iterface for the Trie template + //TODO: add erase methods + //TODO: re-enable iterators + + template + struct Trie { + struct Entry + { + std::string name; + V info; + + Entry(void) {} + Entry(const std::string& N, V I) : name(N), info(I) {} + }; + + struct const_iterator : std::iterator; + + const_iterator begin() const; + const_iterator end() const; + + Trie(void); + Trie(const uint8_t* start, const uint8_t* end); + Trie(const std::vector& entries); + + void emit(std::vector& output); + */ + + +#ifndef __TRIE__ +#define __TRIE__ +#define TRIE_DEBUG (0) + +#include +#include +#include +#include +#include +#include + +#include "MachOFileAbstraction.hpp" + +#if __cplusplus <= 201103L +namespace std { + template + std::unique_ptr make_unique(Args&&... args) + { + return std::unique_ptr(new T(std::forward(args)...)); + } +} +#endif + +namespace TrieUtils { + +static void append_uleb128(uint64_t value, std::vector& out) { + uint8_t byte; + do { + byte = value & 0x7F; + value &= ~0x7F; + if ( value != 0 ) + byte |= 0x80; + out.push_back(byte); + value = value >> 7; + } while( byte >= 0x80 ); +} + +static void append_string(std::string str, std::vector& out) { + for(char& c : str) + out.push_back(c); + out.push_back('\0'); +} + +static unsigned int uleb128_size(uint64_t value) { + uint32_t result = 0; + do { + value = value >> 7; + ++result; + } while ( value != 0 ); + return result; +} + +static +inline bool parse_uleb128(const uint8_t*& p, const uint8_t* end, uint64_t& result) { + result = 0; + int bit = 0; + do { + if (p == end) + return false; // malformed uleb128 extends beyond input + uint64_t slice = *p & 0x7f; + + if (bit >= 64 || slice << bit >> bit != slice) + return false; // malformed + else { + result |= (slice << bit); + bit += 7; + } + } + while (*p++ & 0x80); + return true; +} +}; + +template +struct Trie { + uint32_t count; + uint32_t nodeCount; + + struct Entry + { + std::string name; + V info; + + Entry(void) {} + Entry(const std::string& N, V I) : name(N), info(I) {} + }; + + Trie(const std::vector& entries) : count(0), nodeCount(1) { + // make nodes for all exported symbols + for (auto& entry : entries) { + addEntry(entry); + } + } + + void emit(std::vector& output) { + // create vector of nodes + std::vector orderedNodes; + orderedNodes.reserve(nodeCount); + orderTrie(&root, orderedNodes); + + // assign each node in the vector an offset in the trie stream, iterating until all uleb128 sizes have stabilized + bool more; + do { + uint32_t offset = 0; + more = false; + for (auto& node : orderedNodes) { + if (node->updateOffset(offset)) { + more = true; + } + } + } while ( more ); + + // create trie stream + for (auto& node : orderedNodes) { + node->appendToStream(output); + } + } + + static + inline bool parseTrie(const uint8_t* start, const uint8_t* end, std::vector& output) + { + // empty trie has no entries + if ( start == end ) + return false; + char cummulativeString[32768]; + std::vector entries; + if ( !processExportNode(start, start, end, cummulativeString, 0, entries) ) + return false; + // to preserve tie layout order, sort by node offset + std::sort(entries.begin(), entries.end()); + // copy to output + output.reserve(entries.size()); + for (auto& entryWithOffset : entries) { + output.push_back(entryWithOffset.entry); + } + return true; + } + +private: + struct Node + { + //This needs to be a map to unsure deterministic ordering of tries. + std::map > fChildren; + bool fIsTerminal; + uint32_t fTrieOffset; + V fInfo; + + Node(void) : fIsTerminal(false), fTrieOffset(0) {} + Node(V v) :fInfo(v), fIsTerminal(true), fTrieOffset(0) {} + Node(Node&&) = default; + + // byte for terminal node size in bytes, or 0x00 if not terminal node + // teminal node (uleb128 flags, uleb128 addr [uleb128 other]) + // byte for child node count + // each child: zero terminated substring, uleb128 node offset + bool updateOffset(uint32_t& offset) { + uint32_t nodeSize = 1; // length of export info when no export info + if ( fIsTerminal ) { + nodeSize = fInfo.encodedSize(); + // do have export info, overall node size so far is uleb128 of export info + export info + nodeSize += TrieUtils::uleb128_size(nodeSize); + } + // add children + ++nodeSize; // byte for count of chidren + + for (auto &edge : fChildren) { + nodeSize += edge.first.length() + 1 + TrieUtils::uleb128_size(edge.second->fTrieOffset); + } + + bool result = (fTrieOffset != offset); + fTrieOffset = offset; + //fprintf(stderr, "updateOffset %p %05d %s\n", this, fTrieOffset, fCummulativeString); + offset += nodeSize; + // return true if fTrieOffset was changed + return result; + } + + void appendToStream(std::vector& out) { + if ( fIsTerminal ) { + fInfo.appendToStream(out); + } + else { + // no export info uleb128 of zero is one byte of zero + out.push_back(0); + } + // write number of children + out.push_back(fChildren.size()); + // write each child + for (auto &edge : fChildren) { + TrieUtils::append_string(edge.first, out); + TrieUtils::append_uleb128(edge.second->fTrieOffset, out); + } + } + }; + + Node root; + + struct EntryWithOffset + { + uintptr_t nodeOffset; + Entry entry; + + bool operator<(const EntryWithOffset& other) const { return ( nodeOffset < other.nodeOffset ); } + }; + + void addEntry(const std::string& fullStr, std::string::const_iterator start, V v) { + Node *currentNode = &root; + bool done = false; + + while (!done && !currentNode->fChildren.empty() ) { + done = true; + + for (auto &entry : currentNode->fChildren) { + auto res = std::mismatch(entry.first.begin(), entry.first.end(), start); + + if (res.first == entry.first.end()) { + //Matched a full edge, go down it + done = false; + currentNode = entry.second.get(); + start = res.second; + break; + } else if (res.first != entry.first.begin()) { + // found a common substring, splice in new node + // was A -> C, now A -> B -> C + + //Build the new strings + std::string abEdgeStr(entry.first.begin(), res.first); + std::string bcEdgeStr(res.first, entry.first.end()); + + //Copy out the exist node and delete it from the currentNode + std::unique_ptr nodeC; + std::swap(nodeC, entry.second); + currentNode->fChildren.erase(entry.first); + + //Build the new node and insert it + std::unique_ptr nodeB = std::make_unique(); + Node *newNode = nodeB.get(); + + nodeB->fChildren.insert(std::make_pair(bcEdgeStr, std::move(nodeC))); + currentNode->fChildren.insert(std::make_pair(abEdgeStr, std::move(nodeB))); + + currentNode = newNode; + start = res.second; + ++nodeCount; + break; + } + } + } + + // no commonality with any existing child, make a new edge that is this whole string + std::string edgeStr(start, fullStr.end()); + v.willInsertAs(fullStr); + + if (edgeStr.empty()) { + currentNode->fIsTerminal = true; + currentNode->fInfo = v; + } else { + currentNode->fChildren.emplace(edgeStr, std::make_unique(v)); + ++nodeCount; + } + ++count; + } + + void addEntry(Entry entry) { + addEntry(entry.name, entry.name.begin(), entry.info); + } + +#if TRIE_DEBUG + void printTrie(Node& node, std::string cummulativeString) { + if (node.fTerminal) { + printf("%s: \n", cummulativeString.c_str()); + } + for (auto &edge : node.fChildren) { + printTrie(*edge.second, cummulativeString+edge.first); + } + } + +public: + void printTrie(void) { + printTrie(root, ""); + } +private: +#endif + + static inline bool processExportNode(const uint8_t* const start, const uint8_t* p, const uint8_t* const end, + char* cummulativeString, int curStrOffset, + std::vector& output) + { + if ( p >= end ) + return false; + uint64_t terminalSize; + if ( !TrieUtils::parse_uleb128(p, end, terminalSize) ) + return false; + const uint8_t* children = p + terminalSize; + if ( children >= end ) + return false; + if ( terminalSize != 0 ) { + EntryWithOffset e; + e.nodeOffset = p-start; + e.entry.name = cummulativeString; + e.entry.info.loadData(p,end); + output.push_back(e); + } + const uint8_t childrenCount = *children++; + const uint8_t* s = children; + for (uint8_t i=0; i < childrenCount; ++i) { + int edgeStrLen = 0; + while (*s != '\0') { + cummulativeString[curStrOffset+edgeStrLen] = *s++; + ++edgeStrLen; + } + cummulativeString[curStrOffset+edgeStrLen] = *s++; + uint64_t childNodeOffet; + if ( !TrieUtils::parse_uleb128(s, end, childNodeOffet) ) + return false; + if ( !processExportNode(start, start+childNodeOffet, end, cummulativeString, curStrOffset+edgeStrLen, output) ) + return false; + } + return true; + } + + void orderTrie(Node* node, std::vector& orderedNodes) { + orderedNodes.push_back(node); + for (auto &edge : node->fChildren) { + orderTrie(edge.second.get(), orderedNodes); + } + } +}; // struct Trie + +struct ExportInfo { + uint64_t address; + uint64_t flags; + uint64_t other; + std::string importName; + + ExportInfo() : other(0), address(0) {} + + uint32_t encodedSize(void) { + uint32_t size = 0; + if ( flags & EXPORT_SYMBOL_FLAGS_REEXPORT ) { + size = TrieUtils::uleb128_size(flags) + TrieUtils::uleb128_size(other); // ordinal + if ( !importName.empty() ) + size += importName.length(); + ++size; // trailing zero in imported name + } + else { + size = TrieUtils::uleb128_size(flags) + TrieUtils::uleb128_size(address); + if ( flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER ) + size += TrieUtils::uleb128_size(other); + } + return size; + } + + void appendToStream(std::vector& out) { + if ( flags & EXPORT_SYMBOL_FLAGS_REEXPORT ) { + if ( !importName.empty() ) { + // nodes with re-export info: size, flags, ordinal, string + uint32_t nodeSize = (uint32_t)(TrieUtils::uleb128_size(flags) + TrieUtils::uleb128_size(other) + importName.length() + 1); + out.push_back(nodeSize); + TrieUtils::append_uleb128(flags, out); + TrieUtils::append_uleb128(other, out); + TrieUtils::append_string(importName, out); + } + else { + // nodes with re-export info: size, flags, ordinal, empty-string + uint32_t nodeSize = TrieUtils::uleb128_size(flags) + TrieUtils::uleb128_size(other) + 1; + out.push_back(nodeSize); + TrieUtils::append_uleb128(flags, out); + TrieUtils::append_uleb128(other, out); + out.push_back(0); + } + } + else if ( flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER ) { + // nodes with export info: size, flags, address, other + uint32_t nodeSize = TrieUtils::uleb128_size(flags) + TrieUtils::uleb128_size(address) + TrieUtils::uleb128_size(other); + out.push_back(nodeSize); + TrieUtils::append_uleb128(flags, out); + TrieUtils::append_uleb128(address, out); + TrieUtils::append_uleb128(other, out); + } + else { + // nodes with export info: size, flags, address + uint32_t nodeSize = TrieUtils::uleb128_size(flags) + TrieUtils::uleb128_size(address); + out.push_back(nodeSize); + TrieUtils::append_uleb128(flags, out); + TrieUtils::append_uleb128(address, out); + } + } + + void loadData(const uint8_t* p, const uint8_t* const end) { + TrieUtils::parse_uleb128(p, end, flags); + if ( flags & EXPORT_SYMBOL_FLAGS_REEXPORT ) { + TrieUtils::parse_uleb128(p, end, other); // dylib ordinal + importName = (char*)p; + } + else { + TrieUtils::parse_uleb128(p, end, address); + if ( flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER ) + TrieUtils::parse_uleb128(p, end, other); + } + } + + void willInsertAs(const std::string& name) { + // Symbols re-exported under the same name do not need an explict import name, delete it to save space + if ((name == importName) ) { + importName = ""; + } + } +}; + +typedef Trie ExportInfoTrie; + + +// Used by accelerator tables in dyld shared cache +struct DylibIndex { + uint32_t index; + + DylibIndex() : index(0) {} + DylibIndex(uint32_t i) : index(i) {} + + uint32_t encodedSize(void) { + return TrieUtils::uleb128_size(index); + } + + void appendToStream(std::vector& out) { + uint32_t nodeSize = TrieUtils::uleb128_size(index); + out.push_back(nodeSize); + TrieUtils::append_uleb128(index, out); + } + + void loadData(const uint8_t* p, const uint8_t* const end) { + uint64_t temp; + TrieUtils::parse_uleb128(p, end, temp); + index = (uint32_t)temp; + } + + void willInsertAs(const std::string& name) { + } +}; +typedef Trie DylibIndexTrie; + + +#endif // __TRIE__ + + diff --git a/src/dyld-apple/interlinked-dylibs/dyld_shared_cache_builder.mm b/src/dyld-apple/interlinked-dylibs/dyld_shared_cache_builder.mm new file mode 100644 index 000000000..075a5ed7b --- /dev/null +++ b/src/dyld-apple/interlinked-dylibs/dyld_shared_cache_builder.mm @@ -0,0 +1,387 @@ +/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- + * + * Copyright (c) 2016 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@ + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include + +#include "Manifest.h" +#include "MultiCacheBuilder.h" + +#include "mega-dylib-utils.h" +#include "Logging.h" + +#if !__has_feature(objc_arc) +#error The use of libdispatch in this files requires it to be compiled with ARC in order to avoid leaks +#endif + +extern char** environ; + +static dispatch_queue_t build_queue; + +inline bool has_suffix(std::string const & value, std::string const & ending) +{ + if (ending.size() > value.size()) return false; + return std::equal(ending.rbegin(), ending.rend(), value.rbegin()); +} + +static const char *tempRootDirTemplate = "/tmp/dyld_shared_cache_builder.XXXXXX"; +static char *tempRootDir = nullptr; + +void processRoot( const std::string &root, std::set &roots ) { + struct stat sb; + int res = 0; + pid_t pid; + int status; + const char* args[8]; + + res = stat(root.c_str(), &sb); + + if (res == 0 && S_ISDIR(sb.st_mode)) { + roots.insert( root ); + return; + } else if ( has_suffix( root, ".cpio" ) || has_suffix( root, ".cpio.gz" ) || has_suffix( root, ".cpgz" ) || + has_suffix( root, ".cpio.bz2" ) || has_suffix( root, ".cpbz2" ) || has_suffix( root, ".pax" ) || + has_suffix( root, ".pax.gz" ) || has_suffix( root, ".pgz" ) || has_suffix( root, ".pax.bz2" ) || + has_suffix( root, ".pbz2" ) ) { + args[0] = (char *)"/usr/bin/ditto"; + args[1] = (char *)"-x"; + args[2] = (char *)root.c_str(); + args[3] = tempRootDir; + args[4] = nullptr; + } else if (has_suffix(root, ".tar")) { + args[0] = (char *)"/usr/bin/tar"; + args[1] = (char *)"xf"; + args[2] = (char *)root.c_str(); + args[3] = (char *)"-C"; + args[4] = tempRootDir; + args[5] = nullptr; + } else if (has_suffix(root, ".tar.gz") || has_suffix(root, ".tgz")) { + args[0] = (char *)"/usr/bin/tar"; + args[1] = (char *)"xzf"; + args[2] = (char *)root.c_str(); + args[3] = (char *)"-C"; + args[4] = tempRootDir; + args[5] = nullptr; + } else if (has_suffix(root, ".tar.bz2") + || has_suffix(root, ".tbz2") + || has_suffix(root, ".tbz")) { + args[0] = (char *)"/usr/bin/tar"; + args[1] = (char *)"xjf"; + args[2] = (char *)root.c_str(); + args[3] = (char *)"-C"; + args[4] = tempRootDir; + args[5] = nullptr; + } else if (has_suffix(root, ".xar")) { + args[0] = (char *)"/usr/bin/xar"; + args[1] = (char *)"-xf"; + args[2] = (char *)root.c_str(); + args[3] = (char *)"-C"; + args[4] = tempRootDir; + args[5] = nullptr; + } else if (has_suffix(root, ".zip")) { + args[0] = (char *)"/usr/bin/ditto"; + args[1] = (char *)"-xk"; + args[2] = (char *)root.c_str(); + args[3] = tempRootDir; + args[4] = nullptr; + } else { + terminate("unknown archive type: %s", root.c_str()); + } + + res = posix_spawn(&pid, args[0], nullptr, nullptr, (char**)args, environ); + if (res != 0) terminate("Failed to spawn %s: %s (%d)", args[0], strerror(res), res); + + do { + res = waitpid(pid, &status, 0); + } while (res == -1 && errno == EINTR); + if (res != -1) { + if (WIFEXITED(status)) { + res = WEXITSTATUS(status); + } else { + res = -1; + } + } + + if (res != 0) terminate("Could not expand archive %s: %s (%d)", root.c_str(), strerror(res), res); + + for (auto &existingRoot : roots) { + if (existingRoot == tempRootDir) + return; + } + + roots.insert( tempRootDir ); +} + +bool writeRootList( const std::string &dstRoot, const std::set &roots ) { + if ( roots.size() == 0 ) return false; + + std::string rootFile = dstRoot + "/roots.txt"; + FILE* froots = ::fopen(rootFile.c_str(), "w"); + if ( froots == NULL ) + return false; + + for (auto &root : roots) { + fprintf(froots, "%s\n", root.c_str()); + } + + ::fclose(froots); + return true; +} + +std::set cachePaths; + +BOMCopierCopyOperation filteredCopy(BOMCopier copier, const char *path, BOMFSObjType type, off_t size) { + std::string absolutePath = &path[1]; + if (cachePaths.count(absolutePath)) { + return BOMCopierSkipFile; + } + return BOMCopierContinue; +} + +int main (int argc, const char * argv[]) { + @autoreleasepool { + std::set roots; + std::vector inputRoots; + std::string dylibCacheDir; + std::string release; + bool emitDevCaches = true; + bool emitElidedDylibs = true; + bool listConfigs = false; + bool copyRoots = false; + std::string dstRoot; + std::string configuration; + std::string resultPath; + + time_t mytime = time(0); + log("Started: %s", asctime(localtime(&mytime))); + + tempRootDir = strdup(tempRootDirTemplate); + mkdtemp(tempRootDir); + + for (int i = 1; i < argc; ++i) { + const char* arg = argv[i]; + if (arg[0] == '-') { + if (strcmp(arg, "-debug") == 0) { + setVerbose(true); + } else if (strcmp(arg, "-list_configs") == 0) { + listConfigs = true; + } else if (strcmp(arg, "-root") == 0) { + std::string root = argv[++i]; + inputRoots.push_back(root); + processRoot(root, roots); + } else if (strcmp(arg, "-copy_roots") == 0) { + copyRoots = true; + } else if (strcmp(arg, "-dylib_cache") == 0) { + dylibCacheDir = argv[++i]; + } else if (strcmp(arg, "-no_development_cache") == 0) { + emitDevCaches = false; + } else if (strcmp(arg, "-no_overflow_dylibs") == 0) { + emitElidedDylibs = false; + } else if (strcmp(arg, "-development_cache") == 0) { + emitDevCaches = true; + } else if (strcmp(arg, "-overflow_dylibs") == 0) { + emitElidedDylibs = true; + } else if (strcmp(arg, "-dst_root") == 0) { + dstRoot = argv[++i]; + } else if (strcmp(arg, "-release") == 0) { + release = argv[++i]; + } else if (strcmp(arg, "-results") == 0) { + resultPath = argv[++i]; + } else { + //usage(); + terminate("unknown option: %s\n", arg); + } + } else { + if (!configuration.empty()) { + terminate("You may only specify one configruation"); + } + configuration = argv[i]; + } + } + + struct rlimit rl = {OPEN_MAX, OPEN_MAX}; + (void)setrlimit(RLIMIT_NOFILE, &rl); + + if (dylibCacheDir.empty() && release.empty()) { + terminate("you must specify either -dylib_cache or -release"); + } else if (!dylibCacheDir.empty() && !release.empty()) { + terminate("you may not use -dylib_cache and -release at the same time"); + } + + if ((configuration.empty() || dstRoot.empty()) && !listConfigs) { + terminate("Must specify a configuration and a -dst_root OR -list_configs\n"); + } + + + if (dylibCacheDir.empty()) { + dylibCacheDir = std::string("/AppleInternal/Developer/DylibCaches/") + release + ".dlc"; + } + + //Move into the dir so we can use relative path manifests + chdir(dylibCacheDir.c_str()); + + auto manifest = Manifest(dylibCacheDir + "/Manifest.plist", roots); + + if (manifest.build.empty()) { + terminate("No manifest found at '%s/Manifest.plist'\n", dylibCacheDir.c_str()); + } + log("Building Caches for %s", manifest.build.c_str()); + + if (listConfigs) { + for (auto& config : manifest.configurations) { + printf("%s\n", config.first.c_str()); + } + exit(0); + } + + std::map filteredConfigs; + + for (auto& config : manifest.configurations) { + if (config.first == configuration) { + filteredConfigs[config.first] = config.second; + + for (auto& arch : filteredConfigs[config.first].architectures) { + arch.second.results = Manifest::Results(); + } + } + } + + if ( filteredConfigs.empty() ) { + terminate( "No config %s. Please run with -list_configs to see configurations available for this %s.\n", + configuration.c_str(), manifest.build.c_str() ); + } + + manifest.configurations = filteredConfigs; + manifest.calculateClosure(false); + + // FIXME: Plumb through no_development + + std::shared_ptr builder = std::make_shared( manifest, false, false, true ); + builder->buildCaches(dstRoot); + writeRootList(dstRoot, roots); + + if (copyRoots) { + for (auto& config : manifest.configurations) { + for (auto& arch : config.second.architectures) { + for (auto& dylib : arch.second.results.dylibs) { + if (dylib.second.included) { + MachOProxy *proxy = manifest.dylibProxy( dylib.first, arch.first ); + cachePaths.insert( proxy->installName ); + for ( auto &alias : proxy->installNameAliases ) { + cachePaths.insert(alias); + } + } + } + } + } + + BOMCopier copier = BOMCopierNewWithSys(BomSys_default()); + BOMCopierSetCopyFileStartedHandler(copier, filteredCopy); + for (auto& root : roots) { + BOMCopierCopy(copier, root.c_str(), dstRoot.c_str()); + } + BOMCopierFree(copier); + } + + // Create an empty FIPS data in the root + (void)mkpath_np((dstRoot + "/private/var/db/FIPS/").c_str(), 0755); + int fd = open((dstRoot + "/private/var/db/FIPS/fips_data").c_str(), O_CREAT | O_TRUNC, 0644); + close(fd); + + // Now that all the build commands have been issued lets put a barrier in after then which can tear down the app after + // everything is written. + + builder->logStats(); + if (!resultPath.empty()) { + manifest.write(resultPath); + } + + pid_t pid; + int res = 0; + int status; + const char* args[8]; + + args[0] = (char*)"/bin/rm"; + args[1] = (char*)"-rf"; + args[2] = (char*)tempRootDir; + args[3] = nullptr; + + res = posix_spawn(&pid, args[0], nullptr, nullptr, (char**)args, environ); + if (res != 0) + terminate("Failed to spawn %s: %s (%d)", args[0], strerror(res), res); + + do { + res = waitpid(pid, &status, 0); + } while (res == -1 && errno == EINTR); + if (res != -1) { + if (WIFEXITED(status)) { + res = WEXITSTATUS(status); + } else { + res = -1; + } + } + + dumpLogAndExit(); + } + + dispatch_main(); + + return 0; +} diff --git a/src/dyld-apple/interlinked-dylibs/mega-dylib-utils.h b/src/dyld-apple/interlinked-dylibs/mega-dylib-utils.h new file mode 100644 index 000000000..0482ffa15 --- /dev/null +++ b/src/dyld-apple/interlinked-dylibs/mega-dylib-utils.h @@ -0,0 +1,255 @@ +/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- + * + * Copyright (c) 2014 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@ + */ + +#ifndef __MEGA_DYLIB_UTILS_H__ +#define __MEGA_DYLIB_UTILS_H__ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "CacheFileAbstraction.hpp" + +#include "MachOFileAbstraction.hpp" + +#include "Manifest.h" +#include "MachOProxy.h" + +struct SharedCache; + +struct FileCache { + FileCache(void); + std::tuple cacheLoad(const std::string path); + void preflightCache(const std::string& path); + void preflightCache(const std::unordered_set &paths); +private: + void fill(const std::string& path); + + std::unordered_map> entries; + dispatch_queue_t cache_queue; +}; + +extern FileCache fileCache; + +typedef std::pair>> WarningTargets; + +inline uint64_t align(uint64_t addr, uint8_t p2) +{ + uint64_t mask = (1 << p2); + return (addr + mask - 1) & (-mask); +} + + +uint64_t sharedRegionRegionSize(ArchPair arch); +uint8_t sharedRegionRegionAlignment(ArchPair arch); +ArchPair archForString(const std::string& archStr); +std::string stringForArch(ArchPair arch, bool allowUnknown = false); +std::string fallbackArchStringForArchString( const std::string& archStr ); + +struct SharedCache { + struct SegmentInfo { + SegmentInfo(const MachOProxy::Segment* seg) + : base(seg), address(0), cacheFileOffset(0), cacheSegSize(0) { } + + const MachOProxy::Segment* base; + uint64_t address; + uint64_t cacheFileOffset; + uint64_t cacheSegSize; + }; + + struct Region { + uint64_t address; + uint64_t size; + uint64_t fileOffset; + uint32_t prot; + }; + + SharedCache(Manifest& manifest, + const std::string& configuration, const std::string& architecture); + + // We do not need an explicit destructor despite our move/copy constructor because the resource the are dealing with is a + // std::unique which has a registered destructor + + //FIXME reminants of merging the two classes, unifyu these + uint64_t vmSize() const { return _vmSize; } + uint64_t fileSize() const { return _fileSize; } + const uint8_t* cdHash() { return _cdHash; } + std::string cdHashString(); + + //FIXME: This should be private, but we can't do that until we move write out into the class after the next refactor + std::shared_ptr buffer() const; + + void buildForDevelopment(const std::string& cachePath); + void buildForProduction(const std::string& cachePath); + bool writeCacheMapFile(const std::string& mapPath); + + typedef std::function& segments)> DylibHandler; + // Calls lambda once per image in the cache + void forEachImage(DylibHandler handler); + + typedef std::function RegionHandler; + // Calls lambda once per region in the cache + void forEachRegion(RegionHandler handler); + + void setLinkeditsMappingEndFileOffset(uint64_t newFileSize); + void setAcceleratorInfoRange(uint64_t accelInfoAddr, uint32_t accelInfoSize); + void setUnmappedLocalsRange(uint64_t localSymbolsOffset, uint32_t unmappedSize); + void recomputeCacheUUID(void); + +private: + void loadDirtyDataOrderFile(const std::string& dirtyDataOrderFile); + void sortDylibs(const std::string& dylibOrderFile); + void assignSegmentAddresses(); + void bypassStubs(const std::vector& branchPoolStartAddrs); + void optimizeLinkedit(bool dontMapLocalSymbols, bool addAcceleratorTables, const std::vector& branchPoolOffsets); + + // Once all a dylib's segments are copied into a cache, this function will adjust the contents of + // the TEXT, DATA, and LINKEDIT segments in the cache to be correct for their new addresses. + void bindAllImagesInCache(const std::unordered_map& dylibPathToMachHeader, std::vector& pointersForASLR); + + // After adjustImageForNewSegmentLocations() is called to rebase all segments, this function can be called to + // bind all symbols to their new addresses + void adjustImageForNewSegmentLocations(const std::vector& segNewStartAddresses, + const std::vector& segCacheFileOffsets, + const std::vector& segCacheSizes, std::vector& pointersForASLR); + + uint64_t pathHash(const char* path); + void writeCacheHeader(void); + void writeCacheSegments(void); + void rebaseAll(void); + void rebase(MachOProxy* dylib); + void bindAll(void); + void optimizeObjC(bool forProduction); + void writeSlideInfoV2(void); + + void buildUnoptimizedCache(); + void appendCodeSignature(const std::string& suffix); + template void buildForDevelopment(const std::string& cachePath); + template void buildForProduction(const std::string& cachePath); + template void forEachImage(DylibHandler handler); + template void forEachRegion(RegionHandler handler); + template void setLinkeditsMappingEndFileOffset(uint64_t newFileSize); + template void setAcceleratorInfoRange(uint64_t accelInfoAddr, uint32_t accelInfoSize); + template void setUnmappedLocalsRange(uint64_t localSymbolsOffset, uint32_t unmappedSize); + template void recomputeCacheUUID(void); + template void bypassStubs(const std::vector& branchPoolStartAddrs); + template void optimizeLinkedit(bool dontMapLocalSymbols, bool addAcceleratorTables, const std::vector& branchPoolOffsets); + template void writeCacheHeader(void); + template void writeSlideInfo(void); + template void writeSlideInfoV2(uint64_t deltaMask, uint64_t valueAdd); + + template void findImplicitAliases(std::shared_ptr dylib); + template void addPageStarts(uint8_t* pageContent, const bool bitmap[], const dyldCacheSlideInfo2* info, + std::vector& pageStarts, std::vector& pageExtras); + template bool makeRebaseChain(uint8_t* pageContent, uint16_t lastLocationOffset, uint16_t newOffset, const dyldCacheSlideInfo2* info); + void findDylibAndSegment(const void* contentPtr, std::string& dylibName, std::string& segName); + + ArchPair _arch; + std::vector _dylibs; + std::shared_ptr _buffer; + std::unordered_map> _segmentMap; + + std::string archName(); + + Manifest& _manifest; + const Manifest::Architecture& _archManifest; + uint32_t _aliasCount; + Region _textRegion; + Region _dataRegion; + Region _readOnlyRegion; + uint64_t _slideInfoFileOffset; + uint64_t _slideInfoBufferSize; + uint64_t _fileSize; + uint64_t _vmSize; + std::unordered_map _dataDirtySegsOrder; + std::vector _pointersForASLR; + std::vector _branchPoolStarts; + uint64_t _branchPoolsLinkEditStartAddr; + uint8_t _cdHash[20]; +}; + +std::string normalize_absolute_file_path(const std::string &path); +std::string baspath(const std::string& path); +std::string dirpath(const std::string& path); + +std::string toolDir(); +bool isProtectedBySIP(const std::string& path, int fd=-1); + + +template +inline bool is_disjoint(const Set1& set1, const Set2& set2) +{ + if (set1.empty() || set2.empty()) + return true; + + typename Set1::const_iterator it1 = set1.begin(), it1End = set1.end(); + typename Set2::const_iterator it2 = set2.begin(), it2End = set2.end(); + + if (*it1 > *set2.rbegin() || *it2 > *set1.rbegin()) + return true; + + while (it1 != it1End && it2 != it2End) { + if (*it1 == *it2) + return false; + if (*it1 < *it2) { + it1++; + } else { + it2++; + } + } + + return true; +} + +inline bool has_prefix(const std::string& str, const std::string& prefix) +{ + return std::mismatch(prefix.begin(), prefix.end(), str.begin()).first == prefix.end(); +} + + +#define NEW_CACHE_FILE_FORMAT 0 + +#endif // __MEGA_DYLIB_UTILS_H__ + + + + + diff --git a/src/dyld-apple/interlinked-dylibs/multi_dyld_shared_cache_builder.mm b/src/dyld-apple/interlinked-dylibs/multi_dyld_shared_cache_builder.mm new file mode 100644 index 000000000..7aec8dc4d --- /dev/null +++ b/src/dyld-apple/interlinked-dylibs/multi_dyld_shared_cache_builder.mm @@ -0,0 +1,289 @@ +/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- + * + * Copyright (c) 2016 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@ + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "MultiCacheBuilder.h" +#include "Manifest.h" + +#include "mega-dylib-utils.h" +#include "Logging.h" + +#if !__has_feature(objc_arc) +#error The use of libdispatch in this files requires it to be compiled with ARC in order to avoid leaks +#endif + +static dispatch_queue_t build_queue = dispatch_queue_create("com.apple.dyld.build", DISPATCH_QUEUE_CONCURRENT); +static dispatch_group_t build_group = dispatch_group_create(); +static dispatch_semaphore_t writeLimitingSemaphore = dispatch_semaphore_create(1); + +bool copyFile(const std::string& from, const std::string& to) +{ + const uint8_t* p = (uint8_t*)(-1); + struct stat stat_buf; + bool rp; + + std::tie(p, stat_buf, rp) = fileCache.cacheLoad(from); + if (p == (uint8_t*)(-1)) + return false; + + dispatch_group_enter(build_group); + mkpath_np(dirpath(to).c_str(), 0755); + + dispatch_semaphore_wait(writeLimitingSemaphore, DISPATCH_TIME_FOREVER); + int fd = open(to.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (fd > 0) { + ssize_t writtenSize = pwrite(fd, p, stat_buf.st_size, 0); + if (writtenSize != stat_buf.st_size) + terminate("write() failure creating cache file, errno=%d (%s)", errno, strerror(errno)); + + ::close(fd); + verboseLog("Wrote out: %s", to.c_str()); + } else { + terminate("can't create file '%s', errnor=%d (%s)", to.c_str(), errno, strerror(errno)); + } + dispatch_semaphore_signal(writeLimitingSemaphore); + dispatch_group_leave(build_group); + + return true; +} + +#define kDylibCachePrefix "/AppleInternal/Developer/DylibCaches/" + +void createArtifact(Manifest& manifest, const std::string& dylibCachePath, bool includeExecutables) +{ + mkpath_np(dylibCachePath.c_str(), 0755); + (void)copyFile(manifest.dylibOrderFile, dylibCachePath + "Metadata/dylibOrderFile.txt"); + (void)copyFile(manifest.dirtyDataOrderFile, dylibCachePath + "Metadata/dirtyDataOrderFile.txt"); + (void)copyFile(manifest.metabomFile, dylibCachePath + "Metadata/metabom.bom"); + + std::set copied; + + for (auto archFiles : manifest.architectureFiles) { + for (auto& file : archFiles.second.dylibs) { + std::string installname = file.first; + if (copied.count(installname) > 0) { + continue; + } + (void)copyFile(file.second.proxy->path, normalize_absolute_file_path(dylibCachePath + "/Root/" + file.first)); + copied.insert(installname); + } + if (includeExecutables) { + for (auto& file : archFiles.second.executables) { + std::string installname = file.first; + if (copied.count(installname) > 0) { + continue; + } + (void)copyFile(file.second.proxy->path, normalize_absolute_file_path(dylibCachePath + "/Root/" + file.first)); + copied.insert(installname); + } + } + } + + log("Artifact dylibs copied"); +} + +void addArtifactPaths(Manifest &manifest) { + manifest.dylibOrderFile = "./Metadata/dylibOrderFile.txt"; + manifest.dirtyDataOrderFile = "./Metadata/dirtyDataOrderFile.txt"; + manifest.metabomFile = "./Metadata/metabom.bom"; + + for ( auto& projects : manifest.projects ) { + if ( projects.second.sources[0] != "./Root/" ) { + projects.second.sources.insert( projects.second.sources.begin(), "./Root/" ); + } + } +} + +int main (int argc, const char * argv[]) { + @autoreleasepool { + auto defaultCtx = std::make_shared(""); + setLoggingContext(defaultCtx); + + Manifest manifest; + std::string masterDstRoot; + std::string dylibCacheDir; + std::string resultPath; + bool skipWrites = false; + bool skipBuilds = false; + bool preflight = false; + std::string manifestPath; + + time_t mytime = time(0); + log("Started: %s", asctime(localtime(&mytime))); + + // parse command line options + for (int i = 1; i < argc; ++i) { + const char* arg = argv[i]; + if (arg[0] == '-') { + if (strcmp(arg, "-debug") == 0) { + setVerbose(true); + } else if (strcmp(arg, "-skip_writes") == 0) { + skipWrites = true; + } else if (strcmp(arg, "-skip_builds") == 0) { + skipBuilds = true; + } else if (strcmp(arg, "-delete_writes") == 0) { + skipWrites = true; + } else if (strcmp(arg, "-dylib_cache") == 0) { + dylibCacheDir = argv[++i]; + } else if (strcmp(arg, "-preflight") == 0) { + preflight = true; + skipWrites = true; + } else if (strcmp(arg, "-master_dst_root") == 0) { + masterDstRoot = argv[++i]; + if (masterDstRoot.empty()) { + terminate("-master_dst_root missing path argument"); + } + } else if (strcmp(arg, "-results") == 0) { + resultPath = argv[++i]; + } else if (strcmp(arg, "-plist") == 0) { + manifestPath = argv[++i]; + (void)chdir(dirname(strdup(manifestPath.c_str()))); + manifest = Manifest(manifestPath); + } else { + // usage(); + terminate("unknown option: %s", arg); + } + } else { + manifestPath = argv[i]; + + (void)chdir(dirname(strdup(argv[i]))); + } + } + + if ( getenv( "LGG_SKIP_CACHE_FUN" ) != nullptr ) { + skipBuilds = true; + } + + if (manifest.build.empty()) { + terminate("No version found in manifest"); + } + log("Building Caches for %s", manifest.build.c_str()); + + if ( masterDstRoot.empty() ) { + terminate("-master_dst_root required path argument"); + } + + if (manifest.manifest_version < 4) { + terminate("must specify valid manifest file"); + } + + struct rlimit rl = {OPEN_MAX, OPEN_MAX}; + (void)setrlimit(RLIMIT_NOFILE, &rl); + + if (!skipWrites) { + (void)mkpath_np((masterDstRoot + "/Boms/").c_str(), 0755); + } + + if (manifest.dylibOrderFile.empty()) { + manifest.dylibOrderFile = toolDir() + "/dylib-order.txt"; + } + + if (manifest.dirtyDataOrderFile.empty()) { + manifest.dirtyDataOrderFile = toolDir() + "/dirty-data-segments-order.txt"; + } + + auto dylibCacheCtx = std::make_shared("DylibCache"); + setLoggingContext(dylibCacheCtx); + + if (!skipWrites) { + cacheBuilderDispatchGroupAsync(build_group, build_queue, [&] { + createArtifact(manifest, masterDstRoot + "/Artifact.dlc/", true); + }); + + if (!dylibCacheDir.empty()) { + cacheBuilderDispatchGroupAsync(build_group, build_queue, [&] { + createArtifact(manifest, dylibCacheDir + "/AppleInternal/Developer/DylibCaches/" + manifest.build + ".dlc/", false); + }); + } + } + setLoggingContext(defaultCtx); + + manifest.calculateClosure(false); + std::shared_ptr builder = std::make_shared(manifest, true, skipWrites, false, skipBuilds); + dispatch_group_async(build_group, build_queue, [&] { builder->buildCaches(masterDstRoot); }); + dispatch_group_wait(build_group, DISPATCH_TIME_FOREVER); + + if (!preflight) { + setLoggingContext(dylibCacheCtx); + addArtifactPaths(manifest); + setLoggingContext(defaultCtx); + + if (!resultPath.empty()) { + manifest.write(resultPath); + } + + setLoggingContext(dylibCacheCtx); + copyFile(manifestPath, masterDstRoot + "/Artifact.dlc/BNIManifest.plist"); + manifest.write(masterDstRoot + "/Artifact.dlc/Manifest.plist"); + + if (!dylibCacheDir.empty()) { + manifest.write(dylibCacheDir + kDylibCachePrefix + manifest.build + ".dlc/Manifest.plist"); + } + setLoggingContext(defaultCtx); + } + + builder->logStats(); + + dumpLogAndExit(); + } + + dispatch_main(); + + return 0; +} + + diff --git a/src/dyld-apple/interlinked-dylibs/update_dyld_shared_cache.mm b/src/dyld-apple/interlinked-dylibs/update_dyld_shared_cache.mm new file mode 100644 index 000000000..46f9e3a79 --- /dev/null +++ b/src/dyld-apple/interlinked-dylibs/update_dyld_shared_cache.mm @@ -0,0 +1,448 @@ +/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- + * + * Copyright (c) 2014 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@ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern "C" { +#include +} + +#include +#include +#include +#include +#include + +#include "MachOProxy.h" +#include "manifest.h" +#include "mega-dylib-utils.h" +#include "Logging.h" + +#import "MultiCacheBuilder.h" + +#if !__has_feature(objc_arc) +#error The use of libdispatch in this files requires it to be compiled with ARC in order to avoid leaks +#endif + +const std::string anchorsDirPath = "/private/var/db/dyld/shared_region_roots"; + +bool parsePathsFile( const std::string& filePath, std::set& paths ) { + verboseLog( "parsing .paths file '%s'", filePath.c_str() ); + std::ifstream myfile( filePath ); + if ( myfile.is_open() ) { + std::string line; + while ( std::getline(myfile, line) ) { + size_t pos = line.find('#'); + if ( pos != std::string::npos ) + line.resize(pos); + while ( line.size() != 0 && isspace(line.back()) ) { + line.pop_back(); + } + if ( !line.empty() ) paths.insert( line ); + } + myfile.close(); + + return true; + } + return false; +} + +static bool parseDirectoryOfPathsFiles(const std::string& dirPath, std::set& paths) +{ + DIR* dir = ::opendir(dirPath.c_str()); + if ( dir == NULL ) + return false; + + for (dirent* entry = ::readdir(dir); entry != NULL; entry = ::readdir(dir)) { + if ( entry->d_type == DT_REG || entry->d_type == DT_UNKNOWN ) { + // only look at regular files ending in .paths + if ( strcmp(&entry->d_name[entry->d_namlen-6], ".paths") == 0 ) { + struct stat statBuf; + std::string filePathStr = dirPath + "/" + entry->d_name; + const char* filePath = filePathStr.c_str(); + if ( lstat(filePath, &statBuf) == -1 ) { + warning("can't access file '%s'", filePath); + } + else if ( S_ISREG(statBuf.st_mode) ) { + parsePathsFile(filePath, paths); + } + else { + warning("not a regular file '%s'", filePath); + } + } + else { + warning("ignoring file with wrong extension '%s'", entry->d_name); + } + } + } + ::closedir(dir); + return true; +} + +static bool buildInitialPaths(const std::string& volumeRootPath, const std::string& overlayPath, std::set& paths) +{ + // in -root mode, look for roots in /rootpath/private/var/db/dyld/shared_region_roots + if ( volumeRootPath != "/" ) { + if ( parseDirectoryOfPathsFiles(volumeRootPath + "/" + anchorsDirPath, paths) ) + return true; + // fallback to .paths files on boot volume + return parseDirectoryOfPathsFiles(anchorsDirPath, paths); + } + + // in -overlay mode, look for .paths first in each /overlay/private/var/db/dyld/shared_region_roots + if ( !overlayPath.empty() ) { + parseDirectoryOfPathsFiles(overlayPath + "/" + anchorsDirPath, paths); + } + + // look for .paths files in /private/var/db/dyld/shared_region_roots + return parseDirectoryOfPathsFiles(anchorsDirPath, paths); +} + +bool fileExists(const std::string& path, bool& isSymLink) +{ + struct stat statBuf; + if ( lstat(path.c_str(), &statBuf) == -1 ) + return false; + isSymLink = S_ISLNK(statBuf.st_mode); + return S_ISREG(statBuf.st_mode) || isSymLink; +} + +bool tryPath(const std::string& prefix, const std::string& path, std::string& foundPath, std::vector& aliases) +{ + foundPath = prefix + path; + bool isSymLink; + if ( !fileExists(foundPath, isSymLink) ) + return false; + if ( isSymLink ) { + // handle case where install name is a symlink to real file (e.g. libstdc++.6.dylib -> libstdc++.6.0.9.dylib) + char pathInSymLink[MAXPATHLEN]; + long len = ::readlink(foundPath.c_str(), pathInSymLink, sizeof(pathInSymLink)); + if ( len != -1 ) { + pathInSymLink[len] = '\0'; + if ( pathInSymLink[0] != '/' ) { + std::string aliasPath = path; + size_t pos = aliasPath.rfind('/'); + if ( pos != std::string::npos ) { + std::string newPath = aliasPath.substr(0,pos+1) + pathInSymLink; + aliases.push_back(newPath); + } + } + } + } + char realPath[MAXPATHLEN]; + if ( ::realpath(foundPath.c_str(), realPath) ) { + if ( foundPath != realPath ) { + std::string altPath = realPath; + if ( !prefix.empty() ) { + if ( altPath.substr(0, prefix.size()) == prefix ) { + altPath = altPath.substr(prefix.size()); + } + } + if ( altPath != path ) + aliases.push_back(path); + else + aliases.push_back(altPath); + } + } + return true; +} + +bool improvePath(const char* volumeRootPath, const std::vector& overlayPaths, + const std::string& path, std::string& foundPath, std::vector& aliases) +{ + for (const char* overlay : overlayPaths) { + if ( tryPath(overlay, path, foundPath, aliases) ) + return true; + } + if ( volumeRootPath[0] != '\0' ) { + if ( tryPath(volumeRootPath, path, foundPath, aliases) ) + return true; + } + return tryPath("", path, foundPath, aliases); +} + +std::string fileExists( const std::string& path ) { + const uint8_t* p = (uint8_t*)( -1 ); + struct stat stat_buf; + bool rootless; + + std::tie( p, stat_buf, rootless ) = fileCache.cacheLoad( path ); + if ( p != (uint8_t*)( -1 ) ) { + return normalize_absolute_file_path( path ); + } + + return ""; +} + +void populateManifest(Manifest& manifest, std::set archs, const std::string& overlayPath, + const std::string& rootPath, const std::set& paths) { + for ( const auto& arch : archs ) { + auto fallback = fallbackArchStringForArchString(arch); + std::set allArchs = archs; + std::set processedPaths; + std::set unprocessedPaths = paths; + std::set pathsToProcess; + std::set_difference( unprocessedPaths.begin(), unprocessedPaths.end(), processedPaths.begin(), processedPaths.end(), + std::inserter( pathsToProcess, pathsToProcess.begin() ) ); + while ( !pathsToProcess.empty() ) { + for (const std::string path : pathsToProcess) { + processedPaths.insert(path); + std::string fullPath; + if ( rootPath != "/" ) { + // with -root, only look in the root path volume + fullPath = fileExists(rootPath + path); + } + else { + // with -overlay, look first in overlay dir + if ( !overlayPath.empty() ) + fullPath = fileExists(overlayPath + path); + // if not in overlay, look in boot volume + if ( fullPath.empty() ) + fullPath = fileExists(path); + } + if ( fullPath.empty() ) + continue; + auto proxies = MachOProxy::findDylibInfo(fullPath, true, true); + auto proxy = proxies.find(arch); + if (proxy == proxies.end()) + proxy = proxies.find(fallback); + if (proxy == proxies.end()) + continue; + + for ( const auto& dependency : proxy->second->dependencies ) { + unprocessedPaths.insert( dependency ); + } + + if ( proxy->second->installName.empty() ) { + continue; + } + + proxy->second->addAlias( path ); + manifest.architectureFiles[arch].dylibs.insert(std::make_pair(proxy->second->installName, + Manifest::File(proxy->second))); + manifest.configurations["localhost"].architectures[arch].anchors.push_back( proxy->second->installName ); + } + + pathsToProcess.clear(); + std::set_difference( unprocessedPaths.begin(), unprocessedPaths.end(), processedPaths.begin(), processedPaths.end(), + std::inserter( pathsToProcess, pathsToProcess.begin() ) ); + } + } +} + +static bool runningOnHaswell() +{ + // check system is capable of running x86_64h code + struct host_basic_info info; + mach_msg_type_number_t count = HOST_BASIC_INFO_COUNT; + mach_port_t hostPort = mach_host_self(); + kern_return_t result = host_info(hostPort, HOST_BASIC_INFO, (host_info_t)&info, &count); + mach_port_deallocate(mach_task_self(), hostPort); + + return ( (result == KERN_SUCCESS) && (info.cpu_subtype == CPU_SUBTYPE_X86_64_H) ); +} + + +#define TERMINATE_IF_LAST_ARG( s ) \ + do { \ + if ( i == argc - 1 ) terminate( s ); \ + } while ( 0 ) + +int main(int argc, const char* argv[]) +{ + std::string rootPath; + std::string overlayPath; + std::string platform = "osx"; + std::string dylibListFile; + bool universal = false; + bool force = false; + std::string cacheDir; + std::set archStrs; + std::vector anchors; + + // parse command line options + for (int i = 1; i < argc; ++i) { + const char* arg = argv[i]; + if (arg[0] == '-') { + if (strcmp(arg, "-debug") == 0) { + setVerbose(true); + } else if (strcmp(arg, "-verbose") == 0) { + setVerbose(true); + } else if (strcmp(arg, "-dont_map_local_symbols") == 0) { + //We are going to ignore this + } else if (strcmp(arg, "-iPhone") == 0) { + platform = "iphoneos"; + } else if (strcmp(arg, "-dylib_list") == 0) { + TERMINATE_IF_LAST_ARG("-dylib_list missing argument"); + dylibListFile = argv[++i]; + } else if ((strcmp(arg, "-root") == 0) || (strcmp(arg, "--root") == 0)) { + TERMINATE_IF_LAST_ARG("-root missing path argument\n"); + rootPath = argv[++i]; + } else if (strcmp(arg, "-overlay") == 0) { + TERMINATE_IF_LAST_ARG("-overlay missing path argument\n"); + overlayPath = argv[++i]; + } else if (strcmp(arg, "-cache_dir") == 0) { + TERMINATE_IF_LAST_ARG("-cache_dir missing path argument\n"); + cacheDir = argv[++i]; + } else if (strcmp(arg, "-arch") == 0) { + TERMINATE_IF_LAST_ARG("-arch missing argument\n"); + archStrs.insert(argv[++i]); + } else if (strcmp(arg, "-force") == 0) { + force = true; + } else if (strcmp(arg, "-sort_by_name") == 0) { + //No-op, we always do this now + } else if (strcmp(arg, "-universal_boot") == 0) { + universal = true; + } else { + //usage(); + terminate("unknown option: %s\n", arg); + } + } else { + //usage(); + terminate("unknown option: %s\n", arg); + } + } + + setReturnNonZeroOnTerminate(); + setWarnAnErrorPrefixes("update_dyld_shared_cache: warning: ", "update_dyld_shared_cache failed: "); + + if ( !rootPath.empty() & !overlayPath.empty() ) + terminate("-root and -overlay cannot be used together\n"); + + //FIXME realpath on root and overlays + + if (rootPath.empty()) { + rootPath = "/"; + } + + if ( cacheDir.empty() ) { + // write cache file into -root or -overlay directory, if used + if ( rootPath != "/" ) + cacheDir = rootPath + MACOSX_DYLD_SHARED_CACHE_DIR; + else if ( !overlayPath.empty() ) + cacheDir = overlayPath + MACOSX_DYLD_SHARED_CACHE_DIR; + else + cacheDir = MACOSX_DYLD_SHARED_CACHE_DIR; + } + + if (universal) { + if ( platform == "iphoneos" ) { + terminate("-iPhoneOS and -universal are incompatible\n"); + } + archStrs.clear(); + } + + if (archStrs.size() == 0) { + if ( platform == "iphoneos" ) { + terminate("Must specify -arch(s) when using -iPhone\n"); + } + else { + if ( universal ) { + // -universal_boot should make all possible dyld caches + archStrs.insert("i386"); + archStrs.insert("x86_64"); + archStrs.insert("x86_64h"); + } + else { + // just make caches for this machine + archStrs.insert("i386"); + archStrs.insert(runningOnHaswell() ? "x86_64h" : "x86_64"); + } + } + } + + int err = mkpath_np(cacheDir.c_str(), S_IRWXU | S_IRGRP|S_IXGRP | S_IROTH|S_IXOTH); + if (err != 0 && err != EEXIST) { + terminate("mkpath_np fail: %d", err); + } + + Manifest manifest; + + std::set paths; + + if ( !dylibListFile.empty() ) { + if ( !parsePathsFile( dylibListFile, paths ) ) { + terminate( "could not build intiial paths\n" ); + } + } else if ( !buildInitialPaths( rootPath, overlayPath, paths ) ) { + terminate( "could not build intiial paths\n" ); + } + + manifest.platform = platform; + populateManifest( manifest, archStrs, overlayPath, rootPath, paths ); + + // If the path we are writing to is trusted then our sources need to be trusted + // Can't update the update_dyld_shared_cache on a non-boot volume + bool requireDylibsBeRootlessProtected = isProtectedBySIP(cacheDir); + manifest.calculateClosure( requireDylibsBeRootlessProtected ); + manifest.pruneClosure(); + + for (const std::string& archStr : archStrs) { + std::string cachePath = cacheDir + "/dyld_shared_cache_" + archStr; + if ( manifest.sameContentsAsCacheAtPath("localhost", archStr, cachePath) && !force ) { + manifest.configurations["localhost"].architectures.erase(archStr); + verboseLog("%s is already up to date", cachePath.c_str()); + } + } + + // If caches already up to date, do nothing + if ( manifest.configurations["localhost"].architectures.empty() ) + dumpLogAndExit(false); + + // build caches + std::shared_ptr builder = std::make_shared(manifest, false, false, false, false, requireDylibsBeRootlessProtected); + builder->buildCaches(cacheDir); + + // Save off spintrace data + std::string nuggetRoot = (overlayPath.empty() ? rootPath : overlayPath); + (void)dscsym_save_nuggets_for_current_caches(nuggetRoot.c_str()); + + // Now that all the build commands have been issued lets put a barrier in after then which can tear down the app after + // everything is written. + builder->logStats(); + dumpLogAndExit(false); + + dispatch_main(); +} + diff --git a/src/dyld-apple/interlinked-dylibs/update_dyld_shared_cache_compat.cpp b/src/dyld-apple/interlinked-dylibs/update_dyld_shared_cache_compat.cpp new file mode 100644 index 000000000..75f39888a --- /dev/null +++ b/src/dyld-apple/interlinked-dylibs/update_dyld_shared_cache_compat.cpp @@ -0,0 +1,309 @@ +/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- + * + * Copyright (c) 2014 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@ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "mega-dylib-utils.h" + +/* + This is a compatibility driver to allow us to support migrating to the new cache codebase and format without forcing B&I to alter gencaches. + This tool only supports flags used by B&I + This intention is for this tool to be removed in the near future and replaced with a tool that is not commandline compatible, + but allows shared caches to be built directly out of BuildRecords. + */ + +/* + Example commandline: + [60042] INFO - Executing Command: /SWE/Teams/DT-SWB/iOS/Binaries/Binaries5/update_dyld_shared_cache/update_dyld_shared_cache-357.0.91~2/Root/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/usr/local/bin/update_dyld_shared_cache + -debug + -root /BuildRoot//Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS9.0.Internal.sdk + -dylib_list /tmp/dylibs.K93aInternalOS.60042 + -cache_dir /tmp/K93aInternalOS.60042 + -arch armv7 + -iPhone + -dont_map_local_symbols + -overlay /SWE/Teams/DT-SWB/iOS/Binaries/Binaries5/XPCService_caches/XPCService_caches-119~94/Root/K93aInternalOS/ + -overlay /SWE/Teams/DT-SWB/iOS/Binaries/Binaries5/libxpc_caches/libxpc_caches-649~18/Root/K93aInternalOS/ + */ + +// record warnings and add to .map file +static std::vector> sWarnings; + + +static void write_cache(const char* cachePath, SharedCache& cache) { + // create temp file for cache + int fd = ::open(cachePath, O_CREAT | O_RDWR | O_TRUNC, 0644); + if ( fd == -1 ) + terminate("can't create temp file %s, errnor=%d", cachePath, errno); + + // try to allocate whole cache file contiguously + fstore_t fcntlSpec = { F_ALLOCATECONTIG|F_ALLOCATEALL, F_PEOFPOSMODE, 0, static_cast(cache.fileSize()), 0 }; + ::fcntl(fd, F_PREALLOCATE, &fcntlSpec); + + // write out cache file + if ( ::pwrite(fd, cache.buffer().get(), cache.fileSize(), 0) != cache.fileSize() ) + terminate("write() failure creating cache file, errno=%d", errno); + + // flush to disk and close + int result = ::fcntl(fd, F_FULLFSYNC, NULL); + if ( result == -1 ) + warning("fcntl(F_FULLFSYNC) failed with errno=%d for %s\n", errno, cachePath); + ::close(fd); + + // write .map file + char mapPath[MAXPATHLEN]; + strlcpy(mapPath, cachePath, MAXPATHLEN); + strlcat(mapPath, ".map", MAXPATHLEN); + cache.writeCacheMapFile(mapPath); + + // append any warnings encountered + if ( !sWarnings.empty() ) { + FILE* fmap = ::fopen(mapPath, "a"); + if ( fmap != NULL ) { + fprintf(fmap, "\n\n"); + for ( std::unique_ptr& msg : sWarnings ) { + fprintf(fmap, "# %s", &*msg); + } + ::fclose(fmap); + } + } +} + +int main(int argc, const char* argv[]) +{ + std::set onlyArchs; + const char *rootPath = NULL; + std::vector searchPaths; + std::vector> dylibs; + const char* dylibListFile = NULL; +// bool force = false; +// bool keepSignatures = false; + bool explicitCacheDir = false; + bool dontMapLocalSymbols = false; + bool verbose = false; + bool iPhoneOS = false; + const char* cacheDir = NULL; + const char* archStr = NULL; + ArchPair archPair(CPU_TYPE_ARM64, CPU_SUBTYPE_ARM64_ALL); + + // parse command line options + for(int i=1; i < argc; ++i) { + const char* arg = argv[i]; + if ( arg[0] == '-' ) { + if ( strcmp(arg, "-debug") == 0 ) { + verbose = true; + } + else if ( strcmp(arg, "-dont_map_local_symbols") == 0 ) { + dontMapLocalSymbols = true; + } + else if ( strcmp(arg, "-iPhone") == 0 ) { + iPhoneOS = true; + } + else if ( strcmp(arg, "-dylib_list") == 0 ) { + dylibListFile = argv[++i]; + if ( dylibListFile == NULL ) + terminate("-dylib_list missing path argument\n"); + } + else if ( (strcmp(arg, "-root") == 0) || (strcmp(arg, "--root") == 0) ) { + rootPath = argv[++i]; + } + else if ( strcmp(arg, "-overlay") == 0 ) { + const char* path = argv[++i]; + if ( path == NULL ) + terminate("-overlay missing path argument\n"); + searchPaths.push_back(path); + } + else if ( strcmp(arg, "-cache_dir") == 0 ) { + cacheDir = argv[++i]; + if ( cacheDir == NULL ) + terminate("-cache_dir missing path argument\n"); + explicitCacheDir = true; + } + else if ( strcmp(arg, "-arch") == 0 ) { + archStr = argv[++i]; + archPair = archForString(archStr); // terminates if unknown + } + else if ( strcmp(arg, "-force") == 0 ) { + // ignore. always forced for iOS + } + else { + //usage(); + terminate("unknown option: %s\n", arg); + } + } + else { + //usage(); + terminate("unknown option: %s\n", arg); + } + } + + if (!rootPath) { + terminate("-root is a required option\n"); + } + if (!iPhoneOS) { + terminate("-iPhone is a required option\n"); + } + if (!cacheDir) { + terminate("-cache_dir is a required option\n"); + } + if (!dylibListFile) { + terminate("-dylib_list is a required option\n"); + } + if (!archStr) { + terminate("-arch is a required option\n"); + } + + char prodCachePath[MAXPATHLEN]; + char devCachePath[MAXPATHLEN]; + strcpy(prodCachePath, cacheDir); + if ( prodCachePath[strlen(prodCachePath)-1] != '/' ) + strcat(prodCachePath, "/"); + strcat(prodCachePath, "dyld_shared_cache_"); + strcat(prodCachePath, archStr); + strcpy(devCachePath, prodCachePath); + strcat(devCachePath, ".development"); + + verboseLog("developement cache path = %s\n", devCachePath); + verboseLog("cache path = %s\n", prodCachePath); + + // create cache dirs if needed + char neededDirs[1024]; + strcpy(neededDirs, prodCachePath); + char* lastSlash = strrchr(neededDirs, '/'); + if ( lastSlash != NULL ) + lastSlash[1] = '\0'; + struct stat stat_buf; + if ( stat(neededDirs, &stat_buf) != 0 ) { + const char* afterSlash = &neededDirs[1]; + char* slash; + while ( (slash = strchr(afterSlash, '/')) != NULL ) { + *slash = '\0'; + ::mkdir(neededDirs, S_IRWXU | S_IRGRP|S_IXGRP | S_IROTH|S_IXOTH); + *slash = '/'; + afterSlash = slash+1; + } + } + + std::string dylibOrderFile = toolDir() + "/dylib-order.txt"; + std::string dirtyDataOrderFile = toolDir() + "/dirty-data-segments-order.txt"; + + std::unordered_map> dependents; + SharedCache devCache(rootPath, searchPaths, dylibListFile, archPair, dylibOrderFile, dirtyDataOrderFile); + + if ( devCache.fileSize() == 0 ) + terminate("Could not find all necessary dylibs\n"); + + devCache.buildUnoptimizedCache(); + + SharedCache prodCache = devCache; + + prodCache.optimizeForProduction(); + devCache.optimizeForDevelopment(); + + verboseLog("developement cache size = %llu", devCache.fileSize()); + verboseLog("developement cache vm size = %llu", devCache.vmSize()); + + // optimize cache + write_cache(devCachePath, devCache); + if ( devCache.vmSize()+align(devCache.vmSize()/200, sharedRegionRegionAlignment(archPair)) > sharedRegionRegionSize(archPair)) { + terminate("update_dyld_shared_cache[%u] for arch=%s, shared cache will not fit in shared regions address space. Overflow amount: %llu\n", + getpid(), archStr, devCache.vmSize()+align(devCache.vmSize()/200, sharedRegionRegionAlignment(archPair)) - sharedRegionRegionSize(archPair)); + } + + write_cache(prodCachePath, prodCache); + + return 0; +} + + +void uniqueWarning(const char* format, ...) +{ + va_list list; + va_start(list, format); + vfprintf(stderr, format, list); + va_end(list); + fprintf(stderr, "\n"); +} + +void log(const char * __restrict format, ...) +{ + va_list list; + va_start(list, format); + vfprintf(stderr, format, list); + va_end(list); + fprintf(stderr, "\n"); +} +void verboseLog(const char* format, ...) +{ + va_list list; + va_start(list, format); + vfprintf(stderr, format, list); + va_end(list); + fprintf(stderr, "\n"); +} + +void warning(const char* format, ...) +{ + fprintf(stderr, "update_dyld_shared_cache warning: "); + va_list list; + va_start(list, format); + char* msg; + vasprintf(&msg, format, list); + va_end(list); + fprintf(stderr, "%s\n", msg); + sWarnings.push_back(std::unique_ptr(msg)); +} + +void terminate(const char* format, ...) +{ + fprintf(stderr, "update_dyld_shared_cache error: "); + va_list list; + va_start(list, format); + vfprintf(stderr, format, list); + va_end(list); + exit(1); +} diff --git a/src/dyld-apple/launch-cache/Architectures.hpp b/src/dyld-apple/launch-cache/Architectures.hpp new file mode 100644 index 000000000..fe3eea443 --- /dev/null +++ b/src/dyld-apple/launch-cache/Architectures.hpp @@ -0,0 +1,62 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2005-2011 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@ + */ + +#ifndef __ARCHITECTURES__ +#define __ARCHITECTURES__ + +#include "FileAbstraction.hpp" + + +// +// Architectures +// +struct x86 +{ + typedef Pointer32 P; + +}; + +struct x86_64 +{ + typedef Pointer64 P; +}; + +struct arm +{ + typedef Pointer32 P; + +}; + +struct arm64 +{ + typedef Pointer64 P; + +}; + + + + +#endif // __ARCHITECTURES__ + + diff --git a/src/dyld-apple/launch-cache/CacheFileAbstraction.hpp b/src/dyld-apple/launch-cache/CacheFileAbstraction.hpp new file mode 100644 index 000000000..b2dddbce4 --- /dev/null +++ b/src/dyld-apple/launch-cache/CacheFileAbstraction.hpp @@ -0,0 +1,416 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, 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@ + */ +#ifndef __DYLD_CACHE_ABSTRACTION__ +#define __DYLD_CACHE_ABSTRACTION__ + +#include "dyld_cache_format.h" + +#include "FileAbstraction.hpp" +#include "Architectures.hpp" + +template +class dyldCacheHeader { +public: + const char* magic() const INLINE { return fields.magic; } + void set_magic(const char* value) INLINE { memcpy(fields.magic, value, 16); } + + uint32_t mappingOffset() const INLINE { return E::get32(fields.mappingOffset); } + void set_mappingOffset(uint32_t value) INLINE { E::set32(fields.mappingOffset, value); } + + uint32_t mappingCount() const INLINE { return E::get32(fields.mappingCount); } + void set_mappingCount(uint32_t value) INLINE { E::set32(fields.mappingCount, value); } + + uint32_t imagesOffset() const INLINE { return E::get32(fields.imagesOffset); } + void set_imagesOffset(uint32_t value) INLINE { E::set32(fields.imagesOffset, value); } + + uint32_t imagesCount() const INLINE { return E::get32(fields.imagesCount); } + void set_imagesCount(uint32_t value) INLINE { E::set32(fields.imagesCount, value); } + + uint64_t dyldBaseAddress() const INLINE { return E::get64(fields.dyldBaseAddress); } + void set_dyldBaseAddress(uint64_t value) INLINE { E::set64(fields.dyldBaseAddress, value); } + + uint64_t codeSignatureOffset() const INLINE { return E::get64(fields.codeSignatureOffset); } + void set_codeSignatureOffset(uint64_t value) INLINE { E::set64(fields.codeSignatureOffset, value); } + + uint64_t codeSignatureSize() const INLINE { return E::get64(fields.codeSignatureSize); } + void set_codeSignatureSize(uint64_t value) INLINE { E::set64(fields.codeSignatureSize, value); } + + uint64_t slideInfoOffset() const INLINE { return E::get64(fields.slideInfoOffset); } + void set_slideInfoOffset(uint64_t value) INLINE { E::set64(fields.slideInfoOffset, value); } + + uint64_t slideInfoSize() const INLINE { return E::get64(fields.slideInfoSize); } + void set_slideInfoSize(uint64_t value) INLINE { E::set64(fields.slideInfoSize, value); } + + uint64_t localSymbolsOffset() const INLINE { return E::get64(fields.localSymbolsOffset); } + void set_localSymbolsOffset(uint64_t value) INLINE { E::set64(fields.localSymbolsOffset, value); } + + uint64_t localSymbolsSize() const INLINE { return E::get64(fields.localSymbolsSize); } + void set_localSymbolsSize(uint64_t value) INLINE { E::set64(fields.localSymbolsSize, value); } + + const uint8_t* uuid() const INLINE { return fields.uuid; } + void set_uuid(const uint8_t value[16]) INLINE { memcpy(fields.uuid, value, 16); } + + uint64_t cacheType() const INLINE { return E::get64(fields.cacheType); } + void set_cacheType(uint64_t value) INLINE { E::set64(fields.cacheType, value); } + + uint32_t branchPoolsOffset() const INLINE { return E::get32(fields.branchPoolsOffset); } + void set_branchPoolsOffset(uint32_t value) INLINE { E::set32(fields.branchPoolsOffset, value); } + + uint32_t branchPoolsCount() const INLINE { return E::get32(fields.branchPoolsCount); } + void set_branchPoolsCount(uint32_t value) INLINE { E::set32(fields.branchPoolsCount, value); } + + uint64_t accelerateInfoAddr() const INLINE { return E::get64(fields.accelerateInfoAddr); } + void set_accelerateInfoAddr(uint64_t value) INLINE { E::set64(fields.accelerateInfoAddr, value); } + + uint64_t accelerateInfoSize() const INLINE { return E::get64(fields.accelerateInfoSize); } + void set_accelerateInfoSize(uint64_t value) INLINE { E::set64(fields.accelerateInfoSize, value); } + + uint64_t imagesTextOffset() const INLINE { return E::get64(fields.imagesTextOffset); } + void set_imagesTextOffset(uint64_t value) INLINE { E::set64(fields.imagesTextOffset, value); } + + uint64_t imagesTextCount() const INLINE { return E::get64(fields.imagesTextCount); } + void set_imagesTextCount(uint64_t value) INLINE { E::set64(fields.imagesTextCount, value); } + +private: + dyld_cache_header fields; +}; + + +template +class dyldCacheFileMapping { +public: + uint64_t address() const INLINE { return E::get64(fields.address); } + void set_address(uint64_t value) INLINE { E::set64(fields.address, value); } + + uint64_t size() const INLINE { return E::get64(fields.size); } + void set_size(uint64_t value) INLINE { E::set64(fields.size, value); } + + uint64_t file_offset() const INLINE { return E::get64(fields.fileOffset); } + void set_file_offset(uint64_t value) INLINE { E::set64(fields.fileOffset, value); } + + uint32_t max_prot() const INLINE { return E::get32(fields.maxProt); } + void set_max_prot(uint32_t value) INLINE { E::set32((uint32_t&)fields.maxProt, value); } + + uint32_t init_prot() const INLINE { return E::get32(fields.initProt); } + void set_init_prot(uint32_t value) INLINE { E::set32((uint32_t&)fields.initProt, value); } + +private: + dyld_cache_mapping_info fields; +}; + + +template +class dyldCacheImageInfo { +public: + uint64_t address() const INLINE { return E::get64(fields.address); } + void set_address(uint64_t value) INLINE { E::set64(fields.address, value); } + + uint64_t modTime() const INLINE { return E::get64(fields.modTime); } + void set_modTime(uint64_t value) INLINE { E::set64(fields.modTime, value); } + + uint64_t inode() const INLINE { return E::get64(fields.inode); } + void set_inode(uint64_t value) INLINE { E::set64(fields.inode, value); } + + uint32_t pathFileOffset() const INLINE { return E::get32(fields.pathFileOffset); } + void set_pathFileOffset(uint32_t value) INLINE { E::set32(fields.pathFileOffset, value); fields.pad=0; } + +private: + dyld_cache_image_info fields; +}; + +template +class dyldCacheImageTextInfo { +public: + const uint8_t* uuid() const INLINE { return fields.uuid; } + void set_uuid(const uint8_t value[16]) INLINE { memcpy(fields.uuid, value, 16); } + + uint64_t loadAddress() const INLINE { return E::get64(fields.loadAddress); } + void set_loadAddress(uint64_t value) INLINE { E::set64(fields.loadAddress, value); } + + uint32_t textSegmentSize() const INLINE { return E::get32(fields.textSegmentSize); } + void set_textSegmentSize(uint32_t value) INLINE { E::set32(fields.textSegmentSize, value); } + + uint32_t pathOffset() const INLINE { return E::get32(fields.pathOffset); } + void set_pathOffset(uint32_t value) INLINE { E::set32(fields.pathOffset, value); } + +private: + dyld_cache_image_text_info fields; +}; + + + +template +class dyldCacheImageInfoExtra { +public: + uint64_t exportsTrieAddr() const INLINE { return E::get64(fields.exportsTrieAddr); } + void set_exportsTrieAddr(uint64_t value) INLINE { E::set64(fields.exportsTrieAddr, value); } + + uint64_t weakBindingsAddr() const INLINE { return E::get64(fields.weakBindingsAddr); } + void set_weakBindingsAddr(uint64_t value) INLINE { E::set64(fields.weakBindingsAddr, value); } + + uint32_t exportsTrieSize() const INLINE { return E::get32(fields.exportsTrieSize); } + void set_exportsTrieSize(uint32_t value) INLINE { E::set32(fields.exportsTrieSize, value); } + + uint32_t weakBindingsSize() const INLINE { return E::get32(fields.weakBindingsSize); } + void set_weakBindingsSize(uint32_t value) INLINE { E::set32(fields.weakBindingsSize, value); } + + uint32_t dependentsStartArrayIndex() const INLINE { return E::get32(fields.dependentsStartArrayIndex); } + void set_dependentsStartArrayIndex(uint32_t value) INLINE { E::set32(fields.dependentsStartArrayIndex, value); } + + uint32_t reExportsStartArrayIndex() const INLINE { return E::get32(fields.reExportsStartArrayIndex); } + void set_reExportsStartArrayIndex(uint32_t value) INLINE { E::set32(fields.reExportsStartArrayIndex, value); } + +private: + dyld_cache_image_info_extra fields; +}; + + +template +class dyldCacheAcceleratorInfo { +public: + uint32_t version() const INLINE { return E::get32(fields.version); } + void set_version(uint32_t value) INLINE { E::set32(fields.version, value); } + + uint32_t imageExtrasCount() const INLINE { return E::get32(fields.imageExtrasCount); } + void set_imageExtrasCount(uint32_t value) INLINE { E::set32(fields.imageExtrasCount, value); } + + uint32_t imagesExtrasOffset() const INLINE { return E::get32(fields.imagesExtrasOffset); } + void set_imagesExtrasOffset(uint32_t value) INLINE { E::set32(fields.imagesExtrasOffset, value); } + + uint32_t bottomUpListOffset() const INLINE { return E::get32(fields.bottomUpListOffset); } + void set_bottomUpListOffset(uint32_t value) INLINE { E::set32(fields.bottomUpListOffset, value); } + + uint32_t dylibTrieOffset() const INLINE { return E::get32(fields.dylibTrieOffset); } + void set_dylibTrieOffset(uint32_t value) INLINE { E::set32(fields.dylibTrieOffset, value); } + + uint32_t dylibTrieSize() const INLINE { return E::get32(fields.dylibTrieSize); } + void set_dylibTrieSize(uint32_t value) INLINE { E::set32(fields.dylibTrieSize, value); } + + uint32_t initializersOffset() const INLINE { return E::get32(fields.initializersOffset); } + void set_initializersOffset(uint32_t value) INLINE { E::set32(fields.initializersOffset, value); } + + uint32_t initializersCount() const INLINE { return E::get32(fields.initializersCount); } + void set_initializersCount(uint32_t value) INLINE { E::set32(fields.initializersCount, value); } + + uint32_t dofSectionsOffset() const INLINE { return E::get32(fields.dofSectionsOffset); } + void set_dofSectionsOffset(uint32_t value) INLINE { E::set32(fields.dofSectionsOffset, value); } + + uint32_t dofSectionsCount() const INLINE { return E::get32(fields.dofSectionsCount); } + void set_dofSectionsCount(uint32_t value) INLINE { E::set32(fields.dofSectionsCount, value); } + + uint32_t reExportListOffset() const INLINE { return E::get32(fields.reExportListOffset); } + void set_reExportListOffset(uint32_t value) INLINE { E::set32(fields.reExportListOffset, value); } + + uint32_t reExportCount() const INLINE { return E::get32(fields.reExportCount); } + void set_reExportCount(uint32_t value) INLINE { E::set32(fields.reExportCount, value); } + + uint32_t depListOffset() const INLINE { return E::get32(fields.depListOffset); } + void set_depListOffset(uint32_t value) INLINE { E::set32(fields.depListOffset, value); } + + uint32_t depListCount() const INLINE { return E::get32(fields.depListCount); } + void set_depListCount(uint32_t value) INLINE { E::set32(fields.depListCount, value); } + + uint32_t rangeTableOffset() const INLINE { return E::get32(fields.rangeTableOffset); } + void set_rangeTableOffset(uint32_t value) INLINE { E::set32(fields.rangeTableOffset, value); } + + uint32_t rangeTableCount() const INLINE { return E::get32(fields.rangeTableCount); } + void set_rangeTableCount(uint32_t value) INLINE { E::set32(fields.rangeTableCount, value); } + + uint64_t dyldSectionAddr() const INLINE { return E::get64(fields.dyldSectionAddr); } + void set_dyldSectionAddr(uint64_t value) INLINE { E::set64(fields.dyldSectionAddr, value); } + + +private: + dyld_cache_accelerator_info fields; +}; + + +template +class dyldCacheAcceleratorInitializer { +public: + uint32_t functionOffset() const INLINE { return E::get32(fields.functionOffset); } + void set_functionOffset(uint32_t value) INLINE { E::set32(fields.functionOffset, value); } + + uint32_t imageIndex() const INLINE { return E::get32(fields.imageIndex); } + void set_imageIndex(uint32_t value) INLINE { E::set32(fields.imageIndex, value); } + +private: + dyld_cache_accelerator_initializer fields; +}; + + +template +class dyldCacheAcceleratorRangeEntry { +public: + uint64_t startAddress() const INLINE { return E::get64(fields.startAddress); } + void set_startAddress(uint64_t value) INLINE { E::set64(fields.startAddress, value); } + + uint32_t size() const INLINE { return E::get32(fields.size); } + void set_size(uint32_t value) INLINE { E::set32(fields.size, value); } + + uint32_t imageIndex() const INLINE { return E::get32(fields.imageIndex); } + void set_imageIndex(uint32_t value) INLINE { E::set32(fields.imageIndex, value); } + +private: + dyld_cache_range_entry fields; +}; + +template +class dyldCacheAcceleratorDOFEntry { +public: + uint64_t sectionAddress() const INLINE { return E::get64(fields.sectionAddress); } + void set_sectionAddress(uint64_t value) INLINE { E::set64(fields.sectionAddress, value); } + + uint32_t sectionSize() const INLINE { return E::get32(fields.sectionSize); } + void set_sectionSize(uint32_t value) INLINE { E::set32(fields.sectionSize, value); } + + uint32_t imageIndex() const INLINE { return E::get32(fields.imageIndex); } + void set_imageIndex(uint32_t value) INLINE { E::set32(fields.imageIndex, value); } + +private: + dyld_cache_accelerator_dof fields; +}; + +template +class dyldCacheSlideInfo { +public: + uint32_t version() const INLINE { return E::get32(fields.version); } + void set_version(uint32_t value) INLINE { E::set32(fields.version, value); } + + uint32_t toc_offset() const INLINE { return E::get32(fields.toc_offset); } + void set_toc_offset(uint32_t value) INLINE { E::set32(fields.toc_offset, value); } + + uint32_t toc_count() const INLINE { return E::get32(fields.toc_count); } + void set_toc_count(uint32_t value) INLINE { E::set32(fields.toc_count, value); } + + uint32_t entries_offset() const INLINE { return E::get32(fields.entries_offset); } + void set_entries_offset(uint32_t value) INLINE { E::set32(fields.entries_offset, value); } + + uint32_t entries_count() const INLINE { return E::get32(fields.entries_count); } + void set_entries_count(uint32_t value) INLINE { E::set32(fields.entries_count, value); } + + uint32_t entries_size() const INLINE { return E::get32(fields.entries_size); } + void set_entries_size(uint32_t value) INLINE { E::set32(fields.entries_size, value); } + + uint16_t toc(unsigned index) const INLINE { return E::get16(((uint16_t*)(((uint8_t*)this)+E::get16(fields.toc_offset)))[index]); } + void set_toc(unsigned index, uint16_t value) INLINE { return E::set16(((uint16_t*)(((uint8_t*)this)+E::get16(fields.toc_offset)))[index], value); } + +private: + dyld_cache_slide_info fields; +}; + + +struct dyldCacheSlideInfoEntry { + uint8_t bits[4096/(8*4)]; // 128-byte bitmap +}; + + +template +class dyldCacheSlideInfo2 { +public: + uint32_t version() const INLINE { return E::get32(fields.version); } + void set_version(uint32_t value) INLINE { E::set32(fields.version, value); } + + uint32_t page_starts_offset() const INLINE { return E::get32(fields.page_starts_offset); } + void set_page_starts_offset(uint32_t value) INLINE { E::set32(fields.page_starts_offset, value); } + + uint32_t page_starts_count() const INLINE { return E::get32(fields.page_starts_count); } + void set_page_starts_count(uint32_t value) INLINE { E::set32(fields.page_starts_count, value); } + + uint32_t page_extras_offset() const INLINE { return E::get32(fields.page_extras_offset); } + void set_page_extras_offset(uint32_t value) INLINE { E::set32(fields.page_extras_offset, value); } + + uint32_t page_extras_count() const INLINE { return E::get32(fields.page_extras_count); } + void set_page_extras_count(uint32_t value) INLINE { E::set32(fields.page_extras_count, value); } + + uint32_t page_size() const INLINE { return E::get32(fields.page_size); } + void set_page_size(uint32_t value) INLINE { E::set32(fields.page_size, value); } + + uint64_t delta_mask() const INLINE { return E::get64(fields.delta_mask); } + void set_delta_mask(uint64_t value) INLINE { E::set64(fields.delta_mask, value); } + + uint64_t value_add() const INLINE { return E::get64(fields.value_add); } + void set_value_add(uint64_t value) INLINE { E::set64(fields.value_add, value); } + + uint16_t page_starts(unsigned index) const INLINE { return E::get16(((uint16_t*)(((uint8_t*)this)+E::get16(fields.page_starts_offset)))[index]); } + void set_page_starts(unsigned index, uint16_t value) INLINE { return E::set16(((uint16_t*)(((uint8_t*)this)+E::get16(fields.page_starts_offset)))[index], value); } + + uint16_t page_extras(unsigned index) const INLINE { return E::get16(((uint16_t*)(((uint8_t*)this)+E::get16(fields.page_extras_offset)))[index]); } + void set_page_extras(unsigned index, uint16_t value) INLINE { return E::set16(((uint16_t*)(((uint8_t*)this)+E::get16(fields.page_extras_offset)))[index], value); } + + +private: + dyld_cache_slide_info2 fields; +}; + + + +template +class dyldCacheLocalSymbolsInfo { +public: + uint32_t nlistOffset() const INLINE { return E::get32(fields.nlistOffset); } + void set_nlistOffset(uint32_t value) INLINE { E::set32(fields.nlistOffset, value); } + + uint32_t nlistCount() const INLINE { return E::get32(fields.nlistCount); } + void set_nlistCount(uint32_t value) INLINE { E::set32(fields.nlistCount, value); } + + uint32_t stringsOffset() const INLINE { return E::get32(fields.stringsOffset); } + void set_stringsOffset(uint32_t value) INLINE { E::set32(fields.stringsOffset, value); } + + uint32_t stringsSize() const INLINE { return E::get32(fields.stringsSize); } + void set_stringsSize(uint32_t value) INLINE { E::set32(fields.stringsSize, value); } + + uint32_t entriesOffset() const INLINE { return E::get32(fields.entriesOffset); } + void set_entriesOffset(uint32_t value) INLINE { E::set32(fields.entriesOffset, value); } + + uint32_t entriesCount() const INLINE { return E::get32(fields.entriesCount); } + void set_entriesCount(uint32_t value) INLINE { E::set32(fields.entriesCount, value); } + +private: + dyld_cache_local_symbols_info fields; +}; + + +template +class dyldCacheLocalSymbolEntry { +public: + uint32_t dylibOffset() const INLINE { return E::get32(fields.dylibOffset); } + void set_dylibOffset(uint32_t value) INLINE { E::set32(fields.dylibOffset, value); } + + uint32_t nlistStartIndex() const INLINE { return E::get32(fields.nlistStartIndex); } + void set_nlistStartIndex(uint32_t value) INLINE { E::set32(fields.nlistStartIndex, value); } + + uint32_t nlistCount() const INLINE { return E::get32(fields.nlistCount); } + void set_nlistCount(uint32_t value) INLINE { E::set32(fields.nlistCount, value); } + +private: + dyld_cache_local_symbols_entry fields; +}; + + + + +#endif // __DYLD_CACHE_ABSTRACTION__ + + diff --git a/src/dyld-apple/launch-cache/FileAbstraction.hpp b/src/dyld-apple/launch-cache/FileAbstraction.hpp new file mode 100644 index 000000000..1d73d74de --- /dev/null +++ b/src/dyld-apple/launch-cache/FileAbstraction.hpp @@ -0,0 +1,163 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2005 Apple Computer, 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@ + */ +#ifndef __FILE_ABSTRACTION__ +#define __FILE_ABSTRACTION__ + + +#include +#include +#include + +#ifdef __OPTIMIZE__ +#define INLINE __attribute__((always_inline)) +#else +#define INLINE +#endif + +// +// This abstraction layer is for use with file formats that have 64-bit/32-bit and Big-Endian/Little-Endian variants +// +// For example: to make a utility that handles 32-bit little enidan files use: Pointer32 +// +// +// get16() read a 16-bit number from an E endian struct +// set16() write a 16-bit number to an E endian struct +// get32() read a 32-bit number from an E endian struct +// set32() write a 32-bit number to an E endian struct +// get64() read a 64-bit number from an E endian struct +// set64() write a 64-bit number to an E endian struct +// +// getBits() read a bit field from an E endian struct (bitCount=number of bits in field, firstBit=bit index of field) +// setBits() write a bit field to an E endian struct (bitCount=number of bits in field, firstBit=bit index of field) +// +// getBitsRaw() read a bit field from a struct with native endianness +// setBitsRaw() write a bit field from a struct with native endianness +// + +class BigEndian +{ +public: + static uint16_t get16(const uint16_t& from) INLINE { return OSReadBigInt16(&from, 0); } + static void set16(uint16_t& into, uint16_t value) INLINE { OSWriteBigInt16(&into, 0, value); } + + static uint32_t get32(const uint32_t& from) INLINE { return OSReadBigInt32(&from, 0); } + static void set32(uint32_t& into, uint32_t value) INLINE { OSWriteBigInt32(&into, 0, value); } + + static int32_t get32(const int32_t& from) INLINE { return OSReadBigInt32(&from, 0); } + static void set32(int32_t& into, int32_t value) INLINE { OSWriteBigInt32(&into, 0, value); } + + static uint64_t get64(const uint64_t& from) INLINE { return OSReadBigInt64(&from, 0); } + static void set64(uint64_t& into, uint64_t value) INLINE { OSWriteBigInt64(&into, 0, value); } + + static uint32_t getBits(const uint32_t& from, + uint8_t firstBit, uint8_t bitCount) INLINE { return getBitsRaw(get32(from), firstBit, bitCount); } + static void setBits(uint32_t& into, uint32_t value, + uint8_t firstBit, uint8_t bitCount) INLINE { uint32_t temp = get32(into); setBitsRaw(temp, value, firstBit, bitCount); set32(into, temp); } + + static uint32_t getBitsRaw(const uint32_t& from, + uint8_t firstBit, uint8_t bitCount) INLINE { return ((from >> (32-firstBit-bitCount)) & ((1<> firstBit) & ((1< +class Pointer32 +{ +public: + typedef uint32_t uint_t; + typedef _E E; + + static uint64_t getP(const uint_t& from) INLINE { return _E::get32(from); } + static void setP(uint_t& into, uint64_t value) INLINE { _E::set32(into, (uint32_t)value); } + + // Round to a P-size boundary + template + static T round_up(T value) { return (value+3) & ~(T)3; } + template + static T round_down(T value) { return value & ~(T)3; } +}; + + +template +class Pointer64 +{ +public: + typedef uint64_t uint_t; + typedef _E E; + + static uint64_t getP(const uint_t& from) INLINE { return _E::get64(from); } + static void setP(uint_t& into, uint64_t value) INLINE { _E::set64(into, value); } + + // Round to a P-size boundary + template + static T round_up(T value) { return (value+7) & ~(T)7; } + template + static T round_down(T value) { return value & ~(T)7; } +}; + + + + + +#endif // __FILE_ABSTRACTION__ + + diff --git a/src/dyld-apple/launch-cache/MachOBinder.hpp b/src/dyld-apple/launch-cache/MachOBinder.hpp new file mode 100644 index 000000000..d87fe233b --- /dev/null +++ b/src/dyld-apple/launch-cache/MachOBinder.hpp @@ -0,0 +1,931 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006-2011 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@ + */ + +#ifndef __MACHO_BINDER__ +#define __MACHO_BINDER__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "MachOFileAbstraction.hpp" +#include "Architectures.hpp" +#include "MachOLayout.hpp" +#include "MachORebaser.hpp" +#include "MachOTrie.hpp" + +#ifndef EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER + #define EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER 0x10 +#endif + +#ifndef EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE + #define EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE 0x02 +#endif + + +template +class Binder : public Rebaser +{ +public: + class CStringHash { + public: + size_t operator()(const char* __s) const { + size_t __h = 0; + for ( ; *__s; ++__s) + __h = 5 * __h + *__s; + return __h; + }; + }; + struct CStringEquals { + bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } + }; + typedef std::unordered_map*, CStringHash, CStringEquals> Map; + + + Binder(const MachOLayoutAbstraction&); + virtual ~Binder() {} + + const char* getDylibID() const; + void setDependentBinders(const Map& map); + void bind(std::vector&); + void optimize(); + void addResolverClient(Binder* clientDylib, const char* symbolName); +private: + typedef typename A::P P; + typedef typename A::P::E E; + typedef typename A::P::uint_t pint_t; + struct BinderAndReExportFlag { Binder* binder; bool reExport; }; + struct SymbolReExport { const char* exportName; int dylibOrdinal; const char* importName; }; + typedef std::unordered_map NameToAddrMap; + typedef std::unordered_set NameSet; + typedef std::unordered_map*>, CStringHash, CStringEquals> ResolverClientsMap; + + + static bool isPublicLocation(const char* pth); + void hoistPrivateRexports(); + int ordinalOfDependentBinder(Binder* dep); + void doBindDyldInfo(std::vector& pointersInData); + void doBindDyldLazyInfo(std::vector& pointersInData); + void bindDyldInfoAt(uint8_t segmentIndex, uint64_t segmentOffset, uint8_t type, + int libraryOrdinal, int64_t addend, + const char* symbolName, bool lazyPointer, bool weakImport, + std::vector& pointersInData); + bool findExportedSymbolAddress(const char* name, pint_t* result, Binder** foundIn, + bool* isResolverSymbol, bool* isAbsolute); + const char* parentUmbrella(); + pint_t runtimeAddressFromNList(const macho_nlist

* sym); + void switchStubToUseSharedLazyPointer(const char* symbolName, pint_t lpVMAddr); + void switchStubsLazyPointer(uint8_t* stubMappedAddress, pint_t stubVMAddress, uint32_t stubSize, pint_t lpVMAddr); + pint_t findLazyPointerFor(const char* symbolName); + void shareLazyPointersToResolvers(); + + + static uint8_t pointerRelocSize(); + static uint8_t pointerRelocType(); + + std::vector fDependentDylibs; + NameToAddrMap fHashTable; + NameSet fSymbolResolvers; + NameSet fAbsoluteSymbols; + std::vector fReExportedSymbols; + const macho_nlist

* fSymbolTable; + const char* fStrings; + const macho_dysymtab_command

* fDynamicInfo; + const macho_segment_command

* fFristWritableSegment; + const macho_dylib_command

* fDylibID; + const macho_dylib_command

* fParentUmbrella; + const macho_dyld_info_command

* fDyldInfo; + bool fOriginallyPrebound; + bool fReExportedSymbolsResolved; + ResolverClientsMap fResolverInfo; +}; + +template <> +uint32_t Binder::runtimeAddressFromNList(const macho_nlist >* sym) +{ + if (sym->n_desc() & N_ARM_THUMB_DEF) + return sym->n_value() + 1; + else + return sym->n_value(); +} + +template +typename A::P::uint_t Binder::runtimeAddressFromNList(const macho_nlist

* sym) +{ + return sym->n_value(); +} + + +template +Binder::Binder(const MachOLayoutAbstraction& layout) + : Rebaser(layout), + fSymbolTable(NULL), fStrings(NULL), fDynamicInfo(NULL), + fFristWritableSegment(NULL), fDylibID(NULL), fDyldInfo(NULL), + fParentUmbrella(NULL), fReExportedSymbolsResolved(false) +{ + fOriginallyPrebound = ((this->fHeader->flags() & MH_PREBOUND) != 0); + // update header flags so the cache looks prebound split-seg (0x80000000 is in-shared-cache bit) + ((macho_header

*)this->fHeader)->set_flags(this->fHeader->flags() | MH_PREBOUND | MH_SPLIT_SEGS | 0x80000000); + + // calculate fDynamicInfo, fStrings, fSymbolTable + const macho_symtab_command

* symtab; + const macho_load_command

* const cmds = (macho_load_command

*)((uint8_t*)this->fHeader + sizeof(macho_header

)); + const uint32_t cmd_count = this->fHeader->ncmds(); + const macho_load_command

* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd()) { + case LC_SYMTAB: + symtab = (macho_symtab_command

*)cmd; + fSymbolTable = (macho_nlist

*)(&this->fLinkEditBase[symtab->symoff()]); + fStrings = (const char*)&this->fLinkEditBase[symtab->stroff()]; + break; + case LC_DYSYMTAB: + fDynamicInfo = (macho_dysymtab_command

*)cmd; + break; + case LC_ID_DYLIB: + ((macho_dylib_command

*)cmd)->set_timestamp(0); + fDylibID = (macho_dylib_command

*)cmd; + break; + case LC_LOAD_DYLIB: + case LC_LOAD_WEAK_DYLIB: + case LC_REEXPORT_DYLIB: + case LC_LOAD_UPWARD_DYLIB: + ((macho_dylib_command

*)cmd)->set_timestamp(0); + break; + case LC_SUB_FRAMEWORK: + fParentUmbrella = (macho_dylib_command

*)cmd; + break; + case LC_DYLD_INFO: + case LC_DYLD_INFO_ONLY: + fDyldInfo = (macho_dyld_info_command

*)cmd; + break; + case LC_RPATH: + fprintf(stderr, "update_dyld_shared_cache: warning: dyld shared cache does not support LC_RPATH found in %s\n", layout.getFilePath()); + break; + default: + if ( cmd->cmd() & LC_REQ_DYLD ) + throwf("unknown required load command 0x%08X", cmd->cmd()); + } + cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); + } + if ( fDynamicInfo == NULL ) + throw "no LC_DYSYMTAB"; + if ( fSymbolTable == NULL ) + throw "no LC_SYMTAB"; + if ( fDyldInfo == NULL ) + throw "no LC_DYLD_INFO"; + // build hash table + //fprintf(stderr, "exports for %s\n", layout.getFilePath()); + std::vector exports; + const uint8_t* exportsStart = layout.getDyldInfoExports(); + const uint8_t* exportsEnd = &exportsStart[fDyldInfo->export_size()]; + mach_o::trie::parseTrie(exportsStart, exportsEnd, exports); + pint_t baseAddress = layout.getSegments()[0].newAddress(); + for(std::vector::iterator it = exports.begin(); it != exports.end(); ++it) { + switch ( it->flags & EXPORT_SYMBOL_FLAGS_KIND_MASK ) { + case EXPORT_SYMBOL_FLAGS_KIND_REGULAR: + if ( (it->flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER) ) { + fSymbolResolvers.insert(it->name); + } + if ( it->flags & EXPORT_SYMBOL_FLAGS_REEXPORT ) { + //fprintf(stderr, "found re-export %s in %s\n", sym.exportName, this->getDylibID()); + SymbolReExport sym; + sym.exportName = it->name; + sym.dylibOrdinal = it->other; + sym.importName = it->importName; + if ( (sym.importName == NULL) || (sym.importName[0] == '\0') ) + sym.importName = sym.exportName; + fReExportedSymbols.push_back(sym); + // fHashTable entry will be added in first call to findExportedSymbolAddress() + } + else { + fHashTable[it->name] = it->address + baseAddress; + } + break; + case EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL: + fHashTable[it->name] = it->address + baseAddress; + break; + case EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE: + fHashTable[it->name] = it->address; + fAbsoluteSymbols.insert(it->name); + break; + default: + throwf("non-regular symbol binding not supported for %s in %s", it->name, layout.getFilePath()); + break; + } + //fprintf(stderr, "0x%08llX %s\n", it->address + baseAddress, it->name); + } +} + +template <> uint8_t Binder::pointerRelocSize() { return 2; } +template <> uint8_t Binder::pointerRelocSize() { return 3; } +template <> uint8_t Binder::pointerRelocSize() { return 2; } +template <> uint8_t Binder::pointerRelocSize() { return 3; } + +template <> uint8_t Binder::pointerRelocType() { return GENERIC_RELOC_VANILLA; } +template <> uint8_t Binder::pointerRelocType() { return X86_64_RELOC_UNSIGNED; } +template <> uint8_t Binder::pointerRelocType() { return ARM_RELOC_VANILLA; } +template <> uint8_t Binder::pointerRelocType() { return ARM64_RELOC_UNSIGNED; } + + +template +const char* Binder::getDylibID() const +{ + if ( fDylibID != NULL ) + return fDylibID->name(); + else + return NULL; +} + +template +const char* Binder::parentUmbrella() +{ + if ( fParentUmbrella != NULL ) + return fParentUmbrella->name(); + else + return NULL; +} + + +template +bool Binder::isPublicLocation(const char* pth) +{ + // /usr/lib is a public location + if ( (strncmp(pth, "/usr/lib/", 9) == 0) && (strchr(&pth[9], '/') == NULL) ) + return true; + + // /System/Library/Frameworks/ is a public location + if ( strncmp(pth, "/System/Library/Frameworks/", 27) == 0 ) { + const char* frameworkDot = strchr(&pth[27], '.'); + // but only top level framework + // /System/Library/Frameworks/Foo.framework/Versions/A/Foo ==> true + // /System/Library/Frameworks/Foo.framework/Resources/libBar.dylib ==> false + // /System/Library/Frameworks/Foo.framework/Frameworks/Bar.framework/Bar ==> false + // /System/Library/Frameworks/Foo.framework/Frameworks/Xfoo.framework/XFoo ==> false + if ( frameworkDot != NULL ) { + int frameworkNameLen = frameworkDot - &pth[27]; + if ( strncmp(&pth[strlen(pth)-frameworkNameLen-1], &pth[26], frameworkNameLen+1) == 0 ) + return true; + } + } + + return false; +} + +template +void Binder::setDependentBinders(const Map& map) +{ + // build vector of dependent dylibs + const macho_load_command

* const cmds = (macho_load_command

*)((uint8_t*)this->fHeader + sizeof(macho_header

)); + const uint32_t cmd_count = this->fHeader->ncmds(); + const macho_load_command

* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd()) { + case LC_LOAD_DYLIB: + case LC_LOAD_WEAK_DYLIB: + case LC_REEXPORT_DYLIB: + case LC_LOAD_UPWARD_DYLIB: + const char* path = ((struct macho_dylib_command

*)cmd)->name(); + typename Map::const_iterator pos = map.find(path); + if ( pos != map.end() ) { + BinderAndReExportFlag entry; + entry.binder = pos->second; + entry.reExport = ( cmd->cmd() == LC_REEXPORT_DYLIB ); + fDependentDylibs.push_back(entry); + } + else { + // the load command string does not match the install name of any loaded dylib + // this could happen if there was not a world build and some dylib changed its + // install path to be some symlinked path + + // use realpath() and walk map looking for a realpath match + bool found = false; + char targetPath[PATH_MAX]; + if ( realpath(path, targetPath) != NULL ) { + for(typename Map::const_iterator it=map.begin(); it != map.end(); ++it) { + char aPath[PATH_MAX]; + if ( realpath(it->first, aPath) != NULL ) { + if ( strcmp(targetPath, aPath) == 0 ) { + BinderAndReExportFlag entry; + entry.binder = it->second; + entry.reExport = ( cmd->cmd() == LC_REEXPORT_DYLIB ); + fDependentDylibs.push_back(entry); + found = true; + fprintf(stderr, "update_dyld_shared_cache: warning mismatched install path in %s for %s\n", + this->getDylibID(), path); + break; + } + } + } + } + if ( ! found ) { + if ( cmd->cmd() == LC_LOAD_WEAK_DYLIB ) { + BinderAndReExportFlag entry; + entry.binder = NULL; + entry.reExport = false; + fDependentDylibs.push_back(entry); + break; + } + else { + throwf("in %s can't find dylib %s", this->getDylibID(), path); + } + } + } + break; + } + cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); + } +} + +template +int Binder::ordinalOfDependentBinder(Binder* dep) +{ + for (int i=0; i < fDependentDylibs.size(); ++i) { + if ( fDependentDylibs[i].binder == dep ) + return i+1; + } + throw "dependend dylib not found"; +} + +template +void Binder::hoistPrivateRexports() +{ + std::vector*> privateReExportedDylibs; + for (typename std::vector::iterator it = fDependentDylibs.begin(); it != fDependentDylibs.end(); ++it) { + if ( it->reExport && ! isPublicLocation(it->binder->getDylibID()) ) + privateReExportedDylibs.push_back(it->binder); + } + if ( privateReExportedDylibs.size() != 0 ) { + // parse export info into vector of exports + const uint8_t* exportsStart = this->fLayout.getDyldInfoExports(); + const uint8_t* exportsEnd = &exportsStart[fDyldInfo->export_size()]; + std::vector exports; + mach_o::trie::parseTrie(exportsStart, exportsEnd, exports); + //fprintf(stderr, "%s exports %lu symbols from trie of size %u \n", this->fLayout.getFilePath(), exports.size(), fDyldInfo->export_size()); + + // add re-exports for each export from an re-exported dylib + for(typename std::vector*>::iterator it = privateReExportedDylibs.begin(); it != privateReExportedDylibs.end(); ++it) { + Binder* binder = *it; + int ordinal = ordinalOfDependentBinder(binder); + const uint8_t* aDylibsExportsStart = binder->fLayout.getDyldInfoExports(); + const uint8_t* aDylibsExportsEnd = &aDylibsExportsStart[binder->fDyldInfo->export_size()]; + std::vector aDylibsExports; + mach_o::trie::parseTrie(aDylibsExportsStart, aDylibsExportsEnd, aDylibsExports); + //fprintf(stderr, "%s re-exports %lu symbols from %s\n", this->fLayout.getFilePath(), aDylibsExports.size(), binder->getDylibID()); + for(std::vector::iterator eit = aDylibsExports.begin(); eit != aDylibsExports.end(); ++eit) { + mach_o::trie::Entry entry = *eit; + entry.flags |= EXPORT_SYMBOL_FLAGS_REEXPORT; + entry.other = ordinal; + entry.importName = NULL; + exports.push_back(entry); + } + } + // rebuild new combined trie + std::vector newExportTrieBytes; + newExportTrieBytes.reserve(fDyldInfo->export_size()); + mach_o::trie::makeTrie(exports, newExportTrieBytes); + //fprintf(stderr, "%s now exports %lu symbols from trie of size %lu\n", this->fLayout.getFilePath(), exports.size(), newExportTrieBytes.size()); + + // allocate new buffer and set export_off to use new buffer instead + uint32_t newExportsSize = newExportTrieBytes.size(); + uint8_t* sideTrie = new uint8_t[newExportsSize]; + memcpy(sideTrie, &newExportTrieBytes[0], newExportsSize); + this->fLayout.setDyldInfoExports(sideTrie); + ((macho_dyld_info_command

*)fDyldInfo)->set_export_off(0); // invalidate old trie + ((macho_dyld_info_command

*)fDyldInfo)->set_export_size(newExportsSize); + } +} + + +template +void Binder::bind(std::vector& pointersInData) +{ + this->doBindDyldInfo(pointersInData); + this->doBindDyldLazyInfo(pointersInData); + // weak bind info is processed at launch time +} + +template +void Binder::bindDyldInfoAt(uint8_t segmentIndex, uint64_t segmentOffset, uint8_t type, int libraryOrdinal, + int64_t addend, const char* symbolName, bool lazyPointer, bool weakImport, std::vector& pointersInData) +{ + //printf("%d 0x%08llX type=%d, lib=%d, addend=%lld, symbol=%s\n", segmentIndex, segmentOffset, type, libraryOrdinal, addend, symbolName); + const std::vector& segments = this->fLayout.getSegments(); + if ( segmentIndex > segments.size() ) + throw "bad segment index in rebase info"; + + if ( libraryOrdinal == BIND_SPECIAL_DYLIB_FLAT_LOOKUP ) + throw "dynamic lookup linkage not allowed in dyld shared cache"; + + if ( libraryOrdinal == BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE ) + throw "linkage to main executable not allowed in dyld shared cache"; + + if ( libraryOrdinal < 0 ) + throw "bad mach-o binary, special library ordinal not allowd in dyld shared cache"; + + if ( (unsigned)libraryOrdinal > fDependentDylibs.size() ) + throw "bad mach-o binary, library ordinal too big"; + + Binder* binder; + if ( libraryOrdinal == BIND_SPECIAL_DYLIB_SELF ) + binder = this; + else + binder = fDependentDylibs[libraryOrdinal-1].binder; + pint_t targetSymbolAddress; + bool isResolverSymbol = false; + bool isAbsolute = false; + Binder* foundIn; + if ( weakImport && (binder == NULL) ) { + targetSymbolAddress = 0; + foundIn = NULL; + } + else { + if ( ! binder->findExportedSymbolAddress(symbolName, &targetSymbolAddress, &foundIn, &isResolverSymbol, &isAbsolute) ) + throwf("could not bind symbol %s in %s expected in %s", symbolName, this->getDylibID(), binder->getDylibID()); + } + + // don't bind lazy pointers to resolver stubs in shared cache + if ( lazyPointer && isResolverSymbol ) { + if ( foundIn != this ) { + // record that this dylib has a lazy pointer to a resolver function + foundIn->addResolverClient(this, symbolName); + // fprintf(stderr, "have lazy pointer to resolver %s in %s\n", symbolName, this->getDylibID()); + } + return; + } + + // do actual update + const MachOLayoutAbstraction::Segment& seg = segments[segmentIndex]; + uint8_t* mappedAddr = (uint8_t*)seg.mappedAddress() + segmentOffset; + pint_t* mappedAddrP = (pint_t*)mappedAddr; + uint32_t* mappedAddr32 = (uint32_t*)mappedAddr; + int32_t svalue32new; + switch ( type ) { + case BIND_TYPE_POINTER: + P::setP(*mappedAddrP, targetSymbolAddress + addend); + break; + + case BIND_TYPE_TEXT_ABSOLUTE32: + E::set32(*mappedAddr32, targetSymbolAddress + addend); + break; + + case BIND_TYPE_TEXT_PCREL32: + svalue32new = seg.address() + segmentOffset + 4 - (targetSymbolAddress + addend); + E::set32(*mappedAddr32, svalue32new); + break; + + default: + throw "bad bind type"; + } + if ( !isAbsolute ) + pointersInData.push_back(mappedAddr); +} + + + +template +void Binder::doBindDyldLazyInfo(std::vector& pointersInData) +{ + const uint8_t* p = &this->fLinkEditBase[fDyldInfo->lazy_bind_off()]; + const uint8_t* end = &p[fDyldInfo->lazy_bind_size()]; + + uint8_t type = BIND_TYPE_POINTER; + uint64_t segmentOffset = 0; + uint8_t segmentIndex = 0; + const char* symbolName = NULL; + int libraryOrdinal = 0; + int64_t addend = 0; + bool weakImport = false; + while ( p < end ) { + uint8_t immediate = *p & BIND_IMMEDIATE_MASK; + uint8_t opcode = *p & BIND_OPCODE_MASK; + ++p; + switch (opcode) { + case BIND_OPCODE_DONE: + // this opcode marks the end of each lazy pointer binding + break; + case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM: + libraryOrdinal = immediate; + break; + case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB: + libraryOrdinal = read_uleb128(p, end); + break; + case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM: + // the special ordinals are negative numbers + if ( immediate == 0 ) + libraryOrdinal = 0; + else { + int8_t signExtended = BIND_OPCODE_MASK | immediate; + libraryOrdinal = signExtended; + } + break; + case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: + weakImport = ( (immediate & BIND_SYMBOL_FLAGS_WEAK_IMPORT) != 0 ); + symbolName = (char*)p; + while (*p != '\0') + ++p; + ++p; + break; + case BIND_OPCODE_SET_ADDEND_SLEB: + addend = read_sleb128(p, end); + break; + case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: + segmentIndex = immediate; + segmentOffset = read_uleb128(p, end); + break; + case BIND_OPCODE_DO_BIND: + bindDyldInfoAt(segmentIndex, segmentOffset, type, libraryOrdinal, addend, symbolName, true, weakImport, pointersInData); + segmentOffset += sizeof(pint_t); + break; + case BIND_OPCODE_SET_TYPE_IMM: + case BIND_OPCODE_ADD_ADDR_ULEB: + case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: + case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: + case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: + default: + throwf("bad lazy bind opcode %d", *p); + } + } + + +} + +template +void Binder::doBindDyldInfo(std::vector& pointersInData) +{ + const uint8_t* p = &this->fLinkEditBase[fDyldInfo->bind_off()]; + const uint8_t* end = &p[fDyldInfo->bind_size()]; + + uint8_t type = 0; + uint64_t segmentOffset = 0; + uint8_t segmentIndex = 0; + const char* symbolName = NULL; + int libraryOrdinal = 0; + int64_t addend = 0; + uint32_t count; + uint32_t skip; + bool weakImport = false; + bool done = false; + while ( !done && (p < end) ) { + uint8_t immediate = *p & BIND_IMMEDIATE_MASK; + uint8_t opcode = *p & BIND_OPCODE_MASK; + ++p; + switch (opcode) { + case BIND_OPCODE_DONE: + done = true; + break; + case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM: + libraryOrdinal = immediate; + break; + case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB: + libraryOrdinal = read_uleb128(p, end); + break; + case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM: + // the special ordinals are negative numbers + if ( immediate == 0 ) + libraryOrdinal = 0; + else { + int8_t signExtended = BIND_OPCODE_MASK | immediate; + libraryOrdinal = signExtended; + } + break; + case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: + weakImport = ( (immediate & BIND_SYMBOL_FLAGS_WEAK_IMPORT) != 0 ); + symbolName = (char*)p; + while (*p != '\0') + ++p; + ++p; + break; + case BIND_OPCODE_SET_TYPE_IMM: + type = immediate; + break; + case BIND_OPCODE_SET_ADDEND_SLEB: + addend = read_sleb128(p, end); + break; + case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: + segmentIndex = immediate; + segmentOffset = read_uleb128(p, end); + break; + case BIND_OPCODE_ADD_ADDR_ULEB: + segmentOffset += read_uleb128(p, end); + break; + case BIND_OPCODE_DO_BIND: + bindDyldInfoAt(segmentIndex, segmentOffset, type, libraryOrdinal, addend, symbolName, false, weakImport, pointersInData); + segmentOffset += sizeof(pint_t); + break; + case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: + bindDyldInfoAt(segmentIndex, segmentOffset, type, libraryOrdinal, addend, symbolName, false, weakImport, pointersInData); + segmentOffset += read_uleb128(p, end) + sizeof(pint_t); + break; + case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: + bindDyldInfoAt(segmentIndex, segmentOffset, type, libraryOrdinal, addend, symbolName, false, weakImport, pointersInData); + segmentOffset += immediate*sizeof(pint_t) + sizeof(pint_t); + break; + case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: + count = read_uleb128(p, end); + skip = read_uleb128(p, end); + for (uint32_t i=0; i < count; ++i) { + bindDyldInfoAt(segmentIndex, segmentOffset, type, libraryOrdinal, addend, symbolName, false, weakImport, pointersInData); + segmentOffset += skip + sizeof(pint_t); + } + break; + default: + throwf("bad bind opcode %d", *p); + } + } +} + +template +bool Binder::findExportedSymbolAddress(const char* name, pint_t* result, Binder** foundIn, bool* isResolverSymbol, bool* isAbsolute) +{ + *foundIn = NULL; + // since re-export chains can be any length, re-exports cannot be resolved in setDependencies() + // instead we lazily, recursively update + if ( !fReExportedSymbolsResolved ) { + + // update fHashTable with any individual symbol re-exports + for (typename std::vector::iterator it=fReExportedSymbols.begin(); it != fReExportedSymbols.end(); ++it) { + pint_t targetSymbolAddress; + bool isResolver; + bool isAb; + + if ( it->dylibOrdinal <= 0 ) + throw "bad mach-o binary, special library ordinal not allowed in re-exported symbols in dyld shared cache"; + + Binder* binder = fDependentDylibs[it->dylibOrdinal-1].binder; + + if ( ! binder->findExportedSymbolAddress(it->importName, &targetSymbolAddress, foundIn, &isResolver, &isAb) ) + throwf("could not bind symbol %s in %s expected in %s", it->importName, this->getDylibID(), binder->getDylibID()); + + if ( isResolver ) + fSymbolResolvers.insert(name); + + fHashTable[it->exportName] = targetSymbolAddress; + } + // mark as done + fReExportedSymbolsResolved = true; + } + + *isResolverSymbol = false; + if ( !fSymbolResolvers.empty() && fSymbolResolvers.count(name) ) { + // lazy pointers should be left unbound, rather than bind to resolver stub + *isResolverSymbol = true; + } + + // search this dylib + typename NameToAddrMap::iterator pos = fHashTable.find(name); + if ( pos != fHashTable.end() ) { + *result = pos->second; + //fprintf(stderr, "findExportedSymbolAddress(%s) => 0x%08llX in %s\n", name, (uint64_t)*result, this->getDylibID()); + *foundIn = this; + *isAbsolute = (fAbsoluteSymbols.count(name) != 0); + return true; + } + + // search re-exported dylibs + for (typename std::vector::iterator it = fDependentDylibs.begin(); it != fDependentDylibs.end(); ++it) { + if ( it->reExport ) { + if ( it->binder->findExportedSymbolAddress(name, result, foundIn, isResolverSymbol, isAbsolute) ) + return true; + } + } + //fprintf(stderr, "findExportedSymbolAddress(%s) => not found in %s\n", name, this->getDylibID()); + return false; +} + +// record which dylibs will be using this dylibs lazy pointer +template +void Binder::addResolverClient(Binder* clientDylib, const char* symbolName) +{ + fResolverInfo[symbolName].insert(clientDylib); +} + + +template +typename A::P::uint_t Binder::findLazyPointerFor(const char* symbolName) +{ + static const bool log = false; + + // lookup in lazy pointer section + const uint32_t* const indirectTable = (uint32_t*)&this->fLinkEditBase[fDynamicInfo->indirectsymoff()]; + const macho_load_command

* const cmds = (macho_load_command

*)((uint8_t*)this->fHeader + sizeof(macho_header

)); + const uint32_t cmd_count = this->fHeader->ncmds(); + const macho_load_command

* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + if ( cmd->cmd() == macho_segment_command

::CMD ) { + const macho_segment_command

* seg = (macho_segment_command

*)cmd; + const macho_section

* const sectionsStart = (macho_section

*)((uint8_t*)seg + sizeof(macho_segment_command

)); + const macho_section

* const sectionsEnd = §ionsStart[seg->nsects()]; + for (const macho_section

* sect=sectionsStart; sect < sectionsEnd; ++sect) { + uint8_t sectionType = sect->flags() & SECTION_TYPE; + if ( sectionType == S_LAZY_SYMBOL_POINTERS) { + uint32_t elementCount = sect->size() / sizeof(pint_t); + const uint32_t indirectTableOffset = sect->reserved1(); + pint_t vmlocation = sect->addr(); + for (uint32_t j=0; j < elementCount; ++j, vmlocation += sizeof(pint_t)) { + uint32_t symbolIndex = E::get32(indirectTable[indirectTableOffset + j]); + switch ( symbolIndex ) { + case INDIRECT_SYMBOL_ABS: + case INDIRECT_SYMBOL_LOCAL: + break; + default: + const macho_nlist

* aSymbol = &fSymbolTable[symbolIndex]; + const char* aName = &fStrings[aSymbol->n_strx()]; + //fprintf(stderr, " sect=%s, index=%d, symbolIndex=%d, sym=%s\n", sect->sectname(), j, symbolIndex, &fStrings[undefinedSymbol->n_strx()]); + if ( strcmp(aName, symbolName) == 0 ) { + if ( log ) fprintf(stderr, "found shared lazy pointer at 0x%llX for %s in %s\n", (uint64_t)vmlocation, symbolName, this->getDylibID()); + return vmlocation; + } + break; + } + } + } + } + } + cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); + } + + if ( log ) fprintf(stderr, "not found shared lazy pointer for %s in %s, checking for re-export symbol\n", symbolName, this->getDylibID()); + for (typename std::vector::iterator it=fReExportedSymbols.begin(); it != fReExportedSymbols.end(); ++it) { + if ( strcmp(it->exportName, symbolName) != 0 ) + continue; + + if ( it->dylibOrdinal <= 0 ) + throw "bad mach-o binary, special library ordinal not allowed in re-exported symbols in dyld shared cache"; + + Binder* binder = fDependentDylibs[it->dylibOrdinal-1].binder; + return binder->findLazyPointerFor(it->importName); + } + + if ( log ) fprintf(stderr, "not found shared lazy pointer for %s in %s, checking re-export dylibs\n", symbolName, this->getDylibID()); + for (typename std::vector::iterator it = fDependentDylibs.begin(); it != fDependentDylibs.end(); ++it) { + if ( it->reExport ) { + pint_t result = it->binder->findLazyPointerFor(symbolName); + if ( result != 0 ) + return result; + } + } + + if ( log ) fprintf(stderr, "NOT found shared lazy pointer for %s in %s\n", symbolName, this->getDylibID()); + return 0; +} + +// called after all binding is done to optimize lazy pointers +template +void Binder::optimize() +{ + hoistPrivateRexports(); + shareLazyPointersToResolvers(); +} + +template +void Binder::shareLazyPointersToResolvers() +{ + for (const auto &entry : fResolverInfo) { + const char* resolverSymbolName = entry.first; + if ( pint_t lpVMAddr = findLazyPointerFor(resolverSymbolName) ) { + for (Binder* clientDylib : entry.second) { + clientDylib->switchStubToUseSharedLazyPointer(resolverSymbolName, lpVMAddr); + } + } + else { + fprintf(stderr, "not able to optimize lazy pointer for %s in %s\n", resolverSymbolName, this->getDylibID()); + } + } +} + + + +template <> +void Binder::switchStubsLazyPointer(uint8_t* stubMappedAddress, pint_t stubVMAddress, uint32_t stubSize, pint_t lpVMAddr) +{ + if ( stubSize != 16 ) { + fprintf(stderr, "could not optimize ARM stub to resolver function in %s because it is wrong size\n", this->getDylibID()); + return; + } + uint32_t* instructions = (uint32_t*)stubMappedAddress; + if ( (E::get32(instructions[0]) != 0xe59fc004) + || (E::get32(instructions[1]) != 0xe08fc00c) + || (E::get32(instructions[2]) != 0xe59cf000) + ) { + fprintf(stderr, "could not optimize ARM stub to resolver function in %s because instructions are not as expected\n", this->getDylibID()); + return; + } + // last .long in stub is: lazyPtr - (stub+8) + // alter to point to more optimal lazy pointer + uint32_t betterOffset = lpVMAddr - (stubVMAddress + 12); + E::set32(instructions[3], betterOffset); +} + + +template <> +void Binder::switchStubsLazyPointer(uint8_t* stubMappedAddress, pint_t stubVMAddress, uint32_t stubSize, pint_t lpVMAddr) +{ + if ( stubSize != 6 ) { + fprintf(stderr, "could not optimize x86_64 stub to resolver function in %s because it is wrong size\n", this->getDylibID()); + return; + } + if ( (stubMappedAddress[0] != 0xFF) || (stubMappedAddress[1] != 0x25) ) { + fprintf(stderr, "could not optimize stub to resolver function in %s because instructions are not as expected\n", this->getDylibID()); + return; + } + // last four bytes in stub is RIP relative offset to lazy pointer + // alter to point to more optimal lazy pointer + uint32_t betterOffset = lpVMAddr - (stubVMAddress + 6); + E::set32(*((uint32_t*)(&stubMappedAddress[2])), betterOffset); +} + +template +void Binder::switchStubsLazyPointer(uint8_t* stubMappedAddress, pint_t stubVMAddress, uint32_t stubSize, pint_t lpVMAddress) +{ + // Remaining architectures are not optimized + //fprintf(stderr, "optimize stub at %p in %s to use lazyPointer at 0x%llX\n", stubMappedAddress, this->getDylibID(), (uint64_t)lpVMAddress); +} + +// search for stub in this image that call target symbol name and then optimize its lazy pointer +template +void Binder::switchStubToUseSharedLazyPointer(const char* stubName, pint_t lpVMAddr) +{ + // find named stub + const uint32_t* const indirectTable = (uint32_t*)&this->fLinkEditBase[fDynamicInfo->indirectsymoff()]; + const macho_load_command

* const cmds = (macho_load_command

*)((uint8_t*)this->fHeader + sizeof(macho_header

)); + const uint32_t cmd_count = this->fHeader->ncmds(); + const macho_load_command

* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + if ( cmd->cmd() == macho_segment_command

::CMD ) { + macho_segment_command

* seg = (macho_segment_command

*)cmd; + macho_section

* const sectionsStart = (macho_section

*)((char*)seg + sizeof(macho_segment_command

)); + macho_section

* const sectionsEnd = §ionsStart[seg->nsects()]; + for(macho_section

* sect = sectionsStart; sect < sectionsEnd; ++sect) { + if ( ((sect->flags() & SECTION_TYPE) == S_SYMBOL_STUBS) && (sect->size() != 0) ) { + pint_t stubsVMStart = sect->addr(); + uint8_t* stubsMappingStart = (uint8_t*)this->mappedAddressForNewAddress(stubsVMStart); + const uint32_t indirectTableOffset = sect->reserved1(); + const uint32_t stubSize = sect->reserved2(); + uint32_t elementCount = sect->size() / stubSize; + pint_t stubVMAddr = stubsVMStart; + uint8_t* stubMappedAddr = stubsMappingStart; + for (uint32_t j=0; j < elementCount; ++j, stubMappedAddr += stubSize, stubVMAddr += stubSize) { + uint32_t symbolIndex = E::get32(indirectTable[indirectTableOffset + j]); + switch ( symbolIndex ) { + case INDIRECT_SYMBOL_ABS: + case INDIRECT_SYMBOL_LOCAL: + break; + default: + { + const macho_nlist

* sym = &this->fSymbolTable[symbolIndex]; + const char* symName = &fStrings[sym->n_strx()]; + if ( strcmp(symName, stubName) == 0 ) + this->switchStubsLazyPointer(stubMappedAddr, stubVMAddr, stubSize, lpVMAddr); + } + break; + } + } + } + } + } + cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); + } +} + + +#endif // __MACHO_BINDER__ + + + + diff --git a/src/dyld-apple/launch-cache/MachOFileAbstraction.hpp b/src/dyld-apple/launch-cache/MachOFileAbstraction.hpp new file mode 100644 index 000000000..f92a95ba5 --- /dev/null +++ b/src/dyld-apple/launch-cache/MachOFileAbstraction.hpp @@ -0,0 +1,964 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2005 Apple Computer, 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@ +*/ +#ifndef __MACH_O_FILE_ABSTRACTION__ +#define __MACH_O_FILE_ABSTRACTION__ + +#include +#include +#include +#include + +// suport older versions of mach-o/loader.h +#ifndef LC_UUID +#define LC_UUID 0x1b +struct uuid_command { + uint32_t cmd; /* LC_UUID */ + uint32_t cmdsize; /* sizeof(struct uuid_command) */ + uint8_t uuid[16]; /* the 128-bit uuid */ +}; +#endif + +#ifndef S_16BYTE_LITERALS + #define S_16BYTE_LITERALS 0xE +#endif + +#ifndef CPU_SUBTYPE_ARM_V5TEJ + #define CPU_SUBTYPE_ARM_V5TEJ ((cpu_subtype_t) 7) +#endif +#ifndef CPU_SUBTYPE_ARM_XSCALE + #define CPU_SUBTYPE_ARM_XSCALE ((cpu_subtype_t) 8) +#endif +#ifndef CPU_SUBTYPE_ARM_V7 + #define CPU_SUBTYPE_ARM_V7 ((cpu_subtype_t) 9) +#endif +#ifndef CPU_SUBTYPE_ARM_V7F + #define CPU_SUBTYPE_ARM_V7F ((cpu_subtype_t) 10) +#endif +#ifndef CPU_SUBTYPE_ARM_V7K + #define CPU_SUBTYPE_ARM_V7K ((cpu_subtype_t) 12) +#endif +#ifndef CPU_SUBTYPE_ARM_V7S + #define CPU_SUBTYPE_ARM_V7S ((cpu_subtype_t) 11) +#endif +#ifndef CPU_SUBTYPE_ARM64_ALL + #define CPU_SUBTYPE_ARM64_ALL ((cpu_subtype_t) 0) +#endif +#ifndef CPU_TYPE_ARM64 + #define CPU_TYPE_ARM64 ((cpu_type_t) (CPU_TYPE_ARM | CPU_ARCH_ABI64)) +#endif + +#define ARM64_RELOC_UNSIGNED 0 // for pointers + + +#ifndef LC_LOAD_UPWARD_DYLIB + #define LC_LOAD_UPWARD_DYLIB (0x23|LC_REQ_DYLD) /* load of dylib whose initializers run later */ +#endif + +#ifndef EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER + #define EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER 0x10 +#endif +#ifndef EXPORT_SYMBOL_FLAGS_REEXPORT + #define EXPORT_SYMBOL_FLAGS_REEXPORT 0x08 +#endif + +#ifndef LC_FUNCTION_STARTS + #define LC_FUNCTION_STARTS 0x26 +#endif + +#ifndef LC_DATA_IN_CODE + #define LC_DATA_IN_CODE 0x29 +#endif + +#ifndef LC_DYLIB_CODE_SIGN_DRS + #define LC_DYLIB_CODE_SIGN_DRS 0x2B +#endif + +#ifndef CPU_SUBTYPE_X86_64_H + #define CPU_SUBTYPE_X86_64_H ((cpu_subtype_t) 8) +#endif + + +#define DYLD_CACHE_ADJ_V2_FORMAT 0x7F + +#define DYLD_CACHE_ADJ_V2_POINTER_32 0x01 +#define DYLD_CACHE_ADJ_V2_POINTER_64 0x02 +#define DYLD_CACHE_ADJ_V2_DELTA_32 0x03 +#define DYLD_CACHE_ADJ_V2_DELTA_64 0x04 +#define DYLD_CACHE_ADJ_V2_ARM64_ADRP 0x05 +#define DYLD_CACHE_ADJ_V2_ARM64_OFF12 0x06 +#define DYLD_CACHE_ADJ_V2_ARM64_BR26 0x07 +#define DYLD_CACHE_ADJ_V2_ARM_MOVW_MOVT 0x08 +#define DYLD_CACHE_ADJ_V2_ARM_BR24 0x09 +#define DYLD_CACHE_ADJ_V2_THUMB_MOVW_MOVT 0x0A +#define DYLD_CACHE_ADJ_V2_THUMB_BR22 0x0B +#define DYLD_CACHE_ADJ_V2_IMAGE_OFF_32 0x0C + +#define MH_HAS_OBJC 0x40000000 + +#include "FileAbstraction.hpp" +#include "Architectures.hpp" + +// utility to pair together a cpu-type and cpu-sub-type +struct ArchPair +{ + uint32_t arch; + uint32_t subtype; + + ArchPair(uint32_t cputype, uint32_t cpusubtype) : arch(cputype), subtype(cpusubtype) {} + + bool operator<(const ArchPair& other) const { + if ( this->arch != other.arch ) + return (this->arch < other.arch); + return (this->subtype < other.subtype); + } + + bool operator==(const ArchPair& other) const { + return this->arch == other.arch && this->subtype == other.subtype; + } +}; + + +// +// This abstraction layer makes every mach-o file look like a 64-bit mach-o file with native endianness +// + +// +// mach-o load command +// +template +class macho_load_command { +public: + uint32_t cmd() const INLINE { return E::get32(command.cmd); } + void set_cmd(uint32_t value) INLINE { E::set32(command.cmd, value); } + + uint32_t cmdsize() const INLINE { return E::get32(command.cmdsize); } + void set_cmdsize(uint32_t value) INLINE { E::set32(command.cmdsize, value); } + + typedef typename P::E E; +private: + load_command command; +}; + + +// +// mach-o segment load command +// +template struct macho_segment_content {}; +template <> struct macho_segment_content > { segment_command fields; enum { CMD = LC_SEGMENT }; }; +template <> struct macho_segment_content > { segment_command_64 fields; enum { CMD = LC_SEGMENT_64 }; }; +template <> struct macho_segment_content > { segment_command fields; enum { CMD = LC_SEGMENT }; }; +template <> struct macho_segment_content > { segment_command_64 fields; enum { CMD = LC_SEGMENT_64 }; }; + +template +class macho_segment_command { +public: + uint32_t cmd() const INLINE { return E::get32(segment.fields.cmd); } + void set_cmd(uint32_t value) INLINE { E::set32(segment.fields.cmd, value); } + + uint32_t cmdsize() const INLINE { return E::get32(segment.fields.cmdsize); } + void set_cmdsize(uint32_t value) INLINE { E::set32(segment.fields.cmdsize, value); } + + const char* segname() const INLINE { return segment.fields.segname; } + void set_segname(const char* value) INLINE { strncpy(segment.fields.segname, value, 16); } + + uint64_t vmaddr() const INLINE { return P::getP(segment.fields.vmaddr); } + void set_vmaddr(uint64_t value) INLINE { P::setP(segment.fields.vmaddr, value); } + + uint64_t vmsize() const INLINE { return P::getP(segment.fields.vmsize); } + void set_vmsize(uint64_t value) INLINE { P::setP(segment.fields.vmsize, value); } + + uint64_t fileoff() const INLINE { return P::getP(segment.fields.fileoff); } + void set_fileoff(uint64_t value) INLINE { P::setP(segment.fields.fileoff, value); } + + uint64_t filesize() const INLINE { return P::getP(segment.fields.filesize); } + void set_filesize(uint64_t value) INLINE { P::setP(segment.fields.filesize, value); } + + uint32_t maxprot() const INLINE { return E::get32(segment.fields.maxprot); } + void set_maxprot(uint32_t value) INLINE { E::set32((uint32_t&)segment.fields.maxprot, value); } + + uint32_t initprot() const INLINE { return E::get32(segment.fields.initprot); } + void set_initprot(uint32_t value) INLINE { E::set32((uint32_t&)segment.fields.initprot, value); } + + uint32_t nsects() const INLINE { return E::get32(segment.fields.nsects); } + void set_nsects(uint32_t value) INLINE { E::set32(segment.fields.nsects, value); } + + uint32_t flags() const INLINE { return E::get32(segment.fields.flags); } + void set_flags(uint32_t value) INLINE { E::set32(segment.fields.flags, value); } + + enum { + CMD = macho_segment_content

::CMD + }; + + typedef typename P::E E; +private: + macho_segment_content

segment; +}; + + +// +// mach-o section +// +template struct macho_section_content {}; +template <> struct macho_section_content > { section fields; }; +template <> struct macho_section_content > { section_64 fields; }; +template <> struct macho_section_content > { section fields; }; +template <> struct macho_section_content > { section_64 fields; }; + +template +class macho_section { +public: + const char* sectname() const INLINE { return section.fields.sectname; } + void set_sectname(const char* value) INLINE { strncpy(section.fields.sectname, value, 16); } + + const char* segname() const INLINE { return section.fields.segname; } + void set_segname(const char* value) INLINE { strncpy(section.fields.segname, value, 16); } + + uint64_t addr() const INLINE { return P::getP(section.fields.addr); } + void set_addr(uint64_t value) INLINE { P::setP(section.fields.addr, value); } + + uint64_t size() const INLINE { return P::getP(section.fields.size); } + void set_size(uint64_t value) INLINE { P::setP(section.fields.size, value); } + + uint32_t offset() const INLINE { return E::get32(section.fields.offset); } + void set_offset(uint32_t value) INLINE { E::set32(section.fields.offset, value); } + + uint32_t align() const INLINE { return E::get32(section.fields.align); } + void set_align(uint32_t value) INLINE { E::set32(section.fields.align, value); } + + uint32_t reloff() const INLINE { return E::get32(section.fields.reloff); } + void set_reloff(uint32_t value) INLINE { E::set32(section.fields.reloff, value); } + + uint32_t nreloc() const INLINE { return E::get32(section.fields.nreloc); } + void set_nreloc(uint32_t value) INLINE { E::set32(section.fields.nreloc, value); } + + uint32_t flags() const INLINE { return E::get32(section.fields.flags); } + void set_flags(uint32_t value) INLINE { E::set32(section.fields.flags, value); } + + uint32_t reserved1() const INLINE { return E::get32(section.fields.reserved1); } + void set_reserved1(uint32_t value) INLINE { E::set32(section.fields.reserved1, value); } + + uint32_t reserved2() const INLINE { return E::get32(section.fields.reserved2); } + void set_reserved2(uint32_t value) INLINE { E::set32(section.fields.reserved2, value); } + + typedef typename P::E E; +private: + macho_section_content

section; +}; + + +// +// mach-o dylib load command +// +template +class macho_dylib_command { +public: + uint32_t cmd() const INLINE { return E::get32(fields.cmd); } + void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } + + uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } + void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } + + uint32_t name_offset() const INLINE { return E::get32(fields.dylib.name.offset); } + void set_name_offset(uint32_t value) INLINE { E::set32(fields.dylib.name.offset, value); } + + uint32_t timestamp() const INLINE { return E::get32(fields.dylib.timestamp); } + void set_timestamp(uint32_t value) INLINE { E::set32(fields.dylib.timestamp, value); } + + uint32_t current_version() const INLINE { return E::get32(fields.dylib.current_version); } + void set_current_version(uint32_t value) INLINE { E::set32(fields.dylib.current_version, value); } + + uint32_t compatibility_version() const INLINE { return E::get32(fields.dylib.compatibility_version); } + void set_compatibility_version(uint32_t value) INLINE { E::set32(fields.dylib.compatibility_version, value); } + + const char* name() const INLINE { return (const char*)&fields + name_offset(); } + void set_name_offset() INLINE { set_name_offset(sizeof(fields)); } + + typedef typename P::E E; +private: + dylib_command fields; +}; + + +// +// mach-o dylinker load command +// +template +class macho_dylinker_command { +public: + uint32_t cmd() const INLINE { return E::get32(fields.cmd); } + void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } + + uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } + void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } + + uint32_t name_offset() const INLINE { return E::get32(fields.name.offset); } + void set_name_offset(uint32_t value) INLINE { E::set32(fields.name.offset, value); } + + const char* name() const INLINE { return (const char*)&fields + name_offset(); } + void set_name_offset() INLINE { set_name_offset(sizeof(fields)); } + + typedef typename P::E E; +private: + dylinker_command fields; +}; + + +// +// mach-o sub_framework load command +// +template +class macho_sub_framework_command { +public: + uint32_t cmd() const INLINE { return E::get32(fields.cmd); } + void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } + + uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } + void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } + + uint32_t umbrella_offset() const INLINE { return E::get32(fields.umbrella.offset); } + void set_umbrella_offset(uint32_t value) INLINE { E::set32(fields.umbrella.offset, value); } + + const char* umbrella() const INLINE { return (const char*)&fields + umbrella_offset(); } + void set_umbrella_offset() INLINE { set_umbrella_offset(sizeof(fields)); } + + typedef typename P::E E; +private: + sub_framework_command fields; +}; + + +// +// mach-o sub_client load command +// +template +class macho_sub_client_command { +public: + uint32_t cmd() const INLINE { return E::get32(fields.cmd); } + void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } + + uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } + void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } + + uint32_t client_offset() const INLINE { return E::get32(fields.client.offset); } + void set_client_offset(uint32_t value) INLINE { E::set32(fields.client.offset, value); } + + const char* client() const INLINE { return (const char*)&fields + client_offset(); } + void set_client_offset() INLINE { set_client_offset(sizeof(fields)); } + + typedef typename P::E E; +private: + sub_client_command fields; +}; + + +// +// mach-o sub_umbrella load command +// +template +class macho_sub_umbrella_command { +public: + uint32_t cmd() const INLINE { return E::get32(fields.cmd); } + void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } + + uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } + void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } + + uint32_t sub_umbrella_offset() const INLINE { return E::get32(fields.sub_umbrella.offset); } + void set_sub_umbrella_offset(uint32_t value) INLINE { E::set32(fields.sub_umbrella.offset, value); } + + const char* sub_umbrella() const INLINE { return (const char*)&fields + sub_umbrella_offset(); } + void set_sub_umbrella_offset() INLINE { set_sub_umbrella_offset(sizeof(fields)); } + + typedef typename P::E E; +private: + sub_umbrella_command fields; +}; + + +// +// mach-o sub_library load command +// +template +class macho_sub_library_command { +public: + uint32_t cmd() const INLINE { return E::get32(fields.cmd); } + void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } + + uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } + void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } + + uint32_t sub_library_offset() const INLINE { return E::get32(fields.sub_library.offset); } + void set_sub_library_offset(uint32_t value) INLINE { E::set32(fields.sub_library.offset, value); } + + const char* sub_library() const INLINE { return (const char*)&fields + sub_library_offset(); } + void set_sub_library_offset() INLINE { set_sub_library_offset(sizeof(fields)); } + + typedef typename P::E E; +private: + sub_library_command fields; +}; + + +// +// mach-o uuid load command +// +template +class macho_uuid_command { +public: + uint32_t cmd() const INLINE { return E::get32(fields.cmd); } + void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } + + uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } + void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } + + const uint8_t* uuid() const INLINE { return fields.uuid; } + void set_uuid(uint8_t value[16]) INLINE { memcpy(&fields.uuid, value, 16); } + + typedef typename P::E E; +private: + uuid_command fields; +}; + + +// +// mach-o routines load command +// +template struct macho_routines_content {}; +template <> struct macho_routines_content > { routines_command fields; enum { CMD = LC_ROUTINES }; }; +template <> struct macho_routines_content > { routines_command_64 fields; enum { CMD = LC_ROUTINES_64 }; }; +template <> struct macho_routines_content > { routines_command fields; enum { CMD = LC_ROUTINES }; }; +template <> struct macho_routines_content > { routines_command_64 fields; enum { CMD = LC_ROUTINES_64 }; }; + +template +class macho_routines_command { +public: + uint32_t cmd() const INLINE { return E::get32(routines.fields.cmd); } + void set_cmd(uint32_t value) INLINE { E::set32(routines.fields.cmd, value); } + + uint32_t cmdsize() const INLINE { return E::get32(routines.fields.cmdsize); } + void set_cmdsize(uint32_t value) INLINE { E::set32(routines.fields.cmdsize, value); } + + uint64_t init_address() const INLINE { return P::getP(routines.fields.init_address); } + void set_init_address(uint64_t value) INLINE { P::setP(routines.fields.init_address, value); } + + uint64_t init_module() const INLINE { return P::getP(routines.fields.init_module); } + void set_init_module(uint64_t value) INLINE { P::setP(routines.fields.init_module, value); } + + uint64_t reserved1() const INLINE { return P::getP(routines.fields.reserved1); } + void set_reserved1(uint64_t value) INLINE { P::setP(routines.fields.reserved1, value); } + + uint64_t reserved2() const INLINE { return P::getP(routines.fields.reserved2); } + void set_reserved2(uint64_t value) INLINE { P::setP(routines.fields.reserved2, value); } + + uint64_t reserved3() const INLINE { return P::getP(routines.fields.reserved3); } + void set_reserved3(uint64_t value) INLINE { P::setP(routines.fields.reserved3, value); } + + uint64_t reserved4() const INLINE { return P::getP(routines.fields.reserved4); } + void set_reserved4(uint64_t value) INLINE { P::setP(routines.fields.reserved4, value); } + + uint64_t reserved5() const INLINE { return P::getP(routines.fields.reserved5); } + void set_reserved5(uint64_t value) INLINE { P::setP(routines.fields.reserved5, value); } + + uint64_t reserved6() const INLINE { return P::getP(routines.fields.reserved6); } + void set_reserved6(uint64_t value) INLINE { P::setP(routines.fields.reserved6, value); } + + typedef typename P::E E; + enum { + CMD = macho_routines_content

::CMD + }; +private: + macho_routines_content

routines; +}; + + +// +// mach-o symbol table load command +// +template +class macho_symtab_command { +public: + uint32_t cmd() const INLINE { return E::get32(fields.cmd); } + void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } + + uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } + void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } + + uint32_t symoff() const INLINE { return E::get32(fields.symoff); } + void set_symoff(uint32_t value) INLINE { E::set32(fields.symoff, value); } + + uint32_t nsyms() const INLINE { return E::get32(fields.nsyms); } + void set_nsyms(uint32_t value) INLINE { E::set32(fields.nsyms, value); } + + uint32_t stroff() const INLINE { return E::get32(fields.stroff); } + void set_stroff(uint32_t value) INLINE { E::set32(fields.stroff, value); } + + uint32_t strsize() const INLINE { return E::get32(fields.strsize); } + void set_strsize(uint32_t value) INLINE { E::set32(fields.strsize, value); } + + + typedef typename P::E E; +private: + symtab_command fields; +}; + + +// +// mach-o dynamic symbol table load command +// +template +class macho_dysymtab_command { +public: + uint32_t cmd() const INLINE { return E::get32(fields.cmd); } + void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } + + uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } + void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } + + uint32_t ilocalsym() const INLINE { return E::get32(fields.ilocalsym); } + void set_ilocalsym(uint32_t value) INLINE { E::set32(fields.ilocalsym, value); } + + uint32_t nlocalsym() const INLINE { return E::get32(fields.nlocalsym); } + void set_nlocalsym(uint32_t value) INLINE { E::set32(fields.nlocalsym, value); } + + uint32_t iextdefsym() const INLINE { return E::get32(fields.iextdefsym); } + void set_iextdefsym(uint32_t value) INLINE { E::set32(fields.iextdefsym, value); } + + uint32_t nextdefsym() const INLINE { return E::get32(fields.nextdefsym); } + void set_nextdefsym(uint32_t value) INLINE { E::set32(fields.nextdefsym, value); } + + uint32_t iundefsym() const INLINE { return E::get32(fields.iundefsym); } + void set_iundefsym(uint32_t value) INLINE { E::set32(fields.iundefsym, value); } + + uint32_t nundefsym() const INLINE { return E::get32(fields.nundefsym); } + void set_nundefsym(uint32_t value) INLINE { E::set32(fields.nundefsym, value); } + + uint32_t tocoff() const INLINE { return E::get32(fields.tocoff); } + void set_tocoff(uint32_t value) INLINE { E::set32(fields.tocoff, value); } + + uint32_t ntoc() const INLINE { return E::get32(fields.ntoc); } + void set_ntoc(uint32_t value) INLINE { E::set32(fields.ntoc, value); } + + uint32_t modtaboff() const INLINE { return E::get32(fields.modtaboff); } + void set_modtaboff(uint32_t value) INLINE { E::set32(fields.modtaboff, value); } + + uint32_t nmodtab() const INLINE { return E::get32(fields.nmodtab); } + void set_nmodtab(uint32_t value) INLINE { E::set32(fields.nmodtab, value); } + + uint32_t extrefsymoff() const INLINE { return E::get32(fields.extrefsymoff); } + void set_extrefsymoff(uint32_t value) INLINE { E::set32(fields.extrefsymoff, value); } + + uint32_t nextrefsyms() const INLINE { return E::get32(fields.nextrefsyms); } + void set_nextrefsyms(uint32_t value) INLINE { E::set32(fields.nextrefsyms, value); } + + uint32_t indirectsymoff() const INLINE { return E::get32(fields.indirectsymoff); } + void set_indirectsymoff(uint32_t value) INLINE { E::set32(fields.indirectsymoff, value); } + + uint32_t nindirectsyms() const INLINE { return E::get32(fields.nindirectsyms); } + void set_nindirectsyms(uint32_t value) INLINE { E::set32(fields.nindirectsyms, value); } + + uint32_t extreloff() const INLINE { return E::get32(fields.extreloff); } + void set_extreloff(uint32_t value) INLINE { E::set32(fields.extreloff, value); } + + uint32_t nextrel() const INLINE { return E::get32(fields.nextrel); } + void set_nextrel(uint32_t value) INLINE { E::set32(fields.nextrel, value); } + + uint32_t locreloff() const INLINE { return E::get32(fields.locreloff); } + void set_locreloff(uint32_t value) INLINE { E::set32(fields.locreloff, value); } + + uint32_t nlocrel() const INLINE { return E::get32(fields.nlocrel); } + void set_nlocrel(uint32_t value) INLINE { E::set32(fields.nlocrel, value); } + + typedef typename P::E E; +private: + dysymtab_command fields; +}; + + +// +// mach-o two-level hints load command +// +template +class macho_twolevel_hints_command { +public: + uint32_t cmd() const INLINE { return E::get32(fields.cmd); } + void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } + + uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } + void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } + + uint32_t offset() const INLINE { return E::get32(fields.offset); } + void set_offset(uint32_t value) INLINE { E::set32(fields.offset, value); } + + uint32_t nhints() const INLINE { return E::get32(fields.nhints); } + void set_nhints(uint32_t value) INLINE { E::set32(fields.nhints, value); } + + typedef typename P::E E; +private: + twolevel_hints_command fields; +}; + + +// +// mach-o threads load command +// +template +class macho_thread_command { +public: + uint32_t cmd() const INLINE { return E::get32(fields.cmd); } + void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } + + uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } + void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } + + uint32_t flavor() const INLINE { return E::get32(fields_flavor); } + void set_flavor(uint32_t value) INLINE { E::set32(fields_flavor, value); } + + uint32_t count() const INLINE { return E::get32(fields_count); } + void set_count(uint32_t value) INLINE { E::set32(fields_count, value); } + + uint64_t thread_register(uint32_t index) const INLINE { return P::getP(thread_registers[index]); } + void set_thread_register(uint32_t index, uint64_t value) INLINE { P::setP(thread_registers[index], value); } + + typedef typename P::E E; + typedef typename P::uint_t pint_t; +private: + struct thread_command fields; + uint32_t fields_flavor; + uint32_t fields_count; + pint_t thread_registers[1]; +}; + + +// +// mach-o misc data +// +template +class macho_linkedit_data_command { +public: + uint32_t cmd() const INLINE { return E::get32(fields.cmd); } + void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } + + uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } + void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } + + uint32_t dataoff() const INLINE { return E::get32(fields.dataoff); } + void set_dataoff(uint32_t value) INLINE { E::set32(fields.dataoff, value); } + + uint32_t datasize() const INLINE { return E::get32(fields.datasize); } + void set_datasize(uint32_t value)INLINE { E::set32(fields.datasize, value); } + + + typedef typename P::E E; +private: + linkedit_data_command fields; +}; + + +// +// mach-o symbol table entry +// +template struct macho_nlist_content {}; +template <> struct macho_nlist_content > { struct nlist fields; }; +template <> struct macho_nlist_content > { struct nlist_64 fields; }; +template <> struct macho_nlist_content > { struct nlist fields; }; +template <> struct macho_nlist_content > { struct nlist_64 fields; }; + +template +class macho_nlist { +public: + uint32_t n_strx() const INLINE { return E::get32(entry.fields.n_un.n_strx); } + void set_n_strx(uint32_t value) INLINE { E::set32((uint32_t&)entry.fields.n_un.n_strx, value); } + + uint8_t n_type() const INLINE { return entry.fields.n_type; } + void set_n_type(uint8_t value) INLINE { entry.fields.n_type = value; } + + uint8_t n_sect() const INLINE { return entry.fields.n_sect; } + void set_n_sect(uint8_t value) INLINE { entry.fields.n_sect = value; } + + uint16_t n_desc() const INLINE { return E::get16(entry.fields.n_desc); } + void set_n_desc(uint16_t value) INLINE { E::set16((uint16_t&)entry.fields.n_desc, value); } + + uint64_t n_value() const INLINE { return P::getP(entry.fields.n_value); } + void set_n_value(uint64_t value) INLINE { P::setP(entry.fields.n_value, value); } + + typedef typename P::E E; +private: + macho_nlist_content

entry; +}; + + + +// +// mach-o relocation info +// +template +class macho_relocation_info { +public: + uint32_t r_address() const INLINE { return E::get32(address); } + void set_r_address(uint32_t value) INLINE { E::set32(address, value); } + + uint32_t r_symbolnum() const INLINE { return E::getBits(other, 0, 24); } + void set_r_symbolnum(uint32_t value) INLINE { E::setBits(other, value, 0, 24); } + + bool r_pcrel() const INLINE { return E::getBits(other, 24, 1); } + void set_r_pcrel(bool value) INLINE { E::setBits(other, value, 24, 1); } + + uint8_t r_length() const INLINE { return E::getBits(other, 25, 2); } + void set_r_length(uint8_t value) INLINE { E::setBits(other, value, 25, 2); } + + bool r_extern() const INLINE { return E::getBits(other, 27, 1); } + void set_r_extern(bool value) INLINE { E::setBits(other, value, 27, 1); } + + uint8_t r_type() const INLINE { return E::getBits(other, 28, 4); } + void set_r_type(uint8_t value) INLINE { E::setBits(other, value, 28, 4); } + + void set_r_length() INLINE { set_r_length((sizeof(typename P::uint_t)==8) ? 3 : 2); } + + typedef typename P::E E; +private: + uint32_t address; + uint32_t other; +}; + + +// +// mach-o scattered relocation info +// The bit fields are always in big-endian order (see mach-o/reloc.h) +// +template +class macho_scattered_relocation_info { +public: + bool r_scattered() const INLINE { return BigEndian::getBitsRaw(E::get32(other), 0, 1); } + void set_r_scattered(bool x) INLINE { uint32_t temp = E::get32(other); BigEndian::setBitsRaw(temp, x, 0, 1); E::set32(other, temp); } + + bool r_pcrel() const INLINE { return BigEndian::getBitsRaw(E::get32(other), 1, 1); } + void set_r_pcrel(bool x) INLINE { uint32_t temp = E::get32(other); BigEndian::setBitsRaw(temp, x, 1, 1); E::set32(other, temp); } + + uint8_t r_length() const INLINE { return BigEndian::getBitsRaw(E::get32(other), 2, 2); } + void set_r_length(uint8_t x) INLINE { uint32_t temp = E::get32(other); BigEndian::setBitsRaw(temp, x, 2, 2); E::set32(other, temp); } + + uint8_t r_type() const INLINE { return BigEndian::getBitsRaw(E::get32(other), 4, 4); } + void set_r_type(uint8_t x) INLINE { uint32_t temp = E::get32(other); BigEndian::setBitsRaw(temp, x, 4, 4); E::set32(other, temp); } + + uint32_t r_address() const INLINE { return BigEndian::getBitsRaw(E::get32(other), 8, 24); } + void set_r_address(uint32_t x) INLINE { uint32_t temp = E::get32(other); BigEndian::setBitsRaw(temp, x, 8, 24); E::set32(other, temp); } + + uint32_t r_value() const INLINE { return E::get32(value); } + void set_r_value(uint32_t x) INLINE { E::set32(value, x); } + + uint32_t r_other() const INLINE { return other; } + + typedef typename P::E E; +private: + uint32_t other; + uint32_t value; +}; + + +// +// mach-o file header +// +template struct macho_header_content {}; +template <> struct macho_header_content > { mach_header fields; }; +template <> struct macho_header_content > { mach_header_64 fields; }; +template <> struct macho_header_content > { mach_header fields; }; +template <> struct macho_header_content > { mach_header_64 fields; }; + +template +class macho_header { +public: + uint32_t magic() const INLINE { return E::get32(header.fields.magic); } + void set_magic(uint32_t value) INLINE { E::set32(header.fields.magic, value); } + + uint32_t cputype() const INLINE { return E::get32(header.fields.cputype); } + void set_cputype(uint32_t value) INLINE { E::set32((uint32_t&)header.fields.cputype, value); } + + uint32_t cpusubtype() const INLINE { return E::get32(header.fields.cpusubtype); } + void set_cpusubtype(uint32_t value) INLINE { E::set32((uint32_t&)header.fields.cpusubtype, value); } + + uint32_t filetype() const INLINE { return E::get32(header.fields.filetype); } + void set_filetype(uint32_t value) INLINE { E::set32(header.fields.filetype, value); } + + uint32_t ncmds() const INLINE { return E::get32(header.fields.ncmds); } + void set_ncmds(uint32_t value) INLINE { E::set32(header.fields.ncmds, value); } + + uint32_t sizeofcmds() const INLINE { return E::get32(header.fields.sizeofcmds); } + void set_sizeofcmds(uint32_t value) INLINE { E::set32(header.fields.sizeofcmds, value); } + + uint32_t flags() const INLINE { return E::get32(header.fields.flags); } + void set_flags(uint32_t value) INLINE { E::set32(header.fields.flags, value); } + + uint32_t reserved() const INLINE { return E::get32(header.fields.reserved); } + void set_reserved(uint32_t value) INLINE { E::set32(header.fields.reserved, value); } + + const macho_segment_command

* getSegment(const char *segname) const + { + const macho_load_command

* cmds = (macho_load_command

*)((uint8_t*)this + sizeof(macho_header

)); + uint32_t cmd_count = this->ncmds(); + const macho_load_command

* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + if ( cmd->cmd() == macho_segment_command

::CMD ) { + const macho_segment_command

* segcmd = (macho_segment_command

*)cmd; + if (0 == strncmp(segname, segcmd->segname(), 16)) { + return segcmd; + } + } + cmd = (macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); + } + return NULL; + } + + const macho_section

* getSection(const char *segname, const char *sectname) const + { + const macho_segment_command

* segcmd = getSegment(segname); + if (!segcmd) return NULL; + + const macho_section

* sectcmd = (macho_section

*)(segcmd+1); + uint32_t section_count = segcmd->nsects(); + for (uint32_t j = 0; j < section_count; ++j) { + if (0 == ::strncmp(sectcmd[j].sectname(), sectname, 16)) { + return sectcmd+j; + } + } + + if (strcmp(segname, "__DATA") == 0) + return getSection("__DATA_CONST", sectname); + return NULL; + } + + const macho_load_command

* getLoadCommand(int query) const + { + const macho_load_command

* cmds = (macho_load_command

*)((uint8_t*)this + sizeof(macho_header

)); + uint32_t cmd_count = this->ncmds(); + const macho_load_command

* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + if ( cmd->cmd() == query ) { + return cmd; + } + cmd = (macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); + } + return NULL; + } + + typedef typename P::E E; +private: + macho_header_content

header; +}; + + + +// +// compressed dyld info load command +// +template +class macho_dyld_info_command { +public: + uint32_t cmd() const INLINE { return E::get32(fields.cmd); } + void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } + + uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } + void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } + + uint32_t rebase_off() const INLINE { return E::get32(fields.rebase_off); } + void set_rebase_off(uint32_t value) INLINE { E::set32(fields.rebase_off, value); } + + uint32_t rebase_size() const INLINE { return E::get32(fields.rebase_size); } + void set_rebase_size(uint32_t value) INLINE { E::set32(fields.rebase_size, value); } + + uint32_t bind_off() const INLINE { return E::get32(fields.bind_off); } + void set_bind_off(uint32_t value) INLINE { E::set32(fields.bind_off, value); } + + uint32_t bind_size() const INLINE { return E::get32(fields.bind_size); } + void set_bind_size(uint32_t value) INLINE { E::set32(fields.bind_size, value); } + + uint32_t weak_bind_off() const INLINE { return E::get32(fields.weak_bind_off); } + void set_weak_bind_off(uint32_t value) INLINE { E::set32(fields.weak_bind_off, value); } + + uint32_t weak_bind_size() const INLINE { return E::get32(fields.weak_bind_size); } + void set_weak_bind_size(uint32_t value) INLINE { E::set32(fields.weak_bind_size, value); } + + uint32_t lazy_bind_off() const INLINE { return E::get32(fields.lazy_bind_off); } + void set_lazy_bind_off(uint32_t value) INLINE { E::set32(fields.lazy_bind_off, value); } + + uint32_t lazy_bind_size() const INLINE { return E::get32(fields.lazy_bind_size); } + void set_lazy_bind_size(uint32_t value) INLINE { E::set32(fields.lazy_bind_size, value); } + + uint32_t export_off() const INLINE { return E::get32(fields.export_off); } + void set_export_off(uint32_t value) INLINE { E::set32(fields.export_off, value); } + + uint32_t export_size() const INLINE { return E::get32(fields.export_size); } + void set_export_size(uint32_t value) INLINE { E::set32(fields.export_size, value); } + + + typedef typename P::E E; +private: + dyld_info_command fields; +}; + +#ifndef NO_ULEB +inline uint64_t read_uleb128(const uint8_t*& p, const uint8_t* end) { + uint64_t result = 0; + int bit = 0; + do { + if (p == end) + throw "malformed uleb128 extends beyond trie"; + + uint64_t slice = *p & 0x7f; + + if (bit >= 64 || slice << bit >> bit != slice) + throw "uleb128 too big for 64-bits"; + else { + result |= (slice << bit); + bit += 7; + } + } + while (*p++ & 0x80); + return result; +} + + +inline int64_t read_sleb128(const uint8_t*& p, const uint8_t* end) +{ + int64_t result = 0; + int bit = 0; + uint8_t byte; + do { + if (p == end) + throw "malformed sleb128"; + byte = *p++; + result |= ((byte & 0x7f) << bit); + bit += 7; + } while (byte & 0x80); + // sign extend negative numbers + if ( (byte & 0x40) != 0 ) + result |= (-1LL) << bit; + return result; +} + +#endif + + +#endif // __MACH_O_FILE_ABSTRACTION__ + + diff --git a/src/dyld-apple/launch-cache/MachOLayout.hpp b/src/dyld-apple/launch-cache/MachOLayout.hpp new file mode 100644 index 000000000..49f593e9d --- /dev/null +++ b/src/dyld-apple/launch-cache/MachOLayout.hpp @@ -0,0 +1,772 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006-2011 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@ + */ + +#ifndef __MACHO_LAYOUT__ +#define __MACHO_LAYOUT__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "MachOFileAbstraction.hpp" +#include "Architectures.hpp" + + +void throwf(const char* format, ...) __attribute__((format(printf, 1, 2))); + +__attribute__((noreturn)) +void throwf(const char* format, ...) +{ + va_list list; + char* p; + va_start(list, format); + vasprintf(&p, format, list); + va_end(list); + + const char* t = p; + throw t; +} + + +class MachOLayoutAbstraction +{ +public: + struct Segment + { + public: + Segment(uint64_t addr, uint64_t vmsize, uint64_t offset, uint64_t file_size, uint64_t sectionsSize, + uint64_t sectionsAlignment, uint64_t align, + uint32_t prot, uint32_t sectionCount, const char* segName) : fOrigAddress(addr), fOrigSize(vmsize), + fOrigFileOffset(offset), fOrigFileSize(file_size), fOrigPermissions(prot), + fSize(vmsize), fFileOffset(offset), fFileSize(file_size), fAlignment(align), + fPermissions(prot), fSectionCount(sectionCount), fSectionsSize(sectionsSize), + fSectionsAlignment(sectionsAlignment), fNewAddress(0), fMappedAddress(NULL) { + strlcpy(fOrigName, segName, 16); + } + + uint64_t address() const { return fOrigAddress; } + uint64_t size() const { return fSize; } + uint64_t fileOffset() const { return fFileOffset; } + uint64_t fileSize() const { return fFileSize; } + uint32_t permissions() const { return fPermissions; } + bool readable() const { return fPermissions & VM_PROT_READ; } + bool writable() const { return fPermissions & VM_PROT_WRITE; } + bool executable() const { return fPermissions & VM_PROT_EXECUTE; } + uint64_t alignment() const { return fAlignment; } + const char* name() const { return fOrigName; } + uint64_t newAddress() const { return fNewAddress; } + uint32_t sectionCount() const{ return fSectionCount; } + uint64_t sectionsSize() const{ return fSectionsSize; } + uint64_t sectionsAlignment() const { return fSectionsAlignment; } + void* mappedAddress() const { return fMappedAddress; } + void setNewAddress(uint64_t addr) { fNewAddress = addr; } + void setMappedAddress(void* addr) { fMappedAddress = addr; } + void setSize(uint64_t new_size) { fSize = new_size; } + void setFileOffset(uint64_t new_off) { fFileOffset = new_off; } + void setFileSize(uint64_t new_size) { fFileSize = new_size; } + void setWritable(bool w) { if (w) fPermissions |= VM_PROT_WRITE; else fPermissions &= ~VM_PROT_WRITE; } + void setSectionsAlignment(uint64_t v){ fSectionsAlignment = v; } + void reset() { fSize=fOrigSize; fFileOffset=fOrigFileOffset; fFileSize=fOrigFileSize; fPermissions=fOrigPermissions; } + private: + uint64_t fOrigAddress; + uint64_t fOrigSize; + uint64_t fOrigFileOffset; + uint64_t fOrigFileSize; + uint32_t fOrigPermissions; + char fOrigName[16]; + uint64_t fSize; + uint64_t fFileOffset; + uint64_t fFileSize; + uint64_t fAlignment; + uint32_t fPermissions; + uint32_t fSectionCount; + uint64_t fSectionsSize; + uint64_t fSectionsAlignment; + uint64_t fNewAddress; + void* fMappedAddress; + }; + + struct Library + { + const char* name; + uint32_t currentVersion; + uint32_t compatibilityVersion; + bool weakImport; + }; + + + virtual ArchPair getArchPair() const = 0; + virtual const char* getFilePath() const = 0; + virtual uint64_t getOffsetInUniversalFile() const = 0; + virtual uint32_t getFileType() const = 0; + virtual uint32_t getFlags() const = 0; + virtual Library getID() const = 0; + virtual bool isDylib() const = 0; + virtual bool isSplitSeg() const = 0; + virtual bool hasSplitSegInfo() const = 0; + virtual bool hasSplitSegInfoV2() const = 0; + virtual bool inSharableLocation() const = 0; + virtual bool hasDynamicLookupLinkage() const = 0; + virtual bool hasMainExecutableLookupLinkage() const = 0; + virtual bool isTwoLevelNamespace() const = 0; + virtual bool hasDyldInfo() const = 0; + virtual bool hasMultipleReadWriteSegments() const = 0; + virtual uint32_t getNameFileOffset() const = 0; + virtual time_t getLastModTime() const = 0; + virtual ino_t getInode() const = 0; + virtual std::vector& getSegments() = 0; + virtual const std::vector& getSegments() const = 0; + virtual const Segment* getSegment(const char* name) const = 0; + virtual const std::vector& getLibraries() const = 0; + virtual uint64_t getBaseAddress() const = 0; + virtual uint64_t getVMSize() const = 0; + virtual uint64_t getBaseExecutableAddress() const = 0; + virtual uint64_t getBaseWritableAddress() const = 0; + virtual uint64_t getBaseReadOnlyAddress() const = 0; + virtual uint64_t getExecutableVMSize() const = 0; + virtual uint64_t getWritableVMSize() const = 0; + virtual uint64_t getReadOnlyVMSize() const = 0; + // need getDyldInfoExports because export info uses ULEB encoding and size could grow + virtual const uint8_t* getDyldInfoExports() const = 0; + virtual void setDyldInfoExports(const uint8_t* newExports) const = 0; + virtual void uuid(uuid_t u) const = 0; +}; + + + + +template +class MachOLayout : public MachOLayoutAbstraction +{ +public: + MachOLayout(const void* machHeader, uint64_t offset, const char* path, + ino_t inode, time_t modTime, uid_t uid); + virtual ~MachOLayout() {} + + virtual ArchPair getArchPair() const { return fArchPair; } + virtual const char* getFilePath() const { return fPath; } + virtual uint64_t getOffsetInUniversalFile() const { return fOffset; } + virtual uint32_t getFileType() const { return fFileType; } + virtual uint32_t getFlags() const { return fFlags; } + virtual Library getID() const { return fDylibID; } + virtual bool isDylib() const { return fIsDylib; } + virtual bool isSplitSeg() const; + virtual bool hasSplitSegInfo() const { return fSplitSegInfo != NULL; } + virtual bool hasSplitSegInfoV2() const{ return fHasSplitSegInfoV2; } + virtual bool inSharableLocation() const { return fShareableLocation; } + virtual bool hasDynamicLookupLinkage() const { return fDynamicLookupLinkage; } + virtual bool hasMainExecutableLookupLinkage() const { return fMainExecutableLookupLinkage; } + virtual bool isTwoLevelNamespace() const { return (fFlags & MH_TWOLEVEL); } + virtual bool hasDyldInfo() const { return fHasDyldInfo; } + virtual bool hasMultipleReadWriteSegments() const { return fHasTooManyWritableSegments; } + virtual uint32_t getNameFileOffset() const{ return fNameFileOffset; } + virtual time_t getLastModTime() const { return fMTime; } + virtual ino_t getInode() const { return fInode; } + virtual std::vector& getSegments() { return fSegments; } + virtual const std::vector& getSegments() const { return fSegments; } + virtual const Segment* getSegment(const char* name) const; + virtual const std::vector& getLibraries() const { return fLibraries; } + virtual uint64_t getBaseAddress() const { return fLowSegment->address(); } + virtual uint64_t getVMSize() const { return fVMSize; } + virtual uint64_t getBaseExecutableAddress() const { return fLowExecutableSegment->address(); } + virtual uint64_t getBaseWritableAddress() const { return fLowWritableSegment->address(); } + virtual uint64_t getBaseReadOnlyAddress() const { return fLowReadOnlySegment->address(); } + virtual uint64_t getExecutableVMSize() const { return fVMExecutableSize; } + virtual uint64_t getWritableVMSize() const { return fVMWritablSize; } + virtual uint64_t getReadOnlyVMSize() const { return fVMReadOnlySize; } + virtual const uint8_t* getDyldInfoExports() const { return fDyldInfoExports; } + virtual void setDyldInfoExports(const uint8_t* newExports) const { fDyldInfoExports = newExports; } + virtual void uuid(uuid_t u) const { memcpy(u, fUUID, 16); } + +private: + typedef typename A::P P; + typedef typename A::P::E E; + typedef typename A::P::uint_t pint_t; + + uint64_t segmentSize(const macho_segment_command* segCmd) const; + uint64_t segmentFileSize(const macho_segment_command* segCmd) const; + uint64_t segmentAlignment(const macho_segment_command* segCmd) const; + uint64_t sectionsSize(const macho_segment_command* segCmd) const; + uint64_t sectionsAlignment(const macho_segment_command* segCmd) const; + + bool validReadWriteSeg(const Segment& seg) const; + + static cpu_type_t arch(); + + const char* fPath; + uint64_t fOffset; + uint32_t fFileType; + ArchPair fArchPair; + uint32_t fFlags; + std::vector fSegments; + std::vector fLibraries; + const Segment* fLowSegment; + const Segment* fLowExecutableSegment; + const Segment* fLowWritableSegment; + const Segment* fLowReadOnlySegment; + Library fDylibID; + uint32_t fNameFileOffset; + time_t fMTime; + ino_t fInode; + uint64_t fVMSize; + uint64_t fVMExecutableSize; + uint64_t fVMWritablSize; + uint64_t fVMReadOnlySize; + const macho_linkedit_data_command

* fSplitSegInfo; + bool fHasSplitSegInfoV2; + bool fShareableLocation; + bool fDynamicLookupLinkage; + bool fMainExecutableLookupLinkage; + bool fIsDylib; + bool fHasDyldInfo; + bool fHasTooManyWritableSegments; + mutable const uint8_t* fDyldInfoExports; + uuid_t fUUID; +}; + + + +class UniversalMachOLayout +{ +public: + UniversalMachOLayout(const char* path, const std::set* onlyArchs=NULL); + ~UniversalMachOLayout() {} + + static const UniversalMachOLayout& find(const char* path, const std::set* onlyArchs=NULL); + const MachOLayoutAbstraction* getSlice(ArchPair ap) const; + const std::vector& allLayouts() const { return fLayouts; } + +private: + class CStringHash { + public: + size_t operator()(const char* __s) const { + size_t __h = 0; + for ( ; *__s; ++__s) + __h = 5 * __h + *__s; + return __h; + }; + }; + struct CStringEquals { + bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } + }; + typedef std::unordered_map PathToNode; + + static bool requestedSlice(const std::set* onlyArchs, cpu_type_t cpuType, cpu_subtype_t cpuSubType); + + static PathToNode fgLayoutCache; + const char* fPath; + std::vector fLayouts; +}; + +UniversalMachOLayout::PathToNode UniversalMachOLayout::fgLayoutCache; + + + + +const MachOLayoutAbstraction* UniversalMachOLayout::getSlice(ArchPair ap) const +{ + // use matching cputype and cpusubtype + for(std::vector::const_iterator it=fLayouts.begin(); it != fLayouts.end(); ++it) { + const MachOLayoutAbstraction* layout = *it; + if ( layout->getArchPair().arch == ap.arch ) { + switch ( ap.arch ) { + case CPU_TYPE_ARM: + case CPU_TYPE_X86_64: + if ( (layout->getArchPair().subtype & ~CPU_SUBTYPE_MASK) == (ap.subtype & ~CPU_SUBTYPE_MASK) ) + return layout; + break; + default: + return layout; + } + } + } + // if requesting x86_64h and it did not exist, try x86_64 as a fallback + if ((ap.arch == CPU_TYPE_X86_64) && (ap.subtype == CPU_SUBTYPE_X86_64_H)) { + ap.subtype = CPU_SUBTYPE_X86_64_ALL; + return this->getSlice(ap); + } + return NULL; +} + + +const UniversalMachOLayout& UniversalMachOLayout::find(const char* path, const std::set* onlyArchs) +{ + // look in cache + PathToNode::iterator pos = fgLayoutCache.find(path); + if ( pos != fgLayoutCache.end() ) + return *pos->second; + + // create UniversalMachOLayout + const UniversalMachOLayout* result = new UniversalMachOLayout(path, onlyArchs); + + // add it to cache + fgLayoutCache[result->fPath] = result; + + return *result; +} + + +bool UniversalMachOLayout::requestedSlice(const std::set* onlyArchs, cpu_type_t cpuType, cpu_subtype_t cpuSubType) +{ + if ( onlyArchs == NULL ) + return true; + // must match cputype and cpusubtype + for (std::set::const_iterator it = onlyArchs->begin(); it != onlyArchs->end(); ++it) { + ArchPair anArch = *it; + if ( cpuType == anArch.arch ) { + switch ( cpuType ) { + case CPU_TYPE_ARM: + if ( cpuSubType == anArch.subtype ) + return true; + break; + default: + return true; + } + } + } + return false; +} + + +UniversalMachOLayout::UniversalMachOLayout(const char* path, const std::set* onlyArchs) + : fPath(strdup(path)) +{ + // map in whole file + int fd = ::open(path, O_RDONLY, 0); + if ( fd == -1 ) { + int err = errno; + if ( err == ENOENT ) + throwf("file not found"); + else + throwf("can't open file, errno=%d", err); + } + struct stat stat_buf; + if ( fstat(fd, &stat_buf) == -1) + throwf("can't stat open file %s, errno=%d", path, errno); + if ( stat_buf.st_size < 20 ) + throwf("file too small %s", path); + uint8_t* p = (uint8_t*)::mmap(NULL, stat_buf.st_size, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0); + if ( p == (uint8_t*)(-1) ) + throwf("can't map file %s, errno=%d", path, errno); + ::close(fd); + + try { + // if fat file, process each architecture + const fat_header* fh = (fat_header*)p; + const mach_header* mh = (mach_header*)p; + if ( fh->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) { + // Fat header is always big-endian + const struct fat_arch* slices = (struct fat_arch*)(p + sizeof(struct fat_header)); + const uint32_t sliceCount = OSSwapBigToHostInt32(fh->nfat_arch); + for (uint32_t i=0; i < sliceCount; ++i) { + if ( requestedSlice(onlyArchs, OSSwapBigToHostInt32(slices[i].cputype), OSSwapBigToHostInt32(slices[i].cpusubtype)) ) { + uint32_t fileOffset = OSSwapBigToHostInt32(slices[i].offset); + if ( fileOffset > stat_buf.st_size ) { + throwf("malformed universal file, slice %u for architecture 0x%08X is beyond end of file: %s", + i, OSSwapBigToHostInt32(slices[i].cputype), path); + } + if ( (fileOffset+OSSwapBigToHostInt32(slices[i].size)) > stat_buf.st_size ) { + throwf("malformed universal file, slice %u for architecture 0x%08X is beyond end of file: %s", + i, OSSwapBigToHostInt32(slices[i].cputype), path); + } + try { + switch ( OSSwapBigToHostInt32(slices[i].cputype) ) { + case CPU_TYPE_I386: + fLayouts.push_back(new MachOLayout(&p[fileOffset], fileOffset, fPath, stat_buf.st_ino, stat_buf.st_mtime, stat_buf.st_uid)); + break; + case CPU_TYPE_X86_64: + fLayouts.push_back(new MachOLayout(&p[fileOffset], fileOffset, fPath, stat_buf.st_ino, stat_buf.st_mtime, stat_buf.st_uid)); + break; + case CPU_TYPE_ARM: + fLayouts.push_back(new MachOLayout(&p[fileOffset], fileOffset, fPath, stat_buf.st_ino, stat_buf.st_mtime, stat_buf.st_uid)); + break; + case CPU_TYPE_ARM64: + fLayouts.push_back(new MachOLayout(&p[fileOffset], fileOffset, fPath, stat_buf.st_ino, stat_buf.st_mtime, stat_buf.st_uid)); + break; + default: + throw "unknown slice in fat file"; + } + } + catch (const char* msg) { + fprintf(stderr, "warning: %s for %s\n", msg, path); + } + } + } + } + else { + try { + if ( (OSSwapLittleToHostInt32(mh->magic) == MH_MAGIC) && (OSSwapLittleToHostInt32(mh->cputype) == CPU_TYPE_I386)) { + if ( requestedSlice(onlyArchs, OSSwapLittleToHostInt32(mh->cputype), OSSwapLittleToHostInt32(mh->cpusubtype)) ) + fLayouts.push_back(new MachOLayout(mh, 0, fPath, stat_buf.st_ino, stat_buf.st_mtime, stat_buf.st_uid)); + } + else if ( (OSSwapLittleToHostInt32(mh->magic) == MH_MAGIC_64) && (OSSwapLittleToHostInt32(mh->cputype) == CPU_TYPE_X86_64)) { + if ( requestedSlice(onlyArchs, OSSwapLittleToHostInt32(mh->cputype), OSSwapLittleToHostInt32(mh->cpusubtype)) ) + fLayouts.push_back(new MachOLayout(mh, 0, fPath, stat_buf.st_ino, stat_buf.st_mtime, stat_buf.st_uid)); + } + else if ( (OSSwapLittleToHostInt32(mh->magic) == MH_MAGIC) && (OSSwapLittleToHostInt32(mh->cputype) == CPU_TYPE_ARM)) { + if ( requestedSlice(onlyArchs, OSSwapLittleToHostInt32(mh->cputype), OSSwapLittleToHostInt32(mh->cpusubtype)) ) + fLayouts.push_back(new MachOLayout(mh, 0, fPath, stat_buf.st_ino, stat_buf.st_mtime, stat_buf.st_uid)); + } + else if ( (OSSwapLittleToHostInt32(mh->magic) == MH_MAGIC_64) && (OSSwapLittleToHostInt32(mh->cputype) == CPU_TYPE_ARM64)) { + if ( requestedSlice(onlyArchs, OSSwapLittleToHostInt32(mh->cputype), OSSwapLittleToHostInt32(mh->cpusubtype)) ) + fLayouts.push_back(new MachOLayout(mh, 0, fPath, stat_buf.st_ino, stat_buf.st_mtime, stat_buf.st_uid)); + } + else { + throw "unknown file format"; + } + } + catch (const char* msg) { + fprintf(stderr, "warning: %s for %s\n", msg, path); + } + } + } + catch (...) { + ::munmap(p, stat_buf.st_size); + throw; + } +} + + +template +uint64_t MachOLayout::segmentSize(const macho_segment_command* segCmd) const +{ + // segments may have 16KB alignment padding at end, if so we can remove that in cache + if ( segCmd->nsects() > 0 ) { + const macho_section

* const sectionsStart = (macho_section

*)((uint8_t*)segCmd + sizeof(macho_segment_command

)); + const macho_section

* const lastSection = §ionsStart[segCmd->nsects()-1]; + uint64_t endSectAddr = lastSection->addr() + lastSection->size(); + uint64_t endSectAddrPage = (endSectAddr + 4095) & (-4096); + if ( endSectAddrPage < (segCmd->vmaddr() + segCmd->vmsize()) ) { + uint64_t size = endSectAddrPage - segCmd->vmaddr(); + //if ( size != segCmd->vmsize() ) + // fprintf(stderr, "trim %s size=0x%08llX instead of 0x%08llX for %s\n", + // segCmd->segname(), size, segCmd->vmsize(), getFilePath()); + return size; + } + } + return segCmd->vmsize(); +} + +template +uint64_t MachOLayout::segmentAlignment(const macho_segment_command* segCmd) const +{ + int p2align = 12; + if ( segCmd->nsects() > 0 ) { + const macho_section

* const sectionsStart = (macho_section

*)((uint8_t*)segCmd + sizeof(macho_segment_command

)); + const macho_section

* const sectionsEnd = §ionsStart[segCmd->nsects()-1]; + for (const macho_section

* sect=sectionsStart; sect < sectionsEnd; ++sect) { + if ( sect->align() > p2align ) + p2align = sect->align(); + } + } + return (1 << p2align); +} + +template +uint64_t MachOLayout::segmentFileSize(const macho_segment_command* segCmd) const +{ + // segments may have 16KB alignment padding at end, if so we can remove that in cache + if ( segCmd->nsects() > 0 ) { + uint64_t endOffset = segCmd->fileoff(); + const macho_section

* const sectionsStart = (macho_section

*)((uint8_t*)segCmd + sizeof(macho_segment_command

)); + const macho_section

* const sectionsEnd = §ionsStart[segCmd->nsects()]; + for (const macho_section

* sect=sectionsStart; sect < sectionsEnd; ++sect) { + if ( sect->offset() != 0 ) + endOffset = sect->offset() + sect->size(); + } + uint64_t size = (endOffset - segCmd->fileoff() + 4095) & (-4096); + //if ( size != segCmd->filesize() ) + // fprintf(stderr, "trim %s filesize=0x%08llX instead of 0x%08llX for %s\n", + // segCmd->segname(), size, segCmd->filesize(), getFilePath()); + return size; + } + return segCmd->filesize(); +} + +template +uint64_t MachOLayout::sectionsSize(const macho_segment_command* segCmd) const +{ + if ( segCmd->nsects() > 0 ) { + const macho_section

* const sectionsStart = (macho_section

*)((uint8_t*)segCmd + sizeof(macho_segment_command

)); + const macho_section

* const lastSection = §ionsStart[segCmd->nsects()-1]; + uint64_t endSectAddr = lastSection->addr() + lastSection->size(); + if ( endSectAddr < (segCmd->vmaddr() + segCmd->vmsize()) ) { + uint64_t size = endSectAddr - segCmd->vmaddr(); + return size; + } + } + return segCmd->vmsize(); +} + +template +uint64_t MachOLayout::sectionsAlignment(const macho_segment_command* segCmd) const +{ + int p2align = 4; + if ( hasSplitSegInfoV2() && (segCmd->nsects() > 0) ) { + const macho_section

* const sectionsStart = (macho_section

*)((uint8_t*)segCmd + sizeof(macho_segment_command

)); + const macho_section

* const sectionsEnd = §ionsStart[segCmd->nsects()-1]; + for (const macho_section

* sect=sectionsStart; sect < sectionsEnd; ++sect) { + if ( sect->align() > p2align ) + p2align = sect->align(); + } + } + return (1 << p2align); +} + + + +template +MachOLayout::MachOLayout(const void* machHeader, uint64_t offset, const char* path, ino_t inode, time_t modTime, uid_t uid) + : fPath(path), fOffset(offset), fArchPair(0,0), fMTime(modTime), fInode(inode), fSplitSegInfo(NULL), fHasSplitSegInfoV2(false), + fShareableLocation(false), fDynamicLookupLinkage(false), fMainExecutableLookupLinkage(false), fIsDylib(false), + fHasDyldInfo(false), fHasTooManyWritableSegments(false), fDyldInfoExports(NULL) +{ + fDylibID.name = NULL; + fDylibID.currentVersion = 0; + fDylibID.compatibilityVersion = 0; + bzero(fUUID, sizeof(fUUID)); + + const macho_header

* mh = (const macho_header

*)machHeader; + if ( mh->cputype() != arch() ) + throw "Layout object is wrong architecture"; + switch ( mh->filetype() ) { + case MH_DYLIB: + fIsDylib = true; + break; + case MH_BUNDLE: + case MH_EXECUTE: + case MH_DYLIB_STUB: + case MH_DYLINKER: + break; + default: + throw "file is not a mach-o final linked image"; + } + fFlags = mh->flags(); + fFileType = mh->filetype(); + fArchPair.arch = mh->cputype(); + fArchPair.subtype = mh->cpusubtype(); + + const macho_dyld_info_command

* dyldInfo = NULL; + const macho_symtab_command

* symbolTableCmd = NULL; + const macho_dysymtab_command

* dynamicSymbolTableCmd = NULL; + const macho_load_command

* const cmds = (macho_load_command

*)((uint8_t*)mh + sizeof(macho_header

)); + const uint32_t cmd_count = mh->ncmds(); + const macho_load_command

* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch ( cmd->cmd() ) { + case LC_ID_DYLIB: + { + macho_dylib_command

* dylib = (macho_dylib_command

*)cmd; + fDylibID.name = strdup(dylib->name()); + fDylibID.currentVersion = dylib->current_version(); + fDylibID.compatibilityVersion = dylib->compatibility_version(); + fNameFileOffset = dylib->name() - (char*)machHeader; + fShareableLocation = ( (strncmp(fDylibID.name, "/usr/lib/", 9) == 0) || (strncmp(fDylibID.name, "/System/Library/", 16) == 0) ); + } + break; + case LC_LOAD_DYLIB: + case LC_LOAD_WEAK_DYLIB: + case LC_REEXPORT_DYLIB: + case LC_LOAD_UPWARD_DYLIB: + { + macho_dylib_command

* dylib = (macho_dylib_command

*)cmd; + Library lib; + lib.name = strdup(dylib->name()); + lib.currentVersion = dylib->current_version(); + lib.compatibilityVersion = dylib->compatibility_version(); + lib.weakImport = ( cmd->cmd() == LC_LOAD_WEAK_DYLIB ); + fLibraries.push_back(lib); + } + break; + case LC_SEGMENT_SPLIT_INFO: + fSplitSegInfo = (macho_linkedit_data_command

*)cmd; + break; + case macho_segment_command

::CMD: + { + const macho_segment_command

* segCmd = (macho_segment_command

*)cmd; + fSegments.push_back(Segment(segCmd->vmaddr(), segmentSize(segCmd), segCmd->fileoff(), + segmentFileSize(segCmd), sectionsSize(segCmd), sectionsAlignment(segCmd), + segmentAlignment(segCmd), segCmd->initprot(), + segCmd->nsects(), segCmd->segname())); + } + break; + case LC_SYMTAB: + symbolTableCmd = (macho_symtab_command

*)cmd; + break; + case LC_DYSYMTAB: + dynamicSymbolTableCmd = (macho_dysymtab_command

*)cmd; + break; + case LC_DYLD_INFO: + case LC_DYLD_INFO_ONLY: + fHasDyldInfo = true; + dyldInfo = (struct macho_dyld_info_command

*)cmd; + break; + case LC_UUID: + { + const macho_uuid_command

* uc = (macho_uuid_command

*)cmd; + memcpy(&fUUID, uc->uuid(), 16); + } + break; + } + cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); + } + + fLowSegment = NULL; + fLowExecutableSegment = NULL; + fLowWritableSegment = NULL; + fLowReadOnlySegment = NULL; + fVMExecutableSize = 0; + fVMWritablSize = 0; + fVMReadOnlySize = 0; + fVMSize = 0; + const Segment* highSegment = NULL; + for(std::vector::const_iterator it = fSegments.begin(); it != fSegments.end(); ++it) { + const Segment& seg = *it; + if ( (fLowSegment == NULL) || (seg.address() < fLowSegment->address()) ) + fLowSegment = &seg; + if ( (highSegment == NULL) || (seg.address() > highSegment->address()) ) + highSegment = &seg; + if ( seg.executable() ) { + if ( (fLowExecutableSegment == NULL) || (seg.address() < fLowExecutableSegment->address()) ) + fLowExecutableSegment = &seg; + fVMExecutableSize += seg.size(); + } + else if ( seg.writable()) { + if ( (fLowWritableSegment == NULL) || (seg.address() < fLowWritableSegment->address()) ) + fLowWritableSegment = &seg; + fVMWritablSize += seg.size(); + if ( !validReadWriteSeg(seg) ) { + fHasTooManyWritableSegments = true; + } + } + else { + if ( (fLowReadOnlySegment == NULL) || (seg.address() < fLowReadOnlySegment->address()) ) + fLowReadOnlySegment = &seg; + fVMReadOnlySize += seg.size(); + } + } + if ( (highSegment != NULL) && (fLowSegment != NULL) ) + fVMSize = (highSegment->address() + highSegment->size() - fLowSegment->address() + 4095) & (-4096); + + // scan undefines looking, for magic ordinals + if ( (symbolTableCmd != NULL) && (dynamicSymbolTableCmd != NULL) ) { + const macho_nlist

* symbolTable = (macho_nlist

*)((uint8_t*)machHeader + symbolTableCmd->symoff()); + const uint32_t startUndefs = dynamicSymbolTableCmd->iundefsym(); + const uint32_t endUndefs = startUndefs + dynamicSymbolTableCmd->nundefsym(); + for (uint32_t i=startUndefs; i < endUndefs; ++i) { + uint8_t ordinal = GET_LIBRARY_ORDINAL(symbolTable[i].n_desc()); + if ( ordinal == DYNAMIC_LOOKUP_ORDINAL ) + fDynamicLookupLinkage = true; + else if ( ordinal == EXECUTABLE_ORDINAL ) + fMainExecutableLookupLinkage = true; + } + } + + if ( dyldInfo != NULL ) { + if ( dyldInfo->export_off() != 0 ) { + fDyldInfoExports = (uint8_t*)machHeader + dyldInfo->export_off(); + } + } + + if ( fSplitSegInfo != NULL ) { + const uint8_t* infoStart = (uint8_t*)machHeader + fSplitSegInfo->dataoff(); + fHasSplitSegInfoV2 = ( *infoStart == DYLD_CACHE_ADJ_V2_FORMAT ); + if ( !fHasSplitSegInfoV2 ) { + // split seg version not known when segments created + // v1 does not support packing, so simulate that by forcing alignment + for (Segment& seg : fSegments) { + seg.setSectionsAlignment(4096); + } + } + } + +} + +template <> cpu_type_t MachOLayout::arch() { return CPU_TYPE_I386; } +template <> cpu_type_t MachOLayout::arch() { return CPU_TYPE_X86_64; } +template <> cpu_type_t MachOLayout::arch() { return CPU_TYPE_ARM; } +template <> cpu_type_t MachOLayout::arch() { return CPU_TYPE_ARM64; } + +template <> +bool MachOLayout::validReadWriteSeg(const Segment& seg) const +{ + return (strcmp(seg.name(), "__DATA") == 0) || (strcmp(seg.name(), "__OBJC") == 0); +} + +template +bool MachOLayout::validReadWriteSeg(const Segment& seg) const +{ + return (strcmp(seg.name(), "__DATA") == 0); +} + + +template <> +bool MachOLayout::isSplitSeg() const +{ + return ( (this->getFlags() & MH_SPLIT_SEGS) != 0 ); +} + +template <> +bool MachOLayout::isSplitSeg() const +{ + return ( (this->getFlags() & MH_SPLIT_SEGS) != 0 ); +} + +template +bool MachOLayout::isSplitSeg() const +{ + return false; +} + +template +const MachOLayoutAbstraction::Segment* MachOLayout::getSegment(const char* name) const +{ + for(std::vector::const_iterator it = fSegments.begin(); it != fSegments.end(); ++it) { + const Segment& seg = *it; + if ( strcmp(seg.name(), name) == 0 ) + return &seg; + } + return NULL; +} + + + +#endif // __MACHO_LAYOUT__ + + + diff --git a/src/dyld-apple/launch-cache/MachORebaser.hpp b/src/dyld-apple/launch-cache/MachORebaser.hpp new file mode 100644 index 000000000..464220ac0 --- /dev/null +++ b/src/dyld-apple/launch-cache/MachORebaser.hpp @@ -0,0 +1,1124 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, 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@ + */ + +#ifndef __MACHO_REBASER__ +#define __MACHO_REBASER__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "MachOFileAbstraction.hpp" +#include "Architectures.hpp" +#include "MachOLayout.hpp" +#include "MachOTrie.hpp" + + + +class AbstractRebaser +{ +public: + virtual cpu_type_t getArchitecture() const = 0; + virtual uint64_t getBaseAddress() const = 0; + virtual uint64_t getVMSize() const = 0; + virtual bool rebase(std::vector&) = 0; +}; + + +template +class Rebaser : public AbstractRebaser +{ +public: + Rebaser(const MachOLayoutAbstraction&); + virtual ~Rebaser() {} + + virtual cpu_type_t getArchitecture() const; + virtual uint64_t getBaseAddress() const; + virtual uint64_t getVMSize() const; + virtual bool rebase(std::vector&); + +protected: + typedef typename A::P P; + typedef typename A::P::E E; + typedef typename A::P::uint_t pint_t; + + pint_t* mappedAddressForNewAddress(pint_t vmaddress); + pint_t getSlideForNewAddress(pint_t newAddress); + +private: + void adjustLoadCommands(); + void adjustSymbolTable(); + void adjustDATA(); + void adjustCode(); + void applyRebaseInfo(std::vector& pointersInData); + void adjustReferencesUsingInfoV2(std::vector& pointersInData); + void adjustReference(uint32_t kind, uint8_t* mappedAddr, uint64_t fromNewAddress, uint64_t toNewAddress, int64_t adjust, int64_t targetSlide, + uint64_t imageStartAddress, uint64_t imageEndAddress, std::vector& pointersInData); + bool adjustExportInfo(); + void doRebase(int segIndex, uint64_t segOffset, uint8_t type, std::vector& pointersInData); + pint_t getSlideForVMAddress(pint_t vmaddress); + pint_t maskedVMAddress(pint_t vmaddress); + pint_t* mappedAddressForVMAddress(pint_t vmaddress); + const uint8_t* doCodeUpdateForEachULEB128Address(const uint8_t* p, uint8_t kind, uint64_t orgBaseAddress, int64_t codeToDataDelta, int64_t codeToImportDelta); + void doCodeUpdate(uint8_t kind, uint64_t address, int64_t codeToDataDelta, int64_t codeToImportDelta); + void doLocalRelocation(const macho_relocation_info

* reloc); + bool unequalSlides() const; + +protected: + const macho_header

* fHeader; + uint8_t* fLinkEditBase; // add file offset to this to get linkedit content + const MachOLayoutAbstraction& fLayout; +private: + const macho_symtab_command

* fSymbolTable; + const macho_dysymtab_command

* fDynamicSymbolTable; + const macho_dyld_info_command

* fDyldInfo; + const macho_linkedit_data_command

* fSplitSegInfo; + bool fSplittingSegments; + bool fHasSplitSegInfoV2; + std::vector fSectionOffsetsInSegment; +}; + + +template +Rebaser::Rebaser(const MachOLayoutAbstraction& layout) + : fLayout(layout), fLinkEditBase(0), fSymbolTable(NULL), fDynamicSymbolTable(NULL), + fDyldInfo(NULL), fSplitSegInfo(NULL), fSplittingSegments(false), fHasSplitSegInfoV2(false) +{ + fHeader = (const macho_header

*)fLayout.getSegments()[0].mappedAddress(); + switch ( fHeader->filetype() ) { + case MH_DYLIB: + case MH_BUNDLE: + break; + default: + throw "file is not a dylib or bundle"; + } + + const std::vector& segments = fLayout.getSegments(); + for(std::vector::const_iterator it = segments.begin(); it != segments.end(); ++it) { + const MachOLayoutAbstraction::Segment& seg = *it; + if ( strcmp(seg.name(), "__LINKEDIT") == 0 ) { + fLinkEditBase = (uint8_t*)seg.mappedAddress() - seg.fileOffset(); + break; + } + } + if ( fLinkEditBase == NULL ) + throw "no __LINKEDIT segment"; + + // get symbol table info + const macho_load_command

* const cmds = (macho_load_command

*)((uint8_t*)fHeader + sizeof(macho_header

)); + const uint32_t cmd_count = fHeader->ncmds(); + const macho_load_command

* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd()) { + case LC_SYMTAB: + fSymbolTable = (macho_symtab_command

*)cmd; + break; + case LC_DYSYMTAB: + fDynamicSymbolTable = (macho_dysymtab_command

*)cmd; + break; + case LC_DYLD_INFO: + case LC_DYLD_INFO_ONLY: + fDyldInfo = (macho_dyld_info_command

*)cmd; + break; + case LC_SEGMENT_SPLIT_INFO: + fSplitSegInfo = (macho_linkedit_data_command

*)cmd; + break; + case macho_segment_command

::CMD: { + // update segment/section file offsets + macho_segment_command

* segCmd = (macho_segment_command

*)cmd; + macho_section

* const sectionsStart = (macho_section

*)((char*)segCmd + sizeof(macho_segment_command

)); + macho_section

* const sectionsEnd = §ionsStart[segCmd->nsects()]; + for(macho_section

* sect = sectionsStart; sect < sectionsEnd; ++sect) { + fSectionOffsetsInSegment.push_back(sect->addr() - segCmd->vmaddr()); + } + } + } + cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); + } + + if ( fDyldInfo == NULL ) + throw "no LC_DYLD_INFO load command"; + + fSplittingSegments = layout.hasSplitSegInfo() && this->unequalSlides(); + + if ( fSplitSegInfo != NULL ) { + const uint8_t* infoStart = &fLinkEditBase[fSplitSegInfo->dataoff()]; + fHasSplitSegInfoV2 = ( *infoStart == DYLD_CACHE_ADJ_V2_FORMAT ); + } +} + +template <> cpu_type_t Rebaser::getArchitecture() const { return CPU_TYPE_I386; } +template <> cpu_type_t Rebaser::getArchitecture() const { return CPU_TYPE_X86_64; } +template <> cpu_type_t Rebaser::getArchitecture() const { return CPU_TYPE_ARM; } +template <> cpu_type_t Rebaser::getArchitecture() const { return CPU_TYPE_ARM64; } + +template +bool Rebaser::unequalSlides() const +{ + const std::vector& segments = fLayout.getSegments(); + uint64_t slide = segments[0].newAddress() - segments[0].address(); + for(std::vector::const_iterator it = segments.begin(); it != segments.end(); ++it) { + const MachOLayoutAbstraction::Segment& seg = *it; + if ( (seg.newAddress() - seg.address()) != slide ) + return true; + } + return false; +} + +template +uint64_t Rebaser::getBaseAddress() const +{ + return fLayout.getSegments()[0].address(); +} + +template +uint64_t Rebaser::getVMSize() const +{ + uint64_t highestVMAddress = 0; + const std::vector& segments = fLayout.getSegments(); + for(std::vector::const_iterator it = segments.begin(); it != segments.end(); ++it) { + const MachOLayoutAbstraction::Segment& seg = *it; + if ( seg.address() > highestVMAddress ) + highestVMAddress = seg.address(); + } + return (((highestVMAddress - getBaseAddress()) + 4095) & (-4096)); +} + + + +template +bool Rebaser::rebase(std::vector& pointersInData) +{ + if ( fHasSplitSegInfoV2 ) { + this->adjustReferencesUsingInfoV2(pointersInData); + } + else { + //fprintf(stderr, "warning: dylib with old split-seg info: %s\n", fLayout.getFilePath()); + // update writable segments that have internal pointers + this->applyRebaseInfo(pointersInData); + + // if splitting segments, update code-to-data references + this->adjustCode(); + } + + // update load commands + this->adjustLoadCommands(); + + // update symbol table + this->adjustSymbolTable(); + + // update export info + return this->adjustExportInfo(); +} + +template +void Rebaser::adjustLoadCommands() +{ + const macho_load_command

* const cmds = (macho_load_command

*)((uint8_t*)fHeader + sizeof(macho_header

)); + const uint32_t cmd_count = fHeader->ncmds(); + const macho_load_command

* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch ( cmd->cmd() ) { + case LC_ID_DYLIB: + if ( (fHeader->flags() & MH_PREBOUND) != 0 ) { + // clear timestamp so that any prebound clients are invalidated + macho_dylib_command

* dylib = (macho_dylib_command

*)cmd; + dylib->set_timestamp(1); + } + break; + case LC_LOAD_DYLIB: + case LC_LOAD_WEAK_DYLIB: + case LC_REEXPORT_DYLIB: + case LC_LOAD_UPWARD_DYLIB: + if ( (fHeader->flags() & MH_PREBOUND) != 0 ) { + // clear expected timestamps so that this image will load with invalid prebinding + macho_dylib_command

* dylib = (macho_dylib_command

*)cmd; + dylib->set_timestamp(2); + } + break; + case macho_routines_command

::CMD: + // update -init command + { + struct macho_routines_command

* routines = (struct macho_routines_command

*)cmd; + routines->set_init_address(routines->init_address() + this->getSlideForVMAddress(routines->init_address())); + } + break; + case macho_segment_command

::CMD: + // update segment commands + { + macho_segment_command

* seg = (macho_segment_command

*)cmd; + pint_t slide = this->getSlideForVMAddress(seg->vmaddr()); + seg->set_vmaddr(seg->vmaddr() + slide); + macho_section

* const sectionsStart = (macho_section

*)((char*)seg + sizeof(macho_segment_command

)); + macho_section

* const sectionsEnd = §ionsStart[seg->nsects()]; + for(macho_section

* sect = sectionsStart; sect < sectionsEnd; ++sect) { + sect->set_addr(sect->addr() + slide); + } + } + break; + } + cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); + } +} + +template <> +uint64_t Rebaser::maskedVMAddress(pint_t vmaddress) +{ + return (vmaddress & 0x0FFFFFFFFFFFFFFF); +} + +template +typename A::P::uint_t Rebaser::maskedVMAddress(pint_t vmaddress) +{ + return vmaddress; +} + + +template +typename A::P::uint_t Rebaser::getSlideForVMAddress(pint_t vmaddress) +{ + pint_t vmaddr = this->maskedVMAddress(vmaddress); + const std::vector& segments = fLayout.getSegments(); + for(std::vector::const_iterator it = segments.begin(); it != segments.end(); ++it) { + const MachOLayoutAbstraction::Segment& seg = *it; + if ( (seg.address() <= vmaddr) && (seg.size() != 0) && ((vmaddr < (seg.address()+seg.size())) || (seg.address() == vmaddr)) ) { + return seg.newAddress() - seg.address(); + } + } + throwf("vm address 0x%08llX not found", (uint64_t)vmaddr); +} + + +template +typename A::P::uint_t* Rebaser::mappedAddressForVMAddress(pint_t vmaddress) +{ + pint_t vmaddr = this->maskedVMAddress(vmaddress); + const std::vector& segments = fLayout.getSegments(); + for(std::vector::const_iterator it = segments.begin(); it != segments.end(); ++it) { + const MachOLayoutAbstraction::Segment& seg = *it; + if ( (seg.address() <= vmaddr) && (vmaddr < (seg.address()+seg.size())) ) { + return (pint_t*)((vmaddr - seg.address()) + (uint8_t*)seg.mappedAddress()); + } + } + throwf("mappedAddressForVMAddress(0x%08llX) not found", (uint64_t)vmaddr); +} + +template +typename A::P::uint_t* Rebaser::mappedAddressForNewAddress(pint_t vmaddress) +{ + const std::vector& segments = fLayout.getSegments(); + for(std::vector::const_iterator it = segments.begin(); it != segments.end(); ++it) { + const MachOLayoutAbstraction::Segment& seg = *it; + if ( (seg.newAddress() <= vmaddress) && (vmaddress < (seg.newAddress()+seg.size())) ) { + return (pint_t*)((vmaddress - seg.newAddress()) + (uint8_t*)seg.mappedAddress()); + } + } + throwf("mappedAddressForNewAddress(0x%08llX) not found", (uint64_t)vmaddress); +} + +template +typename A::P::uint_t Rebaser::getSlideForNewAddress(pint_t newAddress) +{ + const std::vector& segments = fLayout.getSegments(); + for(std::vector::const_iterator it = segments.begin(); it != segments.end(); ++it) { + const MachOLayoutAbstraction::Segment& seg = *it; + if ( (seg.newAddress() <= newAddress) && (newAddress < (seg.newAddress()+seg.size())) ) { + return seg.newAddress() - seg.address(); + } + } + throwf("new address 0x%08llX not found", (uint64_t)newAddress); +} + +template +void Rebaser::adjustSymbolTable() +{ + macho_nlist

* symbolTable = (macho_nlist

*)(&fLinkEditBase[fSymbolTable->symoff()]); + + // walk all exports and slide their n_value + macho_nlist

* lastExport = &symbolTable[fDynamicSymbolTable->iextdefsym()+fDynamicSymbolTable->nextdefsym()]; + for (macho_nlist

* entry = &symbolTable[fDynamicSymbolTable->iextdefsym()]; entry < lastExport; ++entry) { + if ( (entry->n_type() & N_TYPE) == N_SECT ) + entry->set_n_value(entry->n_value() + this->getSlideForVMAddress(entry->n_value())); + } + + // walk all local symbols and slide their n_value (don't adjust any stabs) + macho_nlist

* lastLocal = &symbolTable[fDynamicSymbolTable->ilocalsym()+fDynamicSymbolTable->nlocalsym()]; + for (macho_nlist

* entry = &symbolTable[fDynamicSymbolTable->ilocalsym()]; entry < lastLocal; ++entry) { + if ( (entry->n_sect() != NO_SECT) && ((entry->n_type() & N_STAB) == 0) ) + entry->set_n_value(entry->n_value() + this->getSlideForVMAddress(entry->n_value())); + } +} + +template +bool Rebaser::adjustExportInfo() +{ + // if no export info, nothing to adjust + if ( fDyldInfo->export_size() == 0 ) + return true; + + // since export info addresses are offsets from mach_header, everything in __TEXT is fine + // only __DATA addresses need to be updated + const uint8_t* start = fLayout.getDyldInfoExports(); + const uint8_t* end = &start[fDyldInfo->export_size()]; + std::vector originalExports; + try { + parseTrie(start, end, originalExports); + } + catch (const char* msg) { + throwf("%s in %s", msg, fLayout.getFilePath()); + } + + std::vector newExports; + newExports.reserve(originalExports.size()); + pint_t baseAddress = this->getBaseAddress(); + pint_t baseAddressSlide = this->getSlideForVMAddress(baseAddress); + for (std::vector::iterator it=originalExports.begin(); it != originalExports.end(); ++it) { + // remove symbols used by the static linker only + if ( (strncmp(it->name, "$ld$", 4) == 0) + || (strncmp(it->name, ".objc_class_name",16) == 0) + || (strncmp(it->name, ".objc_category_name",19) == 0) ) { + //fprintf(stderr, "ignoring symbol %s\n", it->name); + continue; + } + // adjust symbols in slid segments + //uint32_t oldOffset = it->address; + it->address += (this->getSlideForVMAddress(it->address + baseAddress) - baseAddressSlide); + //fprintf(stderr, "orig=0x%08X, new=0x%08llX, sym=%s\n", oldOffset, it->address, it->name); + newExports.push_back(*it); + } + + // rebuild export trie + std::vector newExportTrieBytes; + newExportTrieBytes.reserve(fDyldInfo->export_size()); + mach_o::trie::makeTrie(newExports, newExportTrieBytes); + // align + while ( (newExportTrieBytes.size() % sizeof(pint_t)) != 0 ) + newExportTrieBytes.push_back(0); + + uint32_t newExportsSize = newExportTrieBytes.size(); + if ( newExportsSize <= fDyldInfo->export_size() ) { + // override existing trie in place + uint8_t *realStart = &fLinkEditBase[fDyldInfo->export_off()]; + bzero(realStart, fDyldInfo->export_size()); + memcpy(realStart, &newExportTrieBytes[0], newExportsSize); + fLayout.setDyldInfoExports(realStart); + return true; + } + else { + // allocate new buffer and set export_off in layout object to use new buffer instead + uint8_t* sideTrie = new uint8_t[newExportsSize]; + memcpy(sideTrie, &newExportTrieBytes[0], newExportsSize); + fLayout.setDyldInfoExports(sideTrie); + ((macho_dyld_info_command

*)fDyldInfo)->set_export_off(0); // invalidate old trie + ((macho_dyld_info_command

*)fDyldInfo)->set_export_size(newExportsSize); + return false; + } +} + + + +template +void Rebaser::doCodeUpdate(uint8_t kind, uint64_t address, int64_t codeToDataDelta, int64_t codeToImportDelta) +{ + //fprintf(stderr, "doCodeUpdate(kind=%d, address=0x%0llX, dataDelta=0x%08llX, importDelta=0x%08llX, path=%s)\n", + // kind, address, codeToDataDelta, codeToImportDelta, fLayout.getFilePath()); + uint32_t* p; + uint32_t instruction; + uint32_t value; + uint64_t value64; + switch (kind) { + case 1: // 32-bit pointer + p = (uint32_t*)mappedAddressForVMAddress(address); + value = A::P::E::get32(*p); + value += codeToDataDelta; + A::P::E::set32(*p, value); + break; + case 2: // 64-bit pointer + p = (uint32_t*)mappedAddressForVMAddress(address); + value64 = A::P::E::get64(*(uint64_t*)p); + value64 += codeToDataDelta; + A::P::E::set64(*(uint64_t*)p, value64); + break; + case 4: // only used for i386, a reference to something in the IMPORT segment + p = (uint32_t*)mappedAddressForVMAddress(address); + value = A::P::E::get32(*p); + value += codeToImportDelta; + A::P::E::set32(*p, value); + break; + case 5: // used by thumb2 movw + p = (uint32_t*)mappedAddressForVMAddress(address); + instruction = A::P::E::get32(*p); + // codeToDataDelta is always a multiple of 4096, so only top 4 bits of lo16 will ever need adjusting + value = (instruction & 0x0000000F) + (codeToDataDelta >> 12); + instruction = (instruction & 0xFFFFFFF0) | (value & 0x0000000F); + A::P::E::set32(*p, instruction); + break; + case 6: // used by ARM movw + p = (uint32_t*)mappedAddressForVMAddress(address); + instruction = A::P::E::get32(*p); + // codeToDataDelta is always a multiple of 4096, so only top 4 bits of lo16 will ever need adjusting + value = ((instruction & 0x000F0000) >> 16) + (codeToDataDelta >> 12); + instruction = (instruction & 0xFFF0FFFF) | ((value <<16) & 0x000F0000); + A::P::E::set32(*p, instruction); + break; + case 0x10: + case 0x11: + case 0x12: + case 0x13: + case 0x14: + case 0x15: + case 0x16: + case 0x17: + case 0x18: + case 0x19: + case 0x1A: + case 0x1B: + case 0x1C: + case 0x1D: + case 0x1E: + case 0x1F: + // used by thumb2 movt (low nibble of kind is high 4-bits of paired movw) + { + p = (uint32_t*)mappedAddressForVMAddress(address); + instruction = A::P::E::get32(*p); + // extract 16-bit value from instruction + uint32_t i = ((instruction & 0x00000400) >> 10); + uint32_t imm4 = (instruction & 0x0000000F); + uint32_t imm3 = ((instruction & 0x70000000) >> 28); + uint32_t imm8 = ((instruction & 0x00FF0000) >> 16); + uint32_t imm16 = (imm4 << 12) | (i << 11) | (imm3 << 8) | imm8; + // combine with codeToDataDelta and kind nibble + uint32_t targetValue = (imm16 << 16) | ((kind & 0xF) << 12); + uint32_t newTargetValue = targetValue + codeToDataDelta; + // construct new bits slices + uint32_t imm4_ = (newTargetValue & 0xF0000000) >> 28; + uint32_t i_ = (newTargetValue & 0x08000000) >> 27; + uint32_t imm3_ = (newTargetValue & 0x07000000) >> 24; + uint32_t imm8_ = (newTargetValue & 0x00FF0000) >> 16; + // update instruction to match codeToDataDelta + uint32_t newInstruction = (instruction & 0x8F00FBF0) | imm4_ | (i_ << 10) | (imm3_ << 28) | (imm8_ << 16); + A::P::E::set32(*p, newInstruction); + } + break; + case 0x20: + case 0x21: + case 0x22: + case 0x23: + case 0x24: + case 0x25: + case 0x26: + case 0x27: + case 0x28: + case 0x29: + case 0x2A: + case 0x2B: + case 0x2C: + case 0x2D: + case 0x2E: + case 0x2F: + // used by arm movt (low nibble of kind is high 4-bits of paired movw) + { + p = (uint32_t*)mappedAddressForVMAddress(address); + instruction = A::P::E::get32(*p); + // extract 16-bit value from instruction + uint32_t imm4 = ((instruction & 0x000F0000) >> 16); + uint32_t imm12 = (instruction & 0x00000FFF); + uint32_t imm16 = (imm4 << 12) | imm12; + // combine with codeToDataDelta and kind nibble + uint32_t targetValue = (imm16 << 16) | ((kind & 0xF) << 12); + uint32_t newTargetValue = targetValue + codeToDataDelta; + // construct new bits slices + uint32_t imm4_ = (newTargetValue & 0xF0000000) >> 28; + uint32_t imm12_ = (newTargetValue & 0x0FFF0000) >> 16; + // update instruction to match codeToDataDelta + uint32_t newInstruction = (instruction & 0xFFF0F000) | (imm4_ << 16) | imm12_; + A::P::E::set32(*p, newInstruction); + } + break; + case 3: // used for arm64 ADRP + p = (uint32_t*)mappedAddressForVMAddress(address); + instruction = A::P::E::get32(*p); + if ( (instruction & 0x9F000000) == 0x90000000 ) { + // codeToDataDelta is always a multiple of 4096, so only top 4 bits of lo16 will ever need adjusting + value64 = ((instruction & 0x60000000) >> 17) | ((instruction & 0x00FFFFE0) << 9); + value64 += codeToDataDelta; + instruction = (instruction & 0x9F00001F) | ((value64 << 17) & 0x60000000) | ((value64 >> 9) & 0x00FFFFE0); + A::P::E::set32(*p, instruction); + } + break; + default: + throwf("invalid kind=%d in split seg info", kind); + } +} + +template +const uint8_t* Rebaser::doCodeUpdateForEachULEB128Address(const uint8_t* p, uint8_t kind, uint64_t orgBaseAddress, int64_t codeToDataDelta, int64_t codeToImportDelta) +{ + uint64_t address = 0; + uint64_t delta = 0; + uint32_t shift = 0; + bool more = true; + do { + uint8_t byte = *p++; + delta |= ((byte & 0x7F) << shift); + shift += 7; + if ( byte < 0x80 ) { + if ( delta != 0 ) { + address += delta; + doCodeUpdate(kind, address+orgBaseAddress, codeToDataDelta, codeToImportDelta); + delta = 0; + shift = 0; + } + else { + more = false; + } + } + } while (more); + return p; +} + +template +void Rebaser::adjustCode() +{ + if ( fSplittingSegments ) { + // get uleb128 compressed runs of code addresses to update + const uint8_t* infoStart = &fLinkEditBase[fSplitSegInfo->dataoff()]; + const uint8_t* infoEnd = &infoStart[fSplitSegInfo->datasize()];; + // calculate how much we need to slide writable segments + const uint64_t orgBaseAddress = this->getBaseAddress(); + int64_t codeToDataDelta = 0; + int64_t codeToImportDelta = 0; + const std::vector& segments = fLayout.getSegments(); + const MachOLayoutAbstraction::Segment& codeSeg = segments[0]; + for(std::vector::const_iterator it = segments.begin(); it != segments.end(); ++it) { + const MachOLayoutAbstraction::Segment& dataSeg = *it; + if ( dataSeg.writable() ) { + if ( (strcmp(dataSeg.name(), "__DATA") != 0) && (strcmp(dataSeg.name(), "__OBJC") != 0) ) + throwf("only one rw segment named '__DATA' can be used in dylibs placed in the dyld shared cache (%s)", fLayout.getFilePath()); + codeToDataDelta = (dataSeg.newAddress() - codeSeg.newAddress()) - (dataSeg.address() - codeSeg.address()); + } + } + // decompress and call doCodeUpdate() on each address + for(const uint8_t* p = infoStart; (*p != 0) && (p < infoEnd);) { + uint8_t kind = *p++; + p = this->doCodeUpdateForEachULEB128Address(p, kind, orgBaseAddress, codeToDataDelta, codeToImportDelta); + } + } +} + +template +void Rebaser::doRebase(int segIndex, uint64_t segOffset, uint8_t type, std::vector& pointersInData) +{ + const std::vector& segments = fLayout.getSegments(); + if ( segIndex > segments.size() ) + throw "bad segment index in rebase info"; + const MachOLayoutAbstraction::Segment& seg = segments[segIndex]; + uint8_t* mappedAddr = (uint8_t*)seg.mappedAddress() + segOffset; + pint_t* mappedAddrP = (pint_t*)mappedAddr; + uint32_t* mappedAddr32 = (uint32_t*)mappedAddr; + pint_t valueP; + pint_t valuePnew; + uint32_t value32; + int32_t svalue32; + int32_t svalue32new; + switch ( type ) { + case REBASE_TYPE_POINTER: + valueP= P::getP(*mappedAddrP); + try { + P::setP(*mappedAddrP, valueP + this->getSlideForVMAddress(valueP)); + } + catch (const char* msg) { + throwf("at offset=0x%08llX in seg=%s, pointer cannot be rebased because it does not point to __TEXT or __DATA. %s\n", + segOffset, seg.name(), msg); + } + break; + + case REBASE_TYPE_TEXT_ABSOLUTE32: + value32 = E::get32(*mappedAddr32); + E::set32(*mappedAddr32, value32 + this->getSlideForVMAddress(value32)); + break; + + case REBASE_TYPE_TEXT_PCREL32: + svalue32 = E::get32(*mappedAddr32); + valueP = seg.address() + segOffset + 4 + svalue32; + valuePnew = valueP + this->getSlideForVMAddress(valueP); + svalue32new = seg.address() + segOffset + 4 - valuePnew; + E::set32(*mappedAddr32, svalue32new); + break; + + default: + throw "bad rebase type"; + } + pointersInData.push_back(mappedAddr); +} + + +template +void Rebaser::applyRebaseInfo(std::vector& pointersInData) +{ + const uint8_t* p = &fLinkEditBase[fDyldInfo->rebase_off()]; + const uint8_t* end = &p[fDyldInfo->rebase_size()]; + + uint8_t type = 0; + int segIndex; + uint64_t segOffset = 0; + uint32_t count; + uint32_t skip; + bool done = false; + while ( !done && (p < end) ) { + uint8_t immediate = *p & REBASE_IMMEDIATE_MASK; + uint8_t opcode = *p & REBASE_OPCODE_MASK; + ++p; + switch (opcode) { + case REBASE_OPCODE_DONE: + done = true; + break; + case REBASE_OPCODE_SET_TYPE_IMM: + type = immediate; + break; + case REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: + segIndex = immediate; + segOffset = read_uleb128(p, end); + break; + case REBASE_OPCODE_ADD_ADDR_ULEB: + segOffset += read_uleb128(p, end); + break; + case REBASE_OPCODE_ADD_ADDR_IMM_SCALED: + segOffset += immediate*sizeof(pint_t); + break; + case REBASE_OPCODE_DO_REBASE_IMM_TIMES: + for (int i=0; i < immediate; ++i) { + doRebase(segIndex, segOffset, type, pointersInData); + segOffset += sizeof(pint_t); + } + break; + case REBASE_OPCODE_DO_REBASE_ULEB_TIMES: + count = read_uleb128(p, end); + for (uint32_t i=0; i < count; ++i) { + doRebase(segIndex, segOffset, type, pointersInData); + segOffset += sizeof(pint_t); + } + break; + case REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB: + doRebase(segIndex, segOffset, type, pointersInData); + segOffset += read_uleb128(p, end) + sizeof(pint_t); + break; + case REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB: + count = read_uleb128(p, end); + skip = read_uleb128(p, end); + for (uint32_t i=0; i < count; ++i) { + doRebase(segIndex, segOffset, type, pointersInData); + segOffset += skip + sizeof(pint_t); + } + break; + default: + throwf("bad rebase opcode %d", *p); + } + } +} + +template <> +void Rebaser::adjustReference(uint32_t kind, uint8_t* mappedAddr, uint64_t fromNewAddress, uint64_t toNewAddress, int64_t adjust, int64_t targetSlide, + uint64_t imageStartAddress, uint64_t imageEndAddress, std::vector& pointersInData) +{ + uint64_t value64; + uint64_t* mappedAddr64; + uint32_t value32; + uint32_t* mappedAddr32; + uint32_t instruction; + int64_t offsetAdjust; + switch ( kind ) { + case DYLD_CACHE_ADJ_V2_DELTA_32: + mappedAddr32 = (uint32_t*)mappedAddr; + value32 = arm64::P::E::get32(*mappedAddr32); + E::set32(*mappedAddr32, value32 + adjust); + break; + case DYLD_CACHE_ADJ_V2_POINTER_64: + mappedAddr64 = (uint64_t*)mappedAddr; + if ( toNewAddress != (E::get64(*mappedAddr64) + targetSlide) ) + throwf("bad DYLD_CACHE_ADJ_V2_POINTER_64 value not as expected at address 0x%llX\n", fromNewAddress); + E::set64(*mappedAddr64, toNewAddress); + pointersInData.push_back(mappedAddr); + break; + case DYLD_CACHE_ADJ_V2_DELTA_64: + mappedAddr64 = (uint64_t*)mappedAddr; + value64 = arm64::P::E::get64(*mappedAddr64); + E::set64(*mappedAddr64, value64 + adjust); + break; + case DYLD_CACHE_ADJ_V2_IMAGE_OFF_32: + mappedAddr32 = (uint32_t*)mappedAddr; + value64 = toNewAddress - imageStartAddress; + E::set32(*mappedAddr32, (uint32_t)value64); + break; + case DYLD_CACHE_ADJ_V2_ARM64_ADRP: + mappedAddr32 = (uint32_t*)mappedAddr; + instruction = arm64::P::E::get32(*mappedAddr32); + if ( (instruction & 0x9F000000) == 0x90000000 ) { + //value64 = ((instruction & 0x60000000) >> 17) | ((instruction & 0x00FFFFE0) << 9); + uint32_t newPage21 = ((toNewAddress & ~0xFFF) - (fromNewAddress & ~0xFFF)) >> 12; + instruction = (instruction & 0x9F00001F) | ((newPage21 << 29) & 0x60000000) | ((newPage21 << 3) & 0x00FFFFE0); + arm64::P::E::set32(*mappedAddr32, instruction); + } + else { + // ADRP instructions are sometimes optimized to other instructions (e.g. ADR) after the split-seg-info is generated + } + break; + case DYLD_CACHE_ADJ_V2_ARM64_OFF12: + mappedAddr32 = (uint32_t*)mappedAddr; + instruction = arm64::P::E::get32(*mappedAddr32); + offsetAdjust = (adjust & 0xFFF); + if ( offsetAdjust == 0 ) + break; + if ( (instruction & 0x3B000000) == 0x39000000 ) { + // LDR/STR imm12 + if ( offsetAdjust != 0 ) { + uint32_t encodedAddend = ((instruction & 0x003FFC00) >> 10); + uint32_t newAddend; + switch ( instruction & 0xC0000000 ) { + case 0x00000000: + if ( (instruction & 0x04800000) == 0x04800000 ) { + if ( offsetAdjust & 0xF ) + throwf("can't adjust off12 scale=16 instruction by %lld bytes at mapped address=%p", offsetAdjust, mappedAddr); + if ( encodedAddend*16 >= 4096 ) + throwf("off12 scale=16 instruction points outside its page at mapped address=%p", mappedAddr); + newAddend = (encodedAddend + offsetAdjust/16) % 256; + } + else { + // scale=1 + newAddend = encodedAddend + offsetAdjust; + } + break; + case 0x40000000: + if ( offsetAdjust & 1 ) + throwf("can't adjust off12 scale=2 instruction by %lld bytes at mapped address=%p", offsetAdjust, mappedAddr); + if ( encodedAddend*2 >= 4096 ) + throwf("off12 scale=2 instruction points outside its page at mapped address=%p", mappedAddr); + newAddend = (encodedAddend + offsetAdjust/2) % 2048; + break; + case 0x80000000: + if ( offsetAdjust & 3 ) + throwf("can't adjust off12 scale=4 instruction by %lld bytes at mapped address=%p", offsetAdjust, mappedAddr); + if ( encodedAddend*4 >= 4096 ) + throwf("off12 scale=4 instruction points outside its page at mapped address=%p", mappedAddr); + newAddend = (encodedAddend + offsetAdjust/4) % 1024; + break; + case 0xC0000000: + if ( offsetAdjust & 7 ) + throwf("can't adjust off12 scale=8 instruction by %lld bytes at mapped address=%p", offsetAdjust, mappedAddr); + if ( encodedAddend*8 >= 4096 ) + throwf("off12 scale=8 instruction points outside its page at mapped address=%p", mappedAddr); + newAddend = (encodedAddend + offsetAdjust/8) % 512; + break; + } + uint32_t newInstruction = (instruction & 0xFFC003FF) | (newAddend << 10); + arm64::P::E::set32(*mappedAddr32, newInstruction); + } + } + else if ( (instruction & 0xFFC00000) == 0x91000000 ) { + // ADD imm12 + if ( instruction & 0x00C00000 ) + throwf("ADD off12 uses shift at mapped address=%p", mappedAddr); + uint32_t encodedAddend = ((instruction & 0x003FFC00) >> 10); + uint32_t newAddend = (encodedAddend + offsetAdjust) & 0xFFF; + uint32_t newInstruction = (instruction & 0xFFC003FF) | (newAddend << 10); + arm64::P::E::set32(*mappedAddr32, newInstruction); + } + else if ( instruction != 0xD503201F ) { + // ignore imm12 instructions optimized into a NOP, but warn about others + fprintf(stderr, "unknown off12 instruction 0x%08X at 0x%0llX in %s\n", instruction, fromNewAddress, fLayout.getFilePath()); + } + break; + case DYLD_CACHE_ADJ_V2_ARM64_BR26: + // nothing to do with calls to stubs + break; + default: + throwf("unknown split seg kind=%d", kind); + } +} + +static bool isThumbMovw(uint32_t instruction) +{ + return ( (instruction & 0x8000FBF0) == 0x0000F240 ); +} + +static bool isThumbMovt(uint32_t instruction) +{ + return ( (instruction & 0x8000FBF0) == 0x0000F2C0 ); +} + +static uint16_t getThumbWord(uint32_t instruction) +{ + uint32_t i = ((instruction & 0x00000400) >> 10); + uint32_t imm4 = (instruction & 0x0000000F); + uint32_t imm3 = ((instruction & 0x70000000) >> 28); + uint32_t imm8 = ((instruction & 0x00FF0000) >> 16); + return ((imm4 << 12) | (i << 11) | (imm3 << 8) | imm8); +} + +static uint32_t setThumbWord(uint32_t instruction, uint16_t word) { + uint32_t imm4 = (word & 0xF000) >> 12; + uint32_t i = (word & 0x0800) >> 11; + uint32_t imm3 = (word & 0x0700) >> 8; + uint32_t imm8 = word & 0x00FF; + return (instruction & 0x8F00FBF0) | imm4 | (i << 10) | (imm3 << 28) | (imm8 << 16); +} + +static bool isArmMovw(uint32_t instruction) +{ + return (instruction & 0x0FF00000) == 0x03000000; +} + +static bool isArmMovt(uint32_t instruction) +{ + return (instruction & 0x0FF00000) == 0x03400000; +} + +static uint16_t getArmWord(uint32_t instruction) +{ + uint32_t imm4 = ((instruction & 0x000F0000) >> 16); + uint32_t imm12 = (instruction & 0x00000FFF); + return (imm4 << 12) | imm12; +} + +static uint32_t setArmWord(uint32_t instruction, uint16_t word) { + uint32_t imm4 = (word & 0xF000) >> 12; + uint32_t imm12 = word & 0x0FFF; + return (instruction & 0xFFF0F000) | (imm4 << 16) | imm12; +} + + +template <> +void Rebaser::adjustReference(uint32_t kind, uint8_t* mappedAddr, uint64_t fromNewAddress, uint64_t toNewAddress, int64_t adjust, int64_t targetSlide, + uint64_t imageStartAddress, uint64_t imageEndAddress, std::vector& pointersInData) +{ + uint32_t value32; + uint32_t* mappedAddr32 = (uint32_t*)mappedAddr; + static uint32_t* lastMappedAddr32 = NULL; + static uint32_t lastKind = 0; + static uint32_t lastToNewAddress = 0; + switch ( kind ) { + case DYLD_CACHE_ADJ_V2_DELTA_32: + value32 = arm64::P::E::get32(*mappedAddr32); + E::set32(*mappedAddr32, value32 + adjust); + break; + case DYLD_CACHE_ADJ_V2_POINTER_32: + if ( toNewAddress != (E::get32(*mappedAddr32) + targetSlide) ) + throwf("bad DYLD_CACHE_ADJ_V2_POINTER_32 value not as expected at address 0x%llX\n", fromNewAddress); + E::set32(*mappedAddr32, toNewAddress); + pointersInData.push_back( mappedAddr); + break; + case DYLD_CACHE_ADJ_V2_IMAGE_OFF_32: + if ( adjust == 0 ) + break; + mappedAddr32 = (uint32_t*)mappedAddr; + value32 = (uint32_t)(toNewAddress - imageStartAddress); + E::set32(*mappedAddr32, value32); + break; + case DYLD_CACHE_ADJ_V2_THUMB_MOVW_MOVT: + // to update a movw/movt pair we need to extract the 32-bit they will make, + // add the adjust and write back the new movw/movt pair. + if ( lastKind == kind ) { + if ( lastToNewAddress == toNewAddress ) { + uint32_t instruction1 = E::get32(*lastMappedAddr32); + uint32_t instruction2 = E::get32(*mappedAddr32); + if ( isThumbMovw(instruction1) && isThumbMovt(instruction2) ) { + uint16_t high = getThumbWord(instruction2); + uint16_t low = getThumbWord(instruction1); + uint32_t full = high << 16 | low; + full += adjust; + instruction1 = setThumbWord(instruction1, full & 0xFFFF); + instruction2 = setThumbWord(instruction2, full >> 16); + } + else if ( isThumbMovt(instruction1) && isThumbMovw(instruction2) ) { + uint16_t high = getThumbWord(instruction1); + uint16_t low = getThumbWord(instruction2); + uint32_t full = high << 16 | low; + full += adjust; + instruction2 = setThumbWord(instruction2, full & 0xFFFF); + instruction1 = setThumbWord(instruction1, full >> 16); + } + else { + throw "two DYLD_CACHE_ADJ_V2_THUMB_MOVW_MOVT in a row but not"; + } + E::set32(*lastMappedAddr32, instruction1); + E::set32(*mappedAddr32, instruction2); + kind = 0; + } + else { + throw "two DYLD_CACHE_ADJ_V2_THUMB_MOVW_MOVT in a row but target different addresses"; + } + } + break; + case DYLD_CACHE_ADJ_V2_ARM_MOVW_MOVT: + // to update a movw/movt pair we need to extract the 32-bit they will make, + // add the adjust and write back the new movw/movt pair. + if ( lastKind == kind ) { + if ( lastToNewAddress == toNewAddress ) { + uint32_t instruction1 = E::get32(*lastMappedAddr32); + uint32_t instruction2 = E::get32(*mappedAddr32); + if ( isArmMovw(instruction1) && isArmMovt(instruction2) ) { + uint16_t high = getArmWord(instruction2); + uint16_t low = getArmWord(instruction1); + uint32_t full = high << 16 | low; + full += adjust; + instruction1 = setArmWord(instruction1, full & 0xFFFF); + instruction2 = setArmWord(instruction2, full >> 16); + } + else if ( isArmMovt(instruction1) && isArmMovw(instruction2) ) { + uint16_t high = getArmWord(instruction1); + uint16_t low = getArmWord(instruction2); + uint32_t full = high << 16 | low; + full += adjust; + instruction2 = setArmWord(instruction2, full & 0xFFFF); + instruction1 = setArmWord(instruction1, full >> 16); + } + else { + throw "two DYLD_CACHE_ADJ_V2_ARM_MOVW_MOVT in a row but not"; + } + E::set32(*lastMappedAddr32, instruction1); + E::set32(*mappedAddr32, instruction2); + kind = 0; + } + else { + throw "two DYLD_CACHE_ADJ_V2_ARM_MOVW_MOVT in a row but target different addresses"; + } + } + break; + case DYLD_CACHE_ADJ_V2_ARM_BR24: + case DYLD_CACHE_ADJ_V2_THUMB_BR22: + // nothing to do with calls to stubs + break; + default: + throwf("v2 split seg info kind (%d) not supported yet", kind); + } + lastKind = kind; + lastToNewAddress = toNewAddress; + lastMappedAddr32 = mappedAddr32; +} + + +template +void Rebaser::adjustReference(uint32_t kind, uint8_t* mappedAddr, uint64_t fromNewAddress, uint64_t toNewAddress, int64_t adjust, int64_t targetSlide, + uint64_t imageStartAddress, uint64_t imageEndAddress, std::vector& pointersInData) +{ + throw "v2 split seg info not supported yet"; +} + + +template +void Rebaser::adjustReferencesUsingInfoV2(std::vector& pointersInData) +{ + static const bool log = false; + + const uint8_t* infoStart = &fLinkEditBase[fSplitSegInfo->dataoff()]; + const uint8_t* infoEnd = &infoStart[fSplitSegInfo->datasize()];; + if ( *infoStart++ != DYLD_CACHE_ADJ_V2_FORMAT ) + throw "malformed split seg info"; + + // build section arrays of slide and mapped address for each section + std::vector sectionSlides; + std::vector sectionNewAddress; + std::vector sectionMappedAddress; + sectionSlides.reserve(16); + sectionNewAddress.reserve(16); + sectionMappedAddress.reserve(16); + // section index 0 refers to mach_header + const MachOLayoutAbstraction::Segment& textSeg = fLayout.getSegments().front(); + sectionMappedAddress.push_back((uint8_t*)textSeg.mappedAddress()); + sectionSlides.push_back(textSeg.newAddress() - textSeg.address()); + sectionNewAddress.push_back(textSeg.newAddress()); + // section 1 and later refer to real sections + unsigned sectionIndex = 0; + for (const MachOLayoutAbstraction::Segment& seg : fLayout.getSegments()) { + uint64_t segSlide = seg.newAddress() - seg.address(); + for (uint32_t i=0; i < seg.sectionCount(); ++i) { + if (log) fprintf(stderr, "seg=%s, sectIndex=%d, mapped at=%p, offsetInSeg=0x%08llX\n", seg.name(), sectionIndex, seg.mappedAddress(), fSectionOffsetsInSegment[sectionIndex]); + sectionMappedAddress.push_back((uint8_t*)seg.mappedAddress() + fSectionOffsetsInSegment[sectionIndex]); + sectionSlides.push_back(segSlide); + sectionNewAddress.push_back(seg.newAddress() + fSectionOffsetsInSegment[sectionIndex]); + ++sectionIndex; + } + } + + // Whole :== FromToSection+ + // FromToSection :== ToOffset+ + // ToOffset :== FromOffset+ + // FromOffset :== + const uint8_t* p = infoStart; + uint64_t sectionCount = read_uleb128(p, infoEnd); + for (uint64_t i=0; i < sectionCount; ++i) { + uint64_t fromSectionIndex = read_uleb128(p, infoEnd); + uint64_t toSectionIndex = read_uleb128(p, infoEnd); + uint64_t toOffsetCount = read_uleb128(p, infoEnd); + uint64_t fromSectionSlide = sectionSlides[fromSectionIndex]; + uint64_t fromSectionNewAddress = sectionNewAddress[fromSectionIndex]; + uint8_t* fromSectionMappedAddress = sectionMappedAddress[fromSectionIndex]; + uint64_t toSectionSlide = sectionSlides[toSectionIndex]; + uint64_t toSectionNewAddress = sectionNewAddress[toSectionIndex]; + if (log) printf("from sect=%lld (mapped=%p), to sect=%lld (new addr=0x%llX):\n", fromSectionIndex, fromSectionMappedAddress, toSectionIndex, toSectionNewAddress); + uint64_t toSectionOffset = 0; + for (uint64_t j=0; j < toOffsetCount; ++j) { + uint64_t toSectionDelta = read_uleb128(p, infoEnd); + uint64_t fromOffsetCount = read_uleb128(p, infoEnd); + toSectionOffset += toSectionDelta; + for (uint64_t k=0; k < fromOffsetCount; ++k) { + uint64_t kind = read_uleb128(p, infoEnd); + uint64_t fromSectDeltaCount = read_uleb128(p, infoEnd); + uint64_t fromSectionOffset = 0; + for (uint64_t l=0; l < fromSectDeltaCount; ++l) { + uint64_t delta = read_uleb128(p, infoEnd); + fromSectionOffset += delta; + int64_t deltaAdjust = toSectionSlide - fromSectionSlide; + if (log) printf(" kind=%lld, from offset=0x%0llX, to offset=0x%0llX, adjust=0x%llX, targetSlide=0x%llX\n", kind, fromSectionOffset, toSectionOffset, deltaAdjust, toSectionSlide); + uint8_t* fromMappedAddr = fromSectionMappedAddress + fromSectionOffset; + uint64_t toNewAddress = toSectionNewAddress + toSectionOffset; + uint64_t fromNewAddress = fromSectionNewAddress + fromSectionOffset; + adjustReference(kind, fromMappedAddr, fromNewAddress, toNewAddress, deltaAdjust, toSectionSlide, sectionNewAddress.front(), sectionNewAddress.back(), pointersInData); + } + } + } + } + + +} + +#endif // __MACHO_REBASER__ + + + + diff --git a/src/dyld-apple/launch-cache/MachOTrie.hpp b/src/dyld-apple/launch-cache/MachOTrie.hpp new file mode 100644 index 000000000..d2f137e24 --- /dev/null +++ b/src/dyld-apple/launch-cache/MachOTrie.hpp @@ -0,0 +1,399 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2008-2010 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@ +*/ +#ifndef __MACH_O_TRIE__ +#define __MACH_O_TRIE__ + +#include +#include + +#include "MachOFileAbstraction.hpp" + + +namespace mach_o { +namespace trie { + +struct Edge +{ + Edge(const char* s, struct Node* n) : fSubString(s), fChild(n) { } + ~Edge() { } + const char* fSubString; + struct Node* fChild; + +}; + +struct Node +{ + Node(const char* s) : fCummulativeString(s), fAddress(0), fFlags(0), + fOther(0), fImportedName(NULL), fOrdered(false), + fHaveExportInfo(false), fTrieOffset(0) {} + ~Node() { } + const char* fCummulativeString; + std::vector fChildren; + uint64_t fAddress; + uint64_t fFlags; + uint64_t fOther; + const char* fImportedName; + bool fOrdered; + bool fHaveExportInfo; + uint32_t fTrieOffset; + + void addSymbol(const char* fullStr, uint64_t address, uint64_t flags, uint64_t other, const char* importName) { + const char* partialStr = &fullStr[strlen(fCummulativeString)]; + for (std::vector::iterator it = fChildren.begin(); it != fChildren.end(); ++it) { + Edge& e = *it; + long subStringLen = strlen(e.fSubString); + if ( strncmp(e.fSubString, partialStr, subStringLen) == 0 ) { + // already have matching edge, go down that path + e.fChild->addSymbol(fullStr, address, flags, other, importName); + return; + } + else { + for (long i=subStringLen-1; i > 0; --i) { + if ( strncmp(e.fSubString, partialStr, i) == 0 ) { + // found a common substring, splice in new node + // was A -> C, now A -> B -> C + char* bNodeCummStr = strdup(e.fChild->fCummulativeString); + bNodeCummStr[strlen(bNodeCummStr)+i-subStringLen] = '\0'; + //node* aNode = this; + Node* bNode = new Node(bNodeCummStr); + Node* cNode = e.fChild; + char* abEdgeStr = strdup(e.fSubString); + abEdgeStr[i] = '\0'; + char* bcEdgeStr = strdup(&e.fSubString[i]); + Edge& abEdge = e; + abEdge.fSubString = abEdgeStr; + abEdge.fChild = bNode; + Edge bcEdge(bcEdgeStr, cNode); + bNode->fChildren.push_back(bcEdge); + bNode->addSymbol(fullStr, address, flags, other, importName); + return; + } + } + } + } + + // no commonality with any existing child, make a new edge that is this whole string + Node* newNode = new Node(strdup(fullStr)); + Edge newEdge(strdup(partialStr), newNode); + fChildren.push_back(newEdge); + newNode->fAddress = address; + newNode->fFlags = flags; + newNode->fOther = other; + if ( (flags & EXPORT_SYMBOL_FLAGS_REEXPORT) && (importName != NULL) && (strcmp(fullStr,importName) != 0) ) + newNode->fImportedName = importName; + else + newNode->fImportedName = NULL; + newNode->fHaveExportInfo = true; + } + + void addOrderedNodes(const char* name, std::vector& orderedNodes) { + if ( !fOrdered ) { + orderedNodes.push_back(this); + //fprintf(stderr, "ordered %p %s\n", this, fCummulativeString); + fOrdered = true; + } + const char* partialStr = &name[strlen(fCummulativeString)]; + for (std::vector::iterator it = fChildren.begin(); it != fChildren.end(); ++it) { + Edge& e = *it; + long subStringLen = strlen(e.fSubString); + if ( strncmp(e.fSubString, partialStr, subStringLen) == 0 ) { + // already have matching edge, go down that path + e.fChild->addOrderedNodes(name, orderedNodes); + return; + } + } + } + + // byte for terminal node size in bytes, or 0x00 if not terminal node + // teminal node (uleb128 flags, uleb128 addr [uleb128 other]) + // byte for child node count + // each child: zero terminated substring, uleb128 node offset + bool updateOffset(uint32_t& offset) { + uint32_t nodeSize = 1; // length of export info when no export info + if ( fHaveExportInfo ) { + if ( fFlags & EXPORT_SYMBOL_FLAGS_REEXPORT ) { + nodeSize = uleb128_size(fFlags) + uleb128_size(fOther); // ordinal + if ( fImportedName != NULL ) + nodeSize += strlen(fImportedName); + ++nodeSize; // trailing zero in imported name + } + else { + nodeSize = uleb128_size(fFlags) + uleb128_size(fAddress); + if ( fFlags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER ) + nodeSize += uleb128_size(fOther); + } + // do have export info, overall node size so far is uleb128 of export info + export info + nodeSize += uleb128_size(nodeSize); + } + // add children + ++nodeSize; // byte for count of chidren + for (std::vector::iterator it = fChildren.begin(); it != fChildren.end(); ++it) { + Edge& e = *it; + nodeSize += strlen(e.fSubString) + 1 + uleb128_size(e.fChild->fTrieOffset); + } + bool result = (fTrieOffset != offset); + fTrieOffset = offset; + //fprintf(stderr, "updateOffset %p %05d %s\n", this, fTrieOffset, fCummulativeString); + offset += nodeSize; + // return true if fTrieOffset was changed + return result; + } + + void appendToStream(std::vector& out) { + if ( fHaveExportInfo ) { + if ( fFlags & EXPORT_SYMBOL_FLAGS_REEXPORT ) { + if ( fImportedName != NULL ) { + // nodes with re-export info: size, flags, ordinal, string + uint32_t nodeSize = (uint32_t)(uleb128_size(fFlags) + uleb128_size(fOther) + strlen(fImportedName) + 1); + out.push_back(nodeSize); + append_uleb128(fFlags, out); + append_uleb128(fOther, out); + append_string(fImportedName, out); + } + else { + // nodes with re-export info: size, flags, ordinal, empty-string + uint32_t nodeSize = uleb128_size(fFlags) + uleb128_size(fOther) + 1; + out.push_back(nodeSize); + append_uleb128(fFlags, out); + append_uleb128(fOther, out); + out.push_back(0); + } + } + else if ( fFlags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER ) { + // nodes with export info: size, flags, address, other + uint32_t nodeSize = uleb128_size(fFlags) + uleb128_size(fAddress) + uleb128_size(fOther); + out.push_back(nodeSize); + append_uleb128(fFlags, out); + append_uleb128(fAddress, out); + append_uleb128(fOther, out); + } + else { + // nodes with export info: size, flags, address + uint32_t nodeSize = uleb128_size(fFlags) + uleb128_size(fAddress); + out.push_back(nodeSize); + append_uleb128(fFlags, out); + append_uleb128(fAddress, out); + } + } + else { + // no export info uleb128 of zero is one byte of zero + out.push_back(0); + } + // write number of children + out.push_back(fChildren.size()); + // write each child + for (std::vector::iterator it = fChildren.begin(); it != fChildren.end(); ++it) { + Edge& e = *it; + append_string(e.fSubString, out); + append_uleb128(e.fChild->fTrieOffset, out); + } + } + +private: + static void append_uleb128(uint64_t value, std::vector& out) { + uint8_t byte; + do { + byte = value & 0x7F; + value &= ~0x7F; + if ( value != 0 ) + byte |= 0x80; + out.push_back(byte); + value = value >> 7; + } while( byte >= 0x80 ); + } + + static void append_string(const char* str, std::vector& out) { + for (const char* s = str; *s != '\0'; ++s) + out.push_back(*s); + out.push_back('\0'); + } + + static unsigned int uleb128_size(uint64_t value) { + uint32_t result = 0; + do { + value = value >> 7; + ++result; + } while ( value != 0 ); + return result; + } + + +}; + +inline uint64_t read_uleb128(const uint8_t*& p, const uint8_t* end) { + uint64_t result = 0; + int bit = 0; + do { + if (p == end) +#if __EXCEPTIONS + throw "malformed uleb128 extends beyond trie"; +#else + return result; +#endif + uint64_t slice = *p & 0x7f; + + if (bit >= 64 || slice << bit >> bit != slice) +#if __EXCEPTIONS + throw "uleb128 too big for 64-bits"; +#else + return result; +#endif + else { + result |= (slice << bit); + bit += 7; + } + } + while (*p++ & 0x80); + return result; +} + + + +struct Entry +{ + const char* name; + uint64_t address; + uint64_t flags; + uint64_t other; + const char* importName; +}; + + + +inline void makeTrie(const std::vector& entries, std::vector& output) +{ + Node start(strdup("")); + + // make nodes for all exported symbols + for (std::vector::const_iterator it = entries.begin(); it != entries.end(); ++it) { + start.addSymbol(it->name, it->address, it->flags, it->other, it->importName); + } + + // create vector of nodes + std::vector orderedNodes; + orderedNodes.reserve(entries.size()*2); + for (std::vector::const_iterator it = entries.begin(); it != entries.end(); ++it) { + start.addOrderedNodes(it->name, orderedNodes); + } + + // assign each node in the vector an offset in the trie stream, iterating until all uleb128 sizes have stabilized + bool more; + do { + uint32_t offset = 0; + more = false; + for (std::vector::iterator it = orderedNodes.begin(); it != orderedNodes.end(); ++it) { + if ( (*it)->updateOffset(offset) ) + more = true; + } + } while ( more ); + + // create trie stream + for (std::vector::iterator it = orderedNodes.begin(); it != orderedNodes.end(); ++it) { + (*it)->appendToStream(output); + } +} + +struct EntryWithOffset +{ + uintptr_t nodeOffset; + Entry entry; + + bool operator<(const EntryWithOffset& other) const { return ( nodeOffset < other.nodeOffset ); } +}; + + + +static inline void processExportNode(const uint8_t* const start, const uint8_t* p, const uint8_t* const end, + char* cummulativeString, int curStrOffset, + std::vector& output) +{ + if ( p >= end ) +#if __EXCEPTIONS + throw "malformed trie, node past end"; +#else + return; +#endif + const uint8_t terminalSize = read_uleb128(p, end); + const uint8_t* children = p + terminalSize; + if ( terminalSize != 0 ) { + EntryWithOffset e; + e.nodeOffset = p-start; + e.entry.name = strdup(cummulativeString); + e.entry.flags = read_uleb128(p, end); + if ( e.entry.flags & EXPORT_SYMBOL_FLAGS_REEXPORT ) { + e.entry.address = 0; + e.entry.other = read_uleb128(p, end); // dylib ordinal + e.entry.importName = (char*)p; + } + else { + e.entry.address = read_uleb128(p, end); + if ( e.entry.flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER ) + e.entry.other = read_uleb128(p, end); + else + e.entry.other = 0; + e.entry.importName = NULL; + } + output.push_back(e); + } + const uint8_t childrenCount = *children++; + const uint8_t* s = children; + for (uint8_t i=0; i < childrenCount; ++i) { + int edgeStrLen = 0; + while (*s != '\0') { + cummulativeString[curStrOffset+edgeStrLen] = *s++; + ++edgeStrLen; + } + cummulativeString[curStrOffset+edgeStrLen] = *s++; + uint32_t childNodeOffet = (uint32_t)read_uleb128(s, end); + processExportNode(start, start+childNodeOffet, end, cummulativeString, curStrOffset+edgeStrLen, output); + } +} + + +inline void parseTrie(const uint8_t* start, const uint8_t* end, std::vector& output) +{ + // empty trie has no entries + if ( start == end ) + return; + char cummulativeString[32000]; + std::vector entries; + processExportNode(start, start, end, cummulativeString, 0, entries); + // to preserve tie layout order, sort by node offset + std::sort(entries.begin(), entries.end()); + // copy to output + output.reserve(entries.size()); + for (std::vector::iterator it=entries.begin(); it != entries.end(); ++it) + output.push_back(it->entry); +} + + + + +}; // namespace trie +}; // namespace mach_o + + +#endif // __MACH_O_TRIE__ + + diff --git a/src/dyld-apple/launch-cache/dsc_extractor.cpp b/src/dyld-apple/launch-cache/dsc_extractor.cpp new file mode 100644 index 000000000..30ba09435 --- /dev/null +++ b/src/dyld-apple/launch-cache/dsc_extractor.cpp @@ -0,0 +1,691 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2011 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@ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define NO_ULEB +#include "Architectures.hpp" +#include "MachOFileAbstraction.hpp" +#include "CacheFileAbstraction.hpp" + +#include "dsc_iterator.h" +#include "dsc_extractor.h" +#include "MachOTrie.hpp" + +#include +#include +#include +#include +#include +#include + +struct seg_info +{ + seg_info(const char* n, uint64_t o, uint64_t s) + : segName(n), offset(o), sizem(s) { } + const char* segName; + uint64_t offset; + uint64_t sizem; +}; + +class CStringHash { +public: + size_t operator()(const char* __s) const { + size_t __h = 0; + for ( ; *__s; ++__s) + __h = 5 * __h + *__s; + return __h; + }; +}; +class CStringEquals { +public: + bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } +}; +typedef std::unordered_map, CStringHash, CStringEquals> NameToSegments; + +// Filter to find individual symbol re-exports in trie +class NotReExportSymbol { +public: + NotReExportSymbol(const std::set &rd) :_reexportDeps(rd) {} + bool operator()(const mach_o::trie::Entry &entry) const { + bool result = isSymbolReExport(entry); + if (result) { + // Xcode 6 leaks in dyld_shared_cache_extract_dylibs + ::free((void*)entry.name); + const_cast(&entry)->name = NULL; + } + return result; + } +private: + bool isSymbolReExport(const mach_o::trie::Entry &entry) const { + if ( (entry.flags & EXPORT_SYMBOL_FLAGS_KIND_MASK) != EXPORT_SYMBOL_FLAGS_KIND_REGULAR ) + return true; + if ( (entry.flags & EXPORT_SYMBOL_FLAGS_REEXPORT) == 0 ) + return true; + // If the symbol comes from a dylib that is re-exported, this is not an individual symbol re-export + if ( _reexportDeps.count((int)entry.other) != 0 ) + return true; + return false; + } + const std::set &_reexportDeps; +}; + + +template +int optimize_linkedit(macho_header* mh, uint64_t textOffsetInCache, const void* mapped_cache, uint64_t* newSize) +{ + typedef typename A::P P; + typedef typename A::P::E E; + typedef typename A::P::uint_t pint_t; + + // update header flags + mh->set_flags(mh->flags() & 0x7FFFFFFF); // remove in-cache bit + + // update load commands + uint64_t cumulativeFileSize = 0; + const unsigned origLoadCommandsSize = mh->sizeofcmds(); + unsigned bytesRemaining = origLoadCommandsSize; + unsigned removedCount = 0; + const macho_load_command

* const cmds = (macho_load_command

*)((uint8_t*)mh + sizeof(macho_header

)); + const uint32_t cmdCount = mh->ncmds(); + const macho_load_command

* cmd = cmds; + macho_segment_command

* linkEditSegCmd = NULL; + macho_symtab_command

* symtab = NULL; + macho_dysymtab_command

* dynamicSymTab = NULL; + macho_linkedit_data_command

* functionStarts = NULL; + macho_linkedit_data_command

* dataInCode = NULL; + uint32_t exportsTrieOffset = 0; + uint32_t exportsTrieSize = 0; + std::set reexportDeps; + int depIndex = 0; + for (uint32_t i = 0; i < cmdCount; ++i) { + bool remove = false; + switch ( cmd->cmd() ) { + case macho_segment_command

::CMD: + { + // update segment/section file offsets + macho_segment_command

* segCmd = (macho_segment_command

*)cmd; + segCmd->set_fileoff(cumulativeFileSize); + macho_section

* const sectionsStart = (macho_section

*)((char*)segCmd + sizeof(macho_segment_command

)); + macho_section

* const sectionsEnd = §ionsStart[segCmd->nsects()]; + for(macho_section

* sect = sectionsStart; sect < sectionsEnd; ++sect) { + if ( sect->offset() != 0 ) + sect->set_offset((uint32_t)(cumulativeFileSize+sect->addr()-segCmd->vmaddr())); + } + if ( strcmp(segCmd->segname(), "__LINKEDIT") == 0 ) { + linkEditSegCmd = segCmd; + } + cumulativeFileSize += segCmd->filesize(); + } + break; + case LC_DYLD_INFO_ONLY: + { + // zero out all dyld info + macho_dyld_info_command

* dyldInfo = (macho_dyld_info_command

*)cmd; + exportsTrieOffset = dyldInfo->export_off(); + exportsTrieSize = dyldInfo->export_size(); + dyldInfo->set_rebase_off(0); + dyldInfo->set_rebase_size(0); + dyldInfo->set_bind_off(0); + dyldInfo->set_bind_size(0); + dyldInfo->set_weak_bind_off(0); + dyldInfo->set_weak_bind_size(0); + dyldInfo->set_lazy_bind_off(0); + dyldInfo->set_lazy_bind_size(0); + dyldInfo->set_export_off(0); + dyldInfo->set_export_size(0); + } + break; + case LC_SYMTAB: + symtab = (macho_symtab_command

*)cmd; + break; + case LC_DYSYMTAB: + dynamicSymTab = (macho_dysymtab_command

*)cmd; + break; + case LC_FUNCTION_STARTS: + functionStarts = (macho_linkedit_data_command

*)cmd; + break; + case LC_DATA_IN_CODE: + dataInCode = (macho_linkedit_data_command

*)cmd; + break; + case LC_LOAD_DYLIB: + case LC_LOAD_WEAK_DYLIB: + case LC_REEXPORT_DYLIB: + case LC_LOAD_UPWARD_DYLIB: + ++depIndex; + if ( cmd->cmd() == LC_REEXPORT_DYLIB ) { + reexportDeps.insert(depIndex); + } + break; + case LC_SEGMENT_SPLIT_INFO: + // dylibs iOS 9 dyld caches have bogus LC_SEGMENT_SPLIT_INFO + remove = true; + break; + } + uint32_t cmdSize = cmd->cmdsize(); + macho_load_command

* nextCmd = (macho_load_command

*)(((uint8_t*)cmd)+cmdSize); + if ( remove ) { + ::memmove((void*)cmd, (void*)nextCmd, bytesRemaining); + ++removedCount; + } + else { + bytesRemaining -= cmdSize; + cmd = nextCmd; + } + } + // zero out stuff removed + ::bzero((void*)cmd, bytesRemaining); + // update header + mh->set_ncmds(cmdCount - removedCount); + mh->set_sizeofcmds(origLoadCommandsSize - bytesRemaining); + + // rebuild symbol table + if ( linkEditSegCmd == NULL ) { + fprintf(stderr, "__LINKEDIT not found\n"); + return -1; + } + if ( symtab == NULL ) { + fprintf(stderr, "LC_SYMTAB not found\n"); + return -1; + } + if ( dynamicSymTab == NULL ) { + fprintf(stderr, "LC_DYSYMTAB not found\n"); + return -1; + } + + const uint64_t newFunctionStartsOffset = linkEditSegCmd->fileoff(); + uint32_t functionStartsSize = 0; + if ( functionStarts != NULL ) { + // copy function starts from original cache file to new mapped dylib file + functionStartsSize = functionStarts->datasize(); + memcpy((char*)mh + newFunctionStartsOffset, (char*)mapped_cache + functionStarts->dataoff(), functionStartsSize); + } + const uint64_t newDataInCodeOffset = (newFunctionStartsOffset + functionStartsSize + sizeof(pint_t) - 1) & (-sizeof(pint_t)); // pointer align + uint32_t dataInCodeSize = 0; + if ( dataInCode != NULL ) { + // copy data-in-code info from original cache file to new mapped dylib file + dataInCodeSize = dataInCode->datasize(); + memcpy((char*)mh + newDataInCodeOffset, (char*)mapped_cache + dataInCode->dataoff(), dataInCodeSize); + } + + std::vector exports; + if ( exportsTrieSize != 0 ) { + const uint8_t* exportsStart = ((uint8_t*)mapped_cache) + exportsTrieOffset; + const uint8_t* exportsEnd = &exportsStart[exportsTrieSize]; + mach_o::trie::parseTrie(exportsStart, exportsEnd, exports); + exports.erase(std::remove_if(exports.begin(), exports.end(), NotReExportSymbol(reexportDeps)), exports.end()); + } + + // look for local symbol info in unmapped part of shared cache + dyldCacheHeader* header = (dyldCacheHeader*)mapped_cache; + macho_nlist

* localNlists = NULL; + uint32_t localNlistCount = 0; + const char* localStrings = NULL; + const char* localStringsEnd = NULL; + if ( header->mappingOffset() > offsetof(dyld_cache_header,localSymbolsSize) ) { + dyldCacheLocalSymbolsInfo* localInfo = (dyldCacheLocalSymbolsInfo*)(((uint8_t*)mapped_cache) + header->localSymbolsOffset()); + dyldCacheLocalSymbolEntry* entries = (dyldCacheLocalSymbolEntry*)(((uint8_t*)mapped_cache) + header->localSymbolsOffset() + localInfo->entriesOffset()); + macho_nlist

* allLocalNlists = (macho_nlist

*)(((uint8_t*)localInfo) + localInfo->nlistOffset()); + const uint32_t entriesCount = localInfo->entriesCount(); + for (uint32_t i=0; i < entriesCount; ++i) { + if ( entries[i].dylibOffset() == textOffsetInCache ) { + uint32_t localNlistStart = entries[i].nlistStartIndex(); + localNlistCount = entries[i].nlistCount(); + localNlists = &allLocalNlists[localNlistStart]; + localStrings = ((char*)localInfo) + localInfo->stringsOffset(); + localStringsEnd = &localStrings[localInfo->stringsSize()]; + break; + } + } + } + // compute number of symbols in new symbol table + const macho_nlist

* const mergedSymTabStart = (macho_nlist

*)(((uint8_t*)mapped_cache) + symtab->symoff()); + const macho_nlist

* const mergedSymTabend = &mergedSymTabStart[symtab->nsyms()]; + uint32_t newSymCount = symtab->nsyms(); + if ( localNlists != NULL ) { + newSymCount = localNlistCount; + for (const macho_nlist

* s = mergedSymTabStart; s != mergedSymTabend; ++s) { + // skip any locals in cache + if ( (s->n_type() & (N_TYPE|N_EXT)) == N_SECT ) + continue; + ++newSymCount; + } + } + + // add room for N_INDR symbols for re-exported symbols + newSymCount += exports.size(); + + // copy symbol entries and strings from original cache file to new mapped dylib file + const uint64_t newSymTabOffset = (newDataInCodeOffset + dataInCodeSize + sizeof(pint_t) - 1) & (-sizeof(pint_t)); // pointer align + const uint64_t newIndSymTabOffset = newSymTabOffset + newSymCount*sizeof(macho_nlist

); + const uint64_t newStringPoolOffset = newIndSymTabOffset + dynamicSymTab->nindirectsyms()*sizeof(uint32_t); + macho_nlist

* const newSymTabStart = (macho_nlist

*)(((uint8_t*)mh) + newSymTabOffset); + char* const newStringPoolStart = (char*)mh + newStringPoolOffset; + const uint32_t* mergedIndSymTab = (uint32_t*)((char*)mapped_cache + dynamicSymTab->indirectsymoff()); + const char* mergedStringPoolStart = (char*)mapped_cache + symtab->stroff(); + const char* mergedStringPoolEnd = &mergedStringPoolStart[symtab->strsize()]; + macho_nlist

* t = newSymTabStart; + int poolOffset = 0; + uint32_t symbolsCopied = 0; + newStringPoolStart[poolOffset++] = '\0'; // first pool entry is always empty string + for (const macho_nlist

* s = mergedSymTabStart; s != mergedSymTabend; ++s) { + // if we have better local symbol info, skip any locals here + if ( (localNlists != NULL) && ((s->n_type() & (N_TYPE|N_EXT)) == N_SECT) ) + continue; + *t = *s; + t->set_n_strx(poolOffset); + const char* symName = &mergedStringPoolStart[s->n_strx()]; + if ( symName > mergedStringPoolEnd ) + symName = ""; + strcpy(&newStringPoolStart[poolOffset], symName); + poolOffset += (strlen(symName) + 1); + ++t; + ++symbolsCopied; + } + // recreate N_INDR symbols in extracted dylibs for debugger + for (std::vector::iterator it = exports.begin(); it != exports.end(); ++it) { + strcpy(&newStringPoolStart[poolOffset], it->name); + t->set_n_strx(poolOffset); + poolOffset += (strlen(it->name) + 1); + t->set_n_type(N_INDR | N_EXT); + t->set_n_sect(0); + t->set_n_desc(0); + const char* importName = it->importName; + if ( *importName == '\0' ) + importName = it->name; + strcpy(&newStringPoolStart[poolOffset], importName); + t->set_n_value(poolOffset); + poolOffset += (strlen(importName) + 1); + ++t; + ++symbolsCopied; + } + if ( localNlists != NULL ) { + // update load command to reflect new count of locals + dynamicSymTab->set_ilocalsym(symbolsCopied); + dynamicSymTab->set_nlocalsym(localNlistCount); + // copy local symbols + for (uint32_t i=0; i < localNlistCount; ++i) { + const char* localName = &localStrings[localNlists[i].n_strx()]; + if ( localName > localStringsEnd ) + localName = ""; + *t = localNlists[i]; + t->set_n_strx(poolOffset); + strcpy(&newStringPoolStart[poolOffset], localName); + poolOffset += (strlen(localName) + 1); + ++t; + ++symbolsCopied; + } + } + + if ( newSymCount != symbolsCopied ) { + fprintf(stderr, "symbol count miscalculation\n"); + return -1; + } + + // pointer align string pool size + while ( (poolOffset % sizeof(pint_t)) != 0 ) + ++poolOffset; + // copy indirect symbol table + uint32_t* newIndSymTab = (uint32_t*)((char*)mh + newIndSymTabOffset); + memcpy(newIndSymTab, mergedIndSymTab, dynamicSymTab->nindirectsyms()*sizeof(uint32_t)); + + // update load commands + if ( functionStarts != NULL ) { + functionStarts->set_dataoff((uint32_t)newFunctionStartsOffset); + functionStarts->set_datasize(functionStartsSize); + } + if ( dataInCode != NULL ) { + dataInCode->set_dataoff((uint32_t)newDataInCodeOffset); + dataInCode->set_datasize(dataInCodeSize); + } + symtab->set_nsyms(symbolsCopied); + symtab->set_symoff((uint32_t)newSymTabOffset); + symtab->set_stroff((uint32_t)newStringPoolOffset); + symtab->set_strsize(poolOffset); + dynamicSymTab->set_extreloff(0); + dynamicSymTab->set_nextrel(0); + dynamicSymTab->set_locreloff(0); + dynamicSymTab->set_nlocrel(0); + dynamicSymTab->set_indirectsymoff((uint32_t)newIndSymTabOffset); + linkEditSegCmd->set_filesize(symtab->stroff()+symtab->strsize() - linkEditSegCmd->fileoff()); + linkEditSegCmd->set_vmsize( (linkEditSegCmd->filesize()+4095) & (-4096) ); + + // return new size + *newSize = (symtab->stroff()+symtab->strsize()+4095) & (-4096); + + // Xcode 6 leaks in dyld_shared_cache_extract_dylibs + for (std::vector::iterator it = exports.begin(); it != exports.end(); ++it) { + ::free((void*)(it->name)); + } + + + return 0; +} + + + +static void make_dirs(const char* file_path) +{ + //printf("make_dirs(%s)\n", file_path); + char dirs[strlen(file_path)+1]; + strcpy(dirs, file_path); + char* lastSlash = strrchr(dirs, '/'); + if ( lastSlash == NULL ) + return; + lastSlash[1] = '\0'; + struct stat stat_buf; + if ( stat(dirs, &stat_buf) != 0 ) { + char* afterSlash = &dirs[1]; + char* slash; + while ( (slash = strchr(afterSlash, '/')) != NULL ) { + *slash = '\0'; + ::mkdir(dirs, S_IRWXU | S_IRGRP|S_IXGRP | S_IROTH|S_IXOTH); + //printf("mkdir(%s)\n", dirs); + *slash = '/'; + afterSlash = slash+1; + } + } +} + + + +template +size_t dylib_maker(const void* mapped_cache, std::vector &dylib_data, const std::vector& segments) { + typedef typename A::P P; + + size_t additionalSize = 0; + for(std::vector::const_iterator it=segments.begin(); it != segments.end(); ++it) { + additionalSize += it->sizem; + } + + dylib_data.reserve(dylib_data.size() + additionalSize); + + uint32_t nfat_archs = 0; + uint32_t offsetInFatFile = 4096; + uint8_t *base_ptr = &dylib_data.front(); + +#define FH reinterpret_cast(base_ptr) +#define FA reinterpret_cast(base_ptr + (8 + (nfat_archs - 1) * sizeof(fat_arch))) + + if(dylib_data.size() >= 4096 && OSSwapBigToHostInt32(FH->magic) == FAT_MAGIC) { + // have fat header, append new arch to end + nfat_archs = OSSwapBigToHostInt32(FH->nfat_arch); + offsetInFatFile = OSSwapBigToHostInt32(FA->offset) + OSSwapBigToHostInt32(FA->size); + } + + dylib_data.resize(offsetInFatFile); + base_ptr = &dylib_data.front(); + + FH->magic = OSSwapHostToBigInt32(FAT_MAGIC); + FH->nfat_arch = OSSwapHostToBigInt32(++nfat_archs); + + FA->cputype = 0; // filled in later + FA->cpusubtype = 0; // filled in later + FA->offset = OSSwapHostToBigInt32(offsetInFatFile); + FA->size = 0; // filled in later + FA->align = OSSwapHostToBigInt32(12); + + // Write regular segments into the buffer + uint64_t totalSize = 0; + uint64_t textOffsetInCache = 0; + for( std::vector::const_iterator it=segments.begin(); it != segments.end(); ++it) { + + if(strcmp(it->segName, "__TEXT") == 0 ) { + textOffsetInCache = it->offset; + const macho_header

*textMH = reinterpret_cast*>((uint8_t*)mapped_cache+textOffsetInCache); + FA->cputype = OSSwapHostToBigInt32(textMH->cputype()); + FA->cpusubtype = OSSwapHostToBigInt32(textMH->cpusubtype()); + + // if this cputype/subtype already exist in fat header, then return immediately + for(uint32_t i=0; i < nfat_archs-1; ++i) { + fat_arch *afa = reinterpret_cast(base_ptr+8)+i; + + if( afa->cputype == FA->cputype + && afa->cpusubtype == FA->cpusubtype) { + //fprintf(stderr, "arch already exists in fat dylib\n"); + dylib_data.resize(offsetInFatFile); + return offsetInFatFile; + } + } + } + + //printf("segName=%s, offset=0x%llX, size=0x%0llX\n", it->segName, it->offset, it->sizem); + std::copy(((uint8_t*)mapped_cache)+it->offset, ((uint8_t*)mapped_cache)+it->offset+it->sizem, std::back_inserter(dylib_data)); + base_ptr = &dylib_data.front(); + totalSize += it->sizem; + } + + FA->size = OSSwapHostToBigInt32(totalSize); + + // optimize linkedit + uint64_t newSize = dylib_data.size(); + optimize_linkedit(((macho_header

*)(base_ptr+offsetInFatFile)), textOffsetInCache, mapped_cache, &newSize); + + // update fat header with new file size + dylib_data.resize(offsetInFatFile+newSize); + base_ptr = &dylib_data.front(); + FA->size = OSSwapHostToBigInt32(newSize); +#undef FH +#undef FA + return offsetInFatFile; +} + + +int dyld_shared_cache_extract_dylibs_progress(const char* shared_cache_file_path, const char* extraction_root_path, + void (^progress)(unsigned current, unsigned total)) +{ + struct stat statbuf; + if (stat(shared_cache_file_path, &statbuf)) { + fprintf(stderr, "Error: stat failed for dyld shared cache at %s\n", shared_cache_file_path); + return -1; + } + + int cache_fd = open(shared_cache_file_path, O_RDONLY); + if (cache_fd < 0) { + fprintf(stderr, "Error: failed to open shared cache file at %s\n", shared_cache_file_path); + return -1; + } + + void* mapped_cache = mmap(NULL, statbuf.st_size, PROT_READ, MAP_PRIVATE, cache_fd, 0); + if (mapped_cache == MAP_FAILED) { + fprintf(stderr, "Error: mmap() for shared cache at %s failed, errno=%d\n", shared_cache_file_path, errno); + return -1; + } + + close(cache_fd); + + // instantiate arch specific dylib maker + size_t (*dylib_create_func)(const void*, std::vector&, const std::vector&) = NULL; + if ( strcmp((char*)mapped_cache, "dyld_v1 i386") == 0 ) + dylib_create_func = dylib_maker; + else if ( strcmp((char*)mapped_cache, "dyld_v1 x86_64") == 0 ) + dylib_create_func = dylib_maker; + else if ( strcmp((char*)mapped_cache, "dyld_v1 x86_64h") == 0 ) + dylib_create_func = dylib_maker; + else if ( strcmp((char*)mapped_cache, "dyld_v1 armv5") == 0 ) + dylib_create_func = dylib_maker; + else if ( strcmp((char*)mapped_cache, "dyld_v1 armv6") == 0 ) + dylib_create_func = dylib_maker; + else if ( strcmp((char*)mapped_cache, "dyld_v1 armv7") == 0 ) + dylib_create_func = dylib_maker; + else if ( strncmp((char*)mapped_cache, "dyld_v1 armv7", 14) == 0 ) + dylib_create_func = dylib_maker; + else if ( strcmp((char*)mapped_cache, "dyld_v1 arm64") == 0 ) + dylib_create_func = dylib_maker; + else { + fprintf(stderr, "Error: unrecognized dyld shared cache magic.\n"); + munmap(mapped_cache, statbuf.st_size); + return -1; + } + + // iterate through all images in cache and build map of dylibs and segments + __block NameToSegments map; + __block int result = dyld_shared_cache_iterate(mapped_cache, (uint32_t)statbuf.st_size, ^(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo) { + map[dylibInfo->path].push_back(seg_info(segInfo->name, segInfo->fileOffset, segInfo->fileSize)); + }); + + if(result != 0) { + fprintf(stderr, "Error: dyld_shared_cache_iterate_segments_with_slide failed.\n"); + munmap(mapped_cache, statbuf.st_size); + return result; + } + + // for each dylib instantiate a dylib file + dispatch_group_t group = dispatch_group_create(); + dispatch_semaphore_t sema = dispatch_semaphore_create(2); + dispatch_queue_t process_queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0); + dispatch_queue_t writer_queue = dispatch_queue_create("dyld writer queue", 0); + + __block unsigned count = 0; + + for ( NameToSegments::iterator it = map.begin(); it != map.end(); ++it) { + dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER); + dispatch_group_async(group, process_queue, ^{ + + char dylib_path[PATH_MAX]; + strcpy(dylib_path, extraction_root_path); + strcat(dylib_path, "/"); + strcat(dylib_path, it->first); + + //printf("%s with %lu segments\n", dylib_path, it->second.size()); + // make sure all directories in this path exist + make_dirs(dylib_path); + + // open file, create if does not already exist + int fd = ::open(dylib_path, O_CREAT | O_EXLOCK | O_RDWR, 0644); + if ( fd == -1 ) { + fprintf(stderr, "can't open or create dylib file %s, errnor=%d\n", dylib_path, errno); + result = -1; + return; + } + + struct stat statbuf; + if (fstat(fd, &statbuf)) { + fprintf(stderr, "Error: stat failed for dyld file %s, errnor=%d\n", dylib_path, errno); + close(fd); + result = -1; + return; + } + + std::vector *vec = new std::vector(statbuf.st_size); + if(pread(fd, &vec->front(), vec->size(), 0) != (long)vec->size()) { + fprintf(stderr, "can't read dylib file %s, errnor=%d\n", dylib_path, errno); + close(fd); + result = -1; + return; + } + + const size_t offset = dylib_create_func(mapped_cache, *vec, it->second); + + dispatch_group_async(group, writer_queue, ^{ + progress(count++, (unsigned)map.size()); + + if(offset != vec->size()) { + //Write out the first page, and everything after offset + if( pwrite(fd, &vec->front(), 4096, 0) == -1 + || pwrite(fd, &vec->front() + offset, vec->size() - offset, offset) == -1) { + fprintf(stderr, "error writing, errnor=%d\n", errno); + result = -1; + } + } + + delete vec; + close(fd); + dispatch_semaphore_signal(sema); + }); + }); + } + + dispatch_group_wait(group, DISPATCH_TIME_FOREVER); + dispatch_release(group); + dispatch_release(writer_queue); + + munmap(mapped_cache, statbuf.st_size); + return result; +} + + + +int dyld_shared_cache_extract_dylibs(const char* shared_cache_file_path, const char* extraction_root_path) +{ + return dyld_shared_cache_extract_dylibs_progress(shared_cache_file_path, extraction_root_path, + ^(unsigned , unsigned) {} ); +} + + +#if 0 +// test program +#include +#include +#include + + +typedef int (*extractor_proc)(const char* shared_cache_file_path, const char* extraction_root_path, + void (^progress)(unsigned current, unsigned total)); + +int main(int argc, const char* argv[]) +{ + if ( argc != 3 ) { + fprintf(stderr, "usage: dsc_extractor \n"); + return 1; + } + + //void* handle = dlopen("/Volumes/my/src/dyld/build/Debug/dsc_extractor.bundle", RTLD_LAZY); + void* handle = dlopen("/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/usr/lib/dsc_extractor.bundle", RTLD_LAZY); + if ( handle == NULL ) { + fprintf(stderr, "dsc_extractor.bundle could not be loaded\n"); + return 1; + } + + extractor_proc proc = (extractor_proc)dlsym(handle, "dyld_shared_cache_extract_dylibs_progress"); + if ( proc == NULL ) { + fprintf(stderr, "dsc_extractor.bundle did not have dyld_shared_cache_extract_dylibs_progress symbol\n"); + return 1; + } + + int result = (*proc)(argv[1], argv[2], ^(unsigned c, unsigned total) { printf("%d/%d\n", c, total); } ); + fprintf(stderr, "dyld_shared_cache_extract_dylibs_progress() => %d\n", result); + return 0; +} + + +#endif + + + + diff --git a/src/dyld-apple/launch-cache/dsc_extractor.h b/src/dyld-apple/launch-cache/dsc_extractor.h new file mode 100644 index 000000000..a8620d0d9 --- /dev/null +++ b/src/dyld-apple/launch-cache/dsc_extractor.h @@ -0,0 +1,43 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2010 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@ + */ + +#ifndef _DYLD_SHARED_CACHE_EXTRACTOR_ +#define _DYLD_SHARED_CACHE_EXTRACTOR_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern int dyld_shared_cache_extract_dylibs(const char* shared_cache_file_path, const char* extraction_root_path); +extern int dyld_shared_cache_extract_dylibs_progress(const char* shared_cache_file_path, const char* extraction_root_path, + void (^progress)(unsigned current, unsigned total)); + +#ifdef __cplusplus +} +#endif + +#endif // _DYLD_SHARED_CACHE_EXTRACTOR_ + diff --git a/src/dyld-apple/launch-cache/dsc_iterator.cpp b/src/dyld-apple/launch-cache/dsc_iterator.cpp new file mode 100644 index 000000000..2c7c21203 --- /dev/null +++ b/src/dyld-apple/launch-cache/dsc_iterator.cpp @@ -0,0 +1,245 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2009-2012 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@ + */ + +#include +#include +#include + + +#include "dsc_iterator.h" +#include "dyld_cache_format.h" +#define NO_ULEB +#include "Architectures.hpp" +#include "MachOFileAbstraction.hpp" +#include "CacheFileAbstraction.hpp" + + +namespace dyld { + + + // convert an address in the shared region where the cache would normally be mapped, into an address where the cache is currently mapped + template + const uint8_t* mappedAddress(const uint8_t* cache, const uint8_t* cacheEnd, uint64_t addr) + { + const dyldCacheHeader* header = (dyldCacheHeader*)cache; + const dyldCacheFileMapping* mappings = (dyldCacheFileMapping*)&cache[header->mappingOffset()]; + for (uint32_t i=0; i < header->mappingCount(); ++i) { + if ( (mappings[i].address() <= addr) && (addr < (mappings[i].address() + mappings[i].size())) ) { + uint64_t cacheOffset = mappings[i].file_offset() + addr - mappings[i].address(); + const uint8_t* result = &cache[cacheOffset]; + if ( result < cacheEnd ) + return result; + else + return NULL; + } + } + return NULL; + } + + // call the callback block on each segment in this image + template + int walkSegments(const uint8_t* cache, const uint8_t* cacheEnd, const uint8_t* firstSeg, const char* dylibPath, uint64_t inode,uint64_t modTime, const uint8_t* machHeader, + void (^callback)(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo)) + { + typedef typename A::P P; + typedef typename A::P::E E; + dyld_shared_cache_dylib_info dylibInfo; + dyld_shared_cache_segment_info segInfo; + dylibInfo.version = 2; + dylibInfo.isAlias = (dylibPath < (char*)firstSeg); // paths for aliases are store between cache header and first segment + dylibInfo.machHeader = machHeader; + dylibInfo.path = dylibPath; + dylibInfo.inode = inode; + dylibInfo.modTime = modTime; + const macho_header

* mh = (const macho_header

*)machHeader; + const macho_load_command

* const cmds = (macho_load_command

*)(machHeader + sizeof(macho_header

)); + if ( (machHeader+ mh->sizeofcmds()) > cacheEnd ) + return -1; + const uint32_t cmd_count = mh->ncmds(); + const macho_load_command

* cmd = cmds; + // scan for LC_UUID + dylibInfo.uuid = NULL; + for (uint32_t i = 0; i < cmd_count; ++i) { + if ( cmd->cmd() == LC_UUID ) { + const uuid_command* uc = (const uuid_command*)cmd; + dylibInfo.uuid = &uc->uuid; + break; + } + cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); + } + // callback for each LC_SEGMENT + cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + if ( cmd->cmd() == macho_segment_command

::CMD ) { + macho_segment_command

* segCmd = (macho_segment_command

*)cmd; + uint64_t fileOffset = segCmd->fileoff(); + // work around until is fixed + if ( fileOffset == 0 ) { + fileOffset = (machHeader - cache); + } + uint64_t sizem = segCmd->vmsize(); + if ( strcmp(segCmd->segname(), "__LINKEDIT") == 0 ) { + // clip LINKEDIT size if bigger than cache file + if ( (fileOffset+sizem) > (uint64_t)(cacheEnd-cache) ) + sizem = (cacheEnd-cache)-fileOffset; + } + segInfo.version = 1; + segInfo.name = segCmd->segname(); + segInfo.fileOffset = fileOffset; + segInfo.fileSize = sizem; + if ( segCmd->filesize() > segCmd->vmsize() ) + return -1; + segInfo.address = segCmd->vmaddr(); + callback(&dylibInfo, &segInfo); + } + cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); + } + return 0; + } + + + // call walkSegments on each image in the cache + template + int walkImages(const uint8_t* cache, uint32_t size, void (^callback)(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo)) + { + // Sanity check there is at least a header + if ( (size > 0) && (size < 0x7000) ) + return -1; + typedef typename A::P::E E; + typedef typename A::P P; + const dyldCacheHeader* header = (dyldCacheHeader*)cache; + const dyldCacheImageInfo* dylibs = (dyldCacheImageInfo*)&cache[header->imagesOffset()]; + const dyldCacheFileMapping* mappings = (dyldCacheFileMapping*)&cache[header->mappingOffset()]; + uint64_t greatestMappingOffset = 0; + for (uint32_t i=0; i < header->mappingCount(); ++i) { + if ( (size != 0) && (mappings[i].file_offset() > size) ) + return -1; + uint64_t endOffset = mappings[i].file_offset()+mappings[i].size(); + if ( (size != 0) && (endOffset > size) ) + return -1; + if ( endOffset > greatestMappingOffset ) + greatestMappingOffset = endOffset; + } + const uint8_t* cacheEnd = &cache[size]; + if ( size == 0 ) { + // Zero size means old API is being used, assume all mapped + cacheEnd = &cache[greatestMappingOffset]; + } + else { + // verifiy mappings are not bigger than size + if ( size < greatestMappingOffset ) + return -1; + } + // verify all image infos are mapped + if ( (const uint8_t*)&dylibs[header->imagesCount()] > cacheEnd ) + return -1; + const uint8_t* firstSeg = NULL; + for (uint32_t i=0; i < header->imagesCount(); ++i) { + const char* dylibPath = (char*)cache + dylibs[i].pathFileOffset(); + uint64_t inode = dylibs[i].inode(); + uint64_t modTime = dylibs[i].modTime(); + if ( (const uint8_t*)dylibPath > cacheEnd ) + return -1; + const uint8_t* machHeader = mappedAddress(cache, cacheEnd, dylibs[i].address()); + if ( machHeader == NULL ) + return -1; + if ( machHeader > cacheEnd ) + return -1; + if ( firstSeg == NULL ) + firstSeg = machHeader; + int result = walkSegments(cache, cacheEnd, firstSeg, dylibPath, inode, modTime, machHeader, callback); + if ( result != 0 ) + return result; + } + return 0; + } + +} + + +// Given a pointer to an in-memory copy of a dyld shared cache file, +// this routine will call the callback block once for each segment +// in each dylib in the shared cache file. +// Returns -1 if there was an error, otherwise 0. +extern int dyld_shared_cache_iterate(const void* shared_cache_file, uint32_t shared_cache_size, + void (^callback)(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo)) { + const uint8_t* cache = (uint8_t*)shared_cache_file; + if ( strcmp((char*)cache, "dyld_v1 i386") == 0 ) + return dyld::walkImages(cache, shared_cache_size, callback); + else if ( strcmp((char*)cache, "dyld_v1 x86_64") == 0 ) + return dyld::walkImages(cache, shared_cache_size, callback); + else if ( strcmp((char*)cache, "dyld_v1 x86_64h") == 0 ) + return dyld::walkImages(cache, shared_cache_size, callback); + else if ( strcmp((char*)cache, "dyld_v1 armv5") == 0 ) + return dyld::walkImages(cache, shared_cache_size, callback); + else if ( strcmp((char*)cache, "dyld_v1 armv6") == 0 ) + return dyld::walkImages(cache, shared_cache_size, callback); + else if ( strcmp((char*)cache, "dyld_v1 armv7") == 0 ) + return dyld::walkImages(cache, shared_cache_size, callback); + else if ( strncmp((char*)cache, "dyld_v1 armv7", 14) == 0 ) + return dyld::walkImages(cache, shared_cache_size, callback); + else if ( strcmp((char*)cache, "dyld_v1 arm64") == 0 ) + return dyld::walkImages(cache, shared_cache_size, callback); + else + return -1; +} + + +// implement old version by calling new version +int dyld_shared_cache_iterate_segments_with_slide(const void* shared_cache_file, dyld_shared_cache_iterator_slide_t callback) +{ + return dyld_shared_cache_iterate(shared_cache_file, 0, ^(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo) { + callback(dylibInfo->path, segInfo->name, segInfo->fileOffset, segInfo->fileSize, segInfo->address, 0); + }); +} + +// implement non-block version by calling block version +int dyld_shared_cache_iterate_segments_with_slide_nb(const void* shared_cache_file, dyld_shared_cache_iterator_slide_nb_t func, void* userData) +{ + return dyld_shared_cache_iterate_segments_with_slide(shared_cache_file, ^(const char* dylibName, const char* segName, + uint64_t offset, uint64_t size, uint64_t mappedddress, uint64_t slide) { + (*func)(dylibName, segName, offset, size, mappedddress, slide, userData); + }); +} + + +// implement non-slide version by wrapping slide version in block +int dyld_shared_cache_iterate_segments(const void* shared_cache_file, dyld_shared_cache_iterator_t callback) +{ + dyld_shared_cache_iterator_slide_t wrapper_cb = ^(const char* dylibName, const char* segName, uint64_t offset, + uint64_t size, uint64_t mappedddress, uint64_t slide) { + callback(dylibName, segName, offset, size, mappedddress); + }; + return dyld_shared_cache_iterate_segments_with_slide(shared_cache_file, wrapper_cb); +} + +// implement non-slide,non-block version by wrapping slide version in block +int dyld_shared_cache_iterate_segments_nb(const void* shared_cache_file, dyld_shared_cache_iterator_nb_t func, void* userData) +{ + return dyld_shared_cache_iterate_segments_with_slide(shared_cache_file, ^(const char* dylibName, const char* segName, + uint64_t offset, uint64_t size, uint64_t mappedddress, uint64_t slide) { + (*func)(dylibName, segName, offset, size, mappedddress, userData); + }); +} + diff --git a/src/dyld-apple/launch-cache/dsc_iterator.h b/src/dyld-apple/launch-cache/dsc_iterator.h new file mode 100644 index 000000000..6c75d0e1e --- /dev/null +++ b/src/dyld-apple/launch-cache/dsc_iterator.h @@ -0,0 +1,82 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2009-2012 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@ + */ + +#include +#include + +struct dyld_shared_cache_dylib_info { + uint32_t version; // current version 2 + // following fields all exist in version 1 + uint32_t isAlias; // this is alternate path (symlink) + const void* machHeader; // of dylib in mapped cached file + const char* path; // of dylib + const uuid_t* uuid; // of dylib, or NULL is missing + // following fields all exist in version 2 + uint64_t inode; // of dylib file or path hash + uint64_t modTime; // of dylib file +}; +typedef struct dyld_shared_cache_dylib_info dyld_shared_cache_dylib_info; + +struct dyld_shared_cache_segment_info { + uint64_t version; // initial version 1 + const char* name; // of segment + uint64_t fileOffset; // of segment in cache file + uint64_t fileSize; // of segment + uint64_t address; // of segment when cache mapped with ASLR (sliding) off + // above fields all exist in version 1 +}; +typedef struct dyld_shared_cache_segment_info dyld_shared_cache_segment_info; + + +#ifdef __cplusplus +extern "C" { +#endif + + +// Given a pointer and size of an in-memory copy of a dyld shared cache file, +// this routine will call the callback block once for each segment in each dylib +// in the shared cache file. +// Returns -1 if there was an error, otherwise 0. +extern int dyld_shared_cache_iterate(const void* shared_cache_file, uint32_t shared_cache_size, + void (^callback)(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo)); + + + +// +// The following iterator functions are deprecated: +// +typedef void (^dyld_shared_cache_iterator_t)(const char* dylibName, const char* segName, uint64_t offset, uint64_t size, uint64_t mappedddress); +typedef void (^dyld_shared_cache_iterator_slide_t)(const char* dylibName, const char* segName, uint64_t offset, uint64_t size, uint64_t mappedddress, uint64_t slide); +typedef void (*dyld_shared_cache_iterator_nb_t)(const char* dylibName, const char* segName, uint64_t offset, uint64_t sizem, uint64_t mappedddress, void* userData); +typedef void (*dyld_shared_cache_iterator_slide_nb_t)(const char* dylibName, const char* segName, uint64_t offset, uint64_t sizem, uint64_t mappedddress, uint64_t slide, void* userData); + +extern int dyld_shared_cache_iterate_segments(const void* shared_cache_file, dyld_shared_cache_iterator_t callback) __attribute__((deprecated)); +extern int dyld_shared_cache_iterate_segments_with_slide(const void* shared_cache_file, dyld_shared_cache_iterator_slide_t callback) __attribute__((deprecated)); +extern int dyld_shared_cache_iterate_segments_nb(const void* shared_cache_file, dyld_shared_cache_iterator_nb_t callback, void* userData) __attribute__((deprecated)); +extern int dyld_shared_cache_iterate_segments_with_slide_nb(const void* shared_cache_file, dyld_shared_cache_iterator_slide_nb_t callback, void* userData) __attribute__((deprecated)); + + +#ifdef __cplusplus +} +#endif diff --git a/src/dyld-apple/launch-cache/dyld_cache_format.h b/src/dyld-apple/launch-cache/dyld_cache_format.h new file mode 100644 index 000000000..fd9d1c2b7 --- /dev/null +++ b/src/dyld-apple/launch-cache/dyld_cache_format.h @@ -0,0 +1,265 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006-2015 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@ + */ +#ifndef __DYLD_CACHE_FORMAT__ +#define __DYLD_CACHE_FORMAT__ + +#include +#include +#include +#include + + +struct dyld_cache_header +{ + char magic[16]; // e.g. "dyld_v0 i386" + uint32_t mappingOffset; // file offset to first dyld_cache_mapping_info + uint32_t mappingCount; // number of dyld_cache_mapping_info entries + uint32_t imagesOffset; // file offset to first dyld_cache_image_info + uint32_t imagesCount; // number of dyld_cache_image_info entries + uint64_t dyldBaseAddress; // base address of dyld when cache was built + uint64_t codeSignatureOffset; // file offset of code signature blob + uint64_t codeSignatureSize; // size of code signature blob (zero means to end of file) + uint64_t slideInfoOffset; // file offset of kernel slid info + uint64_t slideInfoSize; // size of kernel slid info + uint64_t localSymbolsOffset; // file offset of where local symbols are stored + uint64_t localSymbolsSize; // size of local symbols information + uint8_t uuid[16]; // unique value for each shared cache file + uint64_t cacheType; // 0 for development, 1 for production + uint32_t branchPoolsOffset; // file offset to table of uint64_t pool addresses + uint32_t branchPoolsCount; // number of uint64_t entries + uint64_t accelerateInfoAddr; // (unslid) address of optimization info + uint64_t accelerateInfoSize; // size of optimization info + uint64_t imagesTextOffset; // file offset to first dyld_cache_image_text_info + uint64_t imagesTextCount; // number of dyld_cache_image_text_info entries +}; + +struct dyld_cache_mapping_info { + uint64_t address; + uint64_t size; + uint64_t fileOffset; + uint32_t maxProt; + uint32_t initProt; +}; + +struct dyld_cache_image_info +{ + uint64_t address; + uint64_t modTime; + uint64_t inode; + uint32_t pathFileOffset; + uint32_t pad; +}; + +struct dyld_cache_image_info_extra +{ + uint64_t exportsTrieAddr; // address of trie in unslid cache + uint64_t weakBindingsAddr; + uint32_t exportsTrieSize; + uint32_t weakBindingsSize; + uint32_t dependentsStartArrayIndex; + uint32_t reExportsStartArrayIndex; +}; + + +struct dyld_cache_accelerator_info +{ + uint32_t version; // currently 1 + uint32_t imageExtrasCount; // does not include aliases + uint32_t imagesExtrasOffset; // offset into this chunk of first dyld_cache_image_info_extra + uint32_t bottomUpListOffset; // offset into this chunk to start of 16-bit array of sorted image indexes + uint32_t dylibTrieOffset; // offset into this chunk to start of trie containing all dylib paths + uint32_t dylibTrieSize; // size of trie containing all dylib paths + uint32_t initializersOffset; // offset into this chunk to start of initializers list + uint32_t initializersCount; // size of initializers list + uint32_t dofSectionsOffset; // offset into this chunk to start of DOF sections list + uint32_t dofSectionsCount; // size of initializers list + uint32_t reExportListOffset; // offset into this chunk to start of 16-bit array of re-exports + uint32_t reExportCount; // size of re-exports + uint32_t depListOffset; // offset into this chunk to start of 16-bit array of dependencies (0x8000 bit set if upward) + uint32_t depListCount; // size of dependencies + uint32_t rangeTableOffset; // offset into this chunk to start of ss + uint32_t rangeTableCount; // size of dependencies + uint64_t dyldSectionAddr; // address of libdyld's __dyld section in unslid cache +}; + +struct dyld_cache_accelerator_initializer +{ + uint32_t functionOffset; // address offset from start of cache mapping + uint32_t imageIndex; +}; + +struct dyld_cache_range_entry +{ + uint64_t startAddress; // unslid address of start of region + uint32_t size; + uint32_t imageIndex; +}; + +struct dyld_cache_accelerator_dof +{ + uint64_t sectionAddress; // unslid address of start of region + uint32_t sectionSize; + uint32_t imageIndex; +}; + +struct dyld_cache_image_text_info +{ + uuid_t uuid; + uint64_t loadAddress; // unslid address of start of __TEXT + uint32_t textSegmentSize; + uint32_t pathOffset; // offset from start of cache file +}; + + +// The rebasing info is to allow the kernel to lazily rebase DATA pages of the +// dyld shared cache. Rebasing is adding the slide to interior pointers. +struct dyld_cache_slide_info +{ + uint32_t version; // currently 1 + uint32_t toc_offset; + uint32_t toc_count; + uint32_t entries_offset; + uint32_t entries_count; + uint32_t entries_size; // currently 128 + // uint16_t toc[toc_count]; + // entrybitmap entries[entries_count]; +}; + + +// The version 2 of the slide info uses a different compression scheme. Since +// only interior pointers (pointers that point within the cache) are rebased +// (slid), we know the possible range of the pointers and thus know there are +// unused bits in each pointer. We use those bits to form a linked list of +// locations needing rebasing in each page. +// +// Definitions: +// +// pageIndex = (pageAddress - startOfAllDataAddress)/info->page_size +// pageStarts[] = info + info->page_starts_offset +// pageExtras[] = info + info->page_extras_offset +// valueMask = ~(info->delta_mask) +// deltaShift = __builtin_ctzll(info->delta_mask) - 2 +// +// There are three cases: +// +// 1) pageStarts[pageIndex] == DYLD_CACHE_SLIDE_PAGE_ATTR_NO_REBASE +// The page contains no values that need rebasing. +// +// 2) (pageStarts[pageIndex] & DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA) == 0 +// All rebase locations are in one linked list. The offset of the first +// rebase location in the page is pageStarts[pageIndex] * 4. +// +// 3) pageStarts[pageIndex] & DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA +// Multiple linked lists are needed for all rebase locations in a page. +// The pagesExtras array contains 2 or more entries each of which is the +// start of a new linked list in the page. The first is at: +// extrasStartIndex = (pageStarts[pageIndex] & 0x3FFF) +// The next is at extrasStartIndex+1. The last is denoted by +// having the high bit (DYLD_CACHE_SLIDE_PAGE_ATTR_END) of the pageExtras[] +// set. +// +// For 64-bit architectures, there is always enough free bits to encode all +// possible deltas. The info->delta_mask field shows where the delta is located +// in the pointer. That value must be masked off (valueMask) before the slide +// is added to the pointer. +// +// For 32-bit architectures, there are only three bits free (the three most +// significant bits). To extract the delta, you must first subtract value_add +// from the pointer value, then AND with delta_mask, then shift by deltaShift. +// That still leaves a maximum delta to the next rebase location of 28 bytes. +// To reduce the number or chains needed, an optimization was added. Turns +// out zero is common in the DATA region. A zero can be turned into a +// non-rebasing entry in the linked list. The can be done because nothing +// in the shared cache should point out of its dylib to the start of the shared +// cache. +// +// The code for processing a linked list (chain) is: +// +// uint32_t delta = 1; +// while ( delta != 0 ) { +// uint8_t* loc = pageStart + pageOffset; +// uintptr_t rawValue = *((uintptr_t*)loc); +// delta = ((rawValue & deltaMask) >> deltaShift); +// uintptr_t newValue = (rawValue & valueMask); +// if ( newValue != 0 ) { +// newValue += valueAdd; +// newValue += slideAmount; +// } +// *((uintptr_t*)loc) = newValue; +// pageOffset += delta; +// } +// +// +struct dyld_cache_slide_info2 +{ + uint32_t version; // currently 2 + uint32_t page_size; // currently 4096 (may also be 16384) + uint32_t page_starts_offset; + uint32_t page_starts_count; + uint32_t page_extras_offset; + uint32_t page_extras_count; + uint64_t delta_mask; // which (contiguous) set of bits contains the delta to the next rebase location + uint64_t value_add; + //uint16_t page_starts[page_starts_count]; + //uint16_t page_extras[page_extras_count]; +}; +#define DYLD_CACHE_SLIDE_PAGE_ATTRS 0xC000 // high bits of uint16_t are flags +#define DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA 0x8000 // index is into extras array (not starts array) +#define DYLD_CACHE_SLIDE_PAGE_ATTR_NO_REBASE 0x4000 // page has no rebasing +#define DYLD_CACHE_SLIDE_PAGE_ATTR_END 0x8000 // last chain entry for page + + +struct dyld_cache_local_symbols_info +{ + uint32_t nlistOffset; // offset into this chunk of nlist entries + uint32_t nlistCount; // count of nlist entries + uint32_t stringsOffset; // offset into this chunk of string pool + uint32_t stringsSize; // byte count of string pool + uint32_t entriesOffset; // offset into this chunk of array of dyld_cache_local_symbols_entry + uint32_t entriesCount; // number of elements in dyld_cache_local_symbols_entry array +}; + +struct dyld_cache_local_symbols_entry +{ + uint32_t dylibOffset; // offset in cache file of start of dylib + uint32_t nlistStartIndex; // start index of locals for this dylib + uint32_t nlistCount; // number of local symbols for this dylib +}; + + + +#define MACOSX_DYLD_SHARED_CACHE_DIR "/private/var/db/dyld/" +#define IPHONE_DYLD_SHARED_CACHE_DIR "/System/Library/Caches/com.apple.dyld/" +#define DYLD_SHARED_CACHE_BASE_NAME "dyld_shared_cache_" +#define DYLD_SHARED_CACHE_DEVELOPMENT_EXT ".development" + +static const uint64_t kDyldSharedCacheTypeDevelopment = 0; +static const uint64_t kDyldSharedCacheTypeProduction = 1; + + + + +#endif // __DYLD_CACHE_FORMAT__ + + diff --git a/src/dyld-apple/launch-cache/dyld_shared_cache_util.cpp b/src/dyld-apple/launch-cache/dyld_shared_cache_util.cpp new file mode 100644 index 000000000..63d6b733f --- /dev/null +++ b/src/dyld-apple/launch-cache/dyld_shared_cache_util.cpp @@ -0,0 +1,905 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2009-2012 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@ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "dsc_iterator.h" +#include "dsc_extractor.h" +#include "dyld_cache_format.h" +#include "Architectures.hpp" +#include "MachOFileAbstraction.hpp" +#include "CacheFileAbstraction.hpp" +#include "Trie.hpp" + +enum Mode { + modeNone, + modeList, + modeMap, + modeDependencies, + modeSlideInfo, + modeAcceleratorInfo, + modeTextInfo, + modeLinkEdit, + modeLocalSymbols, + modeInfo, + modeSize, + modeExtract +}; + +struct Options { + Mode mode; + const char* dependentsOfPath; + const void* mappedCache; + const char* extractionDir; + bool printUUIDs; + bool printVMAddrs; + bool printDylibVersions; + bool printInodes; +}; + +struct TextInfo { + uint64_t textSize; + const char* path; +}; + +struct TextInfoSorter { + bool operator()(const TextInfo& left, const TextInfo& right) { + return (left.textSize > right.textSize); + } +}; + +struct Results { + std::map pageToContent; + uint64_t linkeditBase; + bool dependentTargetFound; + std::vector textSegments; +}; + + + +void usage() { + fprintf(stderr, "Usage: dyld_shared_cache_util -list [ -uuid ] [-vmaddr] | -dependents [ -versions ] | -linkedit | -map | -slide_info | -info | -extract [ shared-cache-file ] \n"); +} + +#if __x86_64__ +static bool isHaswell() +{ + // check system is capable of running x86_64h code + struct host_basic_info info; + mach_msg_type_number_t count = HOST_BASIC_INFO_COUNT; + mach_port_t hostPort = mach_host_self(); + kern_return_t result = host_info(hostPort, HOST_BASIC_INFO, (host_info_t)&info, &count); + mach_port_deallocate(mach_task_self(), hostPort); + if ( result != KERN_SUCCESS ) + return false; + return ( info.cpu_subtype == CPU_SUBTYPE_X86_64_H ); +} +#endif + +/* + * Get the path to the native shared cache for this host + */ +static const char* default_shared_cache_path() { +#if __i386__ + return MACOSX_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME "i386"; +#elif __x86_64__ + if ( isHaswell() ) + return MACOSX_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME "x86_64h"; + else + return MACOSX_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME "x86_64"; +#elif __ARM_ARCH_5TEJ__ + return IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME "armv5"; +#elif __ARM_ARCH_6K__ + return IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME "armv6"; +#elif __ARM_ARCH_7K__ + return IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME "armv7k"; +#elif __ARM_ARCH_7A__ + return IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME "armv7"; +#elif __ARM_ARCH_7F__ + return IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME "armv7f"; +#elif __ARM_ARCH_7S__ + return IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME "armv7s"; +#elif __arm64__ + return IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME "arm64"; +#else + #error unsupported architecture +#endif +} + +typedef void (*segment_callback_t)(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo, + const Options& options, Results& results); + + + +/* + * List dependencies from the mach-o header at headerAddr + * in the same format as 'otool -L' + */ +template +void print_dependencies(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo, + const Options& options, Results& results) { + typedef typename A::P P; + typedef typename A::P::E E; + + if ( strcmp(options.dependentsOfPath, dylibInfo->path) != 0 ) + return; + if ( strcmp(segInfo->name, "__TEXT") != 0 ) + return; + + const macho_dylib_command

* dylib_cmd; + const macho_header

* mh = (const macho_header

*)dylibInfo->machHeader; + const macho_load_command

* const cmds = (macho_load_command

*)((uintptr_t)dylibInfo->machHeader + sizeof(macho_header

)); + const uint32_t cmd_count = mh->ncmds(); + const macho_load_command

* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch ( cmd->cmd() ) { + case LC_LOAD_DYLIB: + case LC_ID_DYLIB: + case LC_REEXPORT_DYLIB: + case LC_LOAD_WEAK_DYLIB: + case LC_LOAD_UPWARD_DYLIB: + dylib_cmd = (macho_dylib_command

*)cmd; + if ( options.printDylibVersions ) { + uint32_t compat_vers = dylib_cmd->compatibility_version(); + uint32_t current_vers = dylib_cmd->current_version(); + printf("\t%s", dylib_cmd->name()); + if ( compat_vers != 0xFFFFFFFF ) { + printf("(compatibility version %u.%u.%u, current version %u.%u.%u)\n", + (compat_vers >> 16), + (compat_vers >> 8) & 0xff, + (compat_vers) & 0xff, + (current_vers >> 16), + (current_vers >> 8) & 0xff, + (current_vers) & 0xff); + } + else { + printf("\n"); + } + } + else { + printf("\t%s\n", dylib_cmd->name()); + } + break; + } + cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); + } + results.dependentTargetFound = true; +} + +/* + * Print out a dylib from the shared cache, optionally including the UUID or unslid load address + */ +template +void print_list(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo, + const Options& options, Results& results) +{ + if ( strcmp(segInfo->name, "__TEXT") != 0 ) + return; + + if ( options.printVMAddrs ) + printf("0x%08llX ", segInfo->address); + if ( options.printInodes ) + printf("0x%08llX 0x%08llX ", dylibInfo->inode, dylibInfo->modTime); + if ( options.printUUIDs ) { + if ( dylibInfo->uuid != NULL ) { + const uint8_t* uuid = (uint8_t*)dylibInfo->uuid;; + printf("<%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X> ", + uuid[0], uuid[1], uuid[2], uuid[3], + uuid[4], uuid[5], uuid[6], uuid[7], + uuid[8], uuid[9], uuid[10], uuid[11], + uuid[12], uuid[13], uuid[14], uuid[15]); + } + else + printf("< no uuid in dylib > "); + } + if ( dylibInfo->isAlias ) + printf("[alias] %s\n", dylibInfo->path); + else + printf("%s\n", dylibInfo->path); +} + + +template +void collect_size(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo, + const Options& options, Results& results) +{ + if ( strcmp(segInfo->name, "__TEXT") != 0 ) + return; + if ( dylibInfo->isAlias ) + return; + + TextInfo info; + info.textSize = segInfo->fileSize; + info.path = dylibInfo->path; + results.textSegments.push_back(info); + size_t size = segInfo->fileSize; +} + + + + +static void add_linkedit(uint32_t pageStart, uint32_t pageEnd, const char* message, Results& results) +{ + for (uint32_t p = pageStart; p <= pageEnd; p += 4096) { + std::map::iterator pos = results.pageToContent.find(p); + if ( pos == results.pageToContent.end() ) { + results.pageToContent[p] = strdup(message); + } + else { + const char* oldMessage = pos->second; + char* newMesssage; + asprintf(&newMesssage, "%s, %s", oldMessage, message); + results.pageToContent[p] = newMesssage; + ::free((void*)oldMessage); + } + } +} + + +/* + * get LINKEDIT info for dylib + */ +template +void process_linkedit(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo, + const Options& options, Results& results) { + typedef typename A::P P; + typedef typename A::P::E E; + // filter out symlinks + if ( dylibInfo->isAlias ) + return; + const macho_header

* mh = (const macho_header

*)dylibInfo->machHeader; + uint32_t ncmds = mh->ncmds(); + const macho_load_command

* const cmds = (macho_load_command

*)((long)mh + sizeof(macho_header

)); + const macho_load_command

* cmd = cmds; + for (uint32_t i = 0; i < ncmds; i++) { + if ( cmd->cmd() == LC_DYLD_INFO_ONLY ) { + macho_dyld_info_command

* dyldInfo = (macho_dyld_info_command

*)cmd; + char message[1000]; + const char* shortName = strrchr(dylibInfo->path, '/') + 1; + // add export trie info + if ( dyldInfo->export_size() != 0 ) { + //printf("export_off=0x%X\n", dyldInfo->export_off()); + uint32_t exportPageOffsetStart = dyldInfo->export_off() & (-4096); + uint32_t exportPageOffsetEnd = (dyldInfo->export_off() + dyldInfo->export_size()) & (-4096); + sprintf(message, "exports from %s", shortName); + add_linkedit(exportPageOffsetStart, exportPageOffsetEnd, message, results); + } + // add binding info + if ( dyldInfo->bind_size() != 0 ) { + uint32_t bindPageOffsetStart = dyldInfo->bind_off() & (-4096); + uint32_t bindPageOffsetEnd = (dyldInfo->bind_off() + dyldInfo->bind_size()) & (-4096); + sprintf(message, "bindings from %s", shortName); + add_linkedit(bindPageOffsetStart, bindPageOffsetEnd, message, results); + } + // add lazy binding info + if ( dyldInfo->lazy_bind_size() != 0 ) { + uint32_t lazybindPageOffsetStart = dyldInfo->lazy_bind_off() & (-4096); + uint32_t lazybindPageOffsetEnd = (dyldInfo->lazy_bind_off() + dyldInfo->lazy_bind_size()) & (-4096); + sprintf(message, "lazy bindings from %s", shortName); + add_linkedit(lazybindPageOffsetStart, lazybindPageOffsetEnd, message, results); + } + // add weak binding info + if ( dyldInfo->weak_bind_size() != 0 ) { + uint32_t weakbindPageOffsetStart = dyldInfo->weak_bind_off() & (-4096); + uint32_t weakbindPageOffsetEnd = (dyldInfo->weak_bind_off() + dyldInfo->weak_bind_size()) & (-4096); + sprintf(message, "weak bindings from %s", shortName); + add_linkedit(weakbindPageOffsetStart, weakbindPageOffsetEnd, message, results); + } + } + cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); + } +} + + +/* + * Print out a .map file similar to what update_dyld_shared_cache created when the cache file was built + */ +template +void print_map(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo, const Options& options, Results& results) { + if ( !dylibInfo->isAlias ) + printf("0x%08llX - 0x%08llX %s %s\n", segInfo->address, segInfo->address + segInfo->fileSize, segInfo->name, dylibInfo->path); +} + + +static void checkMode(Mode mode) { + if ( mode != modeNone ) { + fprintf(stderr, "Error: select one of: -list, -dependents, -info, -slide_info, -linkedit, -map, -extract, or -size\n"); + usage(); + exit(1); + } +} + +int main (int argc, const char* argv[]) { + + const char* sharedCachePath = default_shared_cache_path(); + + Options options; + options.mode = modeNone; + options.printUUIDs = false; + options.printVMAddrs = false; + options.printDylibVersions = false; + options.printInodes = false; + options.dependentsOfPath = NULL; + options.extractionDir = NULL; + + for (uint32_t i = 1; i < argc; i++) { + const char* opt = argv[i]; + if (opt[0] == '-') { + if (strcmp(opt, "-list") == 0) { + checkMode(options.mode); + options.mode = modeList; + } + else if (strcmp(opt, "-dependents") == 0) { + checkMode(options.mode); + options.mode = modeDependencies; + options.dependentsOfPath = argv[++i]; + if ( i >= argc ) { + fprintf(stderr, "Error: option -depdendents requires an argument\n"); + usage(); + exit(1); + } + } + else if (strcmp(opt, "-linkedit") == 0) { + checkMode(options.mode); + options.mode = modeLinkEdit; + } + else if (strcmp(opt, "-info") == 0) { + checkMode(options.mode); + options.mode = modeInfo; + } + else if (strcmp(opt, "-slide_info") == 0) { + checkMode(options.mode); + options.mode = modeSlideInfo; + } + else if (strcmp(opt, "-accelerator_info") == 0) { + checkMode(options.mode); + options.mode = modeAcceleratorInfo; + } + else if (strcmp(opt, "-text_info") == 0) { + checkMode(options.mode); + options.mode = modeTextInfo; + } + else if (strcmp(opt, "-local_symbols") == 0) { + checkMode(options.mode); + options.mode = modeLocalSymbols; + } + else if (strcmp(opt, "-map") == 0) { + checkMode(options.mode); + options.mode = modeMap; + } + else if (strcmp(opt, "-size") == 0) { + checkMode(options.mode); + options.mode = modeSize; + } + else if (strcmp(opt, "-extract") == 0) { + checkMode(options.mode); + options.mode = modeExtract; + options.extractionDir = argv[++i]; + if ( i >= argc ) { + fprintf(stderr, "Error: option -extract requires a directory argument\n"); + usage(); + exit(1); + } + } + else if (strcmp(opt, "-uuid") == 0) { + options.printUUIDs = true; + } + else if (strcmp(opt, "-inode") == 0) { + options.printInodes = true; + } + else if (strcmp(opt, "-versions") == 0) { + options.printDylibVersions = true; + } + else if (strcmp(opt, "-vmaddr") == 0) { + options.printVMAddrs = true; + } + else { + fprintf(stderr, "Error: unrecognized option %s\n", opt); + usage(); + exit(1); + } + } + else { + sharedCachePath = opt; + } + } + + if ( options.mode == modeNone ) { + fprintf(stderr, "Error: select one of -list, -dependents, -info, -linkedit, or -map\n"); + usage(); + exit(1); + } + + if ( options.mode != modeSlideInfo ) { + if ( options.printUUIDs && (options.mode != modeList) ) + fprintf(stderr, "Warning: -uuid option ignored outside of -list mode\n"); + + if ( options.printVMAddrs && (options.mode != modeList) ) + fprintf(stderr, "Warning: -vmaddr option ignored outside of -list mode\n"); + + if ( options.printDylibVersions && (options.mode != modeDependencies) ) + fprintf(stderr, "Warning: -versions option ignored outside of -dependents mode\n"); + + if ( (options.mode == modeDependencies) && (options.dependentsOfPath == NULL) ) { + fprintf(stderr, "Error: -dependents given, but no dylib path specified\n"); + usage(); + exit(1); + } + } + + struct stat statbuf; + if ( ::stat(sharedCachePath, &statbuf) == -1 ) { + fprintf(stderr, "Error: stat() failed for dyld shared cache at %s, errno=%d\n", sharedCachePath, errno); + exit(1); + } + + int cache_fd = ::open(sharedCachePath, O_RDONLY); + if ( cache_fd < 0 ) { + fprintf(stderr, "Error: open() failed for shared cache file at %s, errno=%d\n", sharedCachePath, errno); + exit(1); + } + options.mappedCache = ::mmap(NULL, statbuf.st_size, PROT_READ, MAP_PRIVATE, cache_fd, 0); + if (options.mappedCache == MAP_FAILED) { + fprintf(stderr, "Error: mmap() for shared cache at %s failed, errno=%d\n", sharedCachePath, errno); + exit(1); + } + + if ( options.mode == modeSlideInfo ) { + const dyldCacheHeader* header = (dyldCacheHeader*)options.mappedCache; + if ( header->slideInfoOffset() == 0 ) { + fprintf(stderr, "Error: dyld shared cache does not contain slide info\n"); + exit(1); + } + const dyldCacheFileMapping* mappings = (dyldCacheFileMapping*)((char*)options.mappedCache + header->mappingOffset()); + const dyldCacheFileMapping* dataMapping = &mappings[1]; + uint64_t dataStartAddress = dataMapping->address(); + uint64_t dataSize = dataMapping->size(); + const dyldCacheSlideInfo* slideInfoHeader = (dyldCacheSlideInfo*)((char*)options.mappedCache+header->slideInfoOffset()); + printf("slide info version=%d\n", slideInfoHeader->version()); + if ( slideInfoHeader->version() == 1 ) { + printf("toc_count=%d, data page count=%lld\n", slideInfoHeader->toc_count(), dataSize/4096); + const dyldCacheSlideInfoEntry* entries = (dyldCacheSlideInfoEntry*)((char*)slideInfoHeader + slideInfoHeader->entries_offset()); + for(int i=0; i < slideInfoHeader->toc_count(); ++i) { + printf("0x%08llX: [% 5d,% 5d] ", dataStartAddress + i*4096, i, slideInfoHeader->toc(i)); + const dyldCacheSlideInfoEntry* entry = &entries[slideInfoHeader->toc(i)]; + for(int j=0; j < slideInfoHeader->entries_size(); ++j) + printf("%02X", entry->bits[j]); + printf("\n"); + } + } + else if ( slideInfoHeader->version() == 2 ) { + const dyldCacheSlideInfo2* slideInfo = (dyldCacheSlideInfo2*)(slideInfoHeader); + printf("page_size=%d\n", slideInfo->page_size()); + printf("delta_mask=0x%016llX\n", slideInfo->delta_mask()); + printf("value_add=0x%016llX\n", slideInfo->value_add()); + printf("page_starts_count=%d, page_extras_count=%d\n", slideInfo->page_starts_count(), slideInfo->page_extras_count()); + const uint16_t* starts = (uint16_t* )((char*)slideInfo + slideInfo->page_starts_offset()); + const uint16_t* extras = (uint16_t* )((char*)slideInfo + slideInfo->page_extras_offset()); + for (int i=0; i < slideInfo->page_starts_count(); ++i) { + const uint16_t start = starts[i]; + if ( start == DYLD_CACHE_SLIDE_PAGE_ATTR_NO_REBASE ) { + printf("page[% 5d]: no rebasing\n", i); + } + else if ( start & DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA ) { + printf("page[% 5d]: ", i); + int j=(start & 0x3FFF); + bool done = false; + do { + uint16_t aStart = extras[j]; + printf("start=0x%04X ", aStart & 0x3FFF); + done = (extras[j] & DYLD_CACHE_SLIDE_PAGE_ATTR_END); + ++j; + } while ( !done ); + printf("\n"); + } + else { + printf("page[% 5d]: start=0x%04X\n", i, starts[i]); + } + } + } + } + else if ( options.mode == modeInfo ) { + const dyldCacheHeader* header = (dyldCacheHeader*)options.mappedCache; + printf("uuid: "); + if ( header->mappingOffset() >= 0x68 ) { + const uint8_t* uuid = header->uuid(); + printf("%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X\n", + uuid[0], uuid[1], uuid[2], uuid[3], + uuid[4], uuid[5], uuid[6], uuid[7], + uuid[8], uuid[9], uuid[10], uuid[11], + uuid[12], uuid[13], uuid[14], uuid[15]); + } + else { + printf("n/a\n"); + } + printf("image count: %u\n", header->imagesCount()); + if ( (header->mappingOffset() >= 0x78) && (header->branchPoolsOffset() != 0) ) { + printf("branch pool count: %u\n", header->branchPoolsCount()); + } + printf("mappings:\n"); + const dyldCacheFileMapping* mappings = (dyldCacheFileMapping*)((char*)options.mappedCache + header->mappingOffset()); + for (uint32_t i=0; i < header->mappingCount(); ++i) { + if ( mappings[i].init_prot() & VM_PROT_EXECUTE ) + printf(" __TEXT %3lluMB, file offset: 0x%08llX -> 0x%08llX, address: 0x%08llX -> 0x%08llX\n", + mappings[i].size()/(1024*1024), mappings[i].file_offset(), mappings[i].file_offset() + mappings[i].size(), + mappings[i].address(), mappings[i].address() + mappings[i].size()); + else if ( mappings[i]. init_prot() & VM_PROT_WRITE ) + printf(" __DATA %3lluMB, file offset: 0x%08llX -> 0x%08llX, address: 0x%08llX -> 0x%08llX\n", + mappings[i].size()/(1024*1024), mappings[i].file_offset(), mappings[i].file_offset() + mappings[i].size(), + mappings[i].address(), mappings[i].address() + mappings[i].size()); + else if ( mappings[i].init_prot() & VM_PROT_READ ) + printf(" __LINKEDIT %3lluMB, file offset: 0x%08llX -> 0x%08llX, address: 0x%08llX -> 0x%08llX\n", + mappings[i].size()/(1024*1024), mappings[i].file_offset(), mappings[i].file_offset() + mappings[i].size(), + mappings[i].address(), mappings[i].address() + mappings[i].size()); + } + if ( header->codeSignatureOffset() != 0 ) { + uint64_t size = statbuf.st_size - header->codeSignatureOffset(); + uint64_t csAddr = mappings[header->mappingCount()-1].address() + mappings[header->mappingCount()-1].size(); + if ( size != 0 ) + printf(" code sign %3lluMB, file offset: 0x%08llX -> 0x%08llX, address: 0x%08llX -> 0x%08llX\n", + size/(1024*1024), header->codeSignatureOffset(), header->codeSignatureOffset() + size, csAddr, csAddr + size); + } + printf("slide info: %4lluKB, file offset: 0x%08llX -> 0x%08llX\n", + header->slideInfoSize()/1024, header->slideInfoOffset(), header->slideInfoOffset() + header->slideInfoSize()); + if ( header->localSymbolsOffset() != 0 ) + printf("local symbols: %3lluMB, file offset: 0x%08llX -> 0x%08llX\n", + header->localSymbolsSize()/(1024*1024), header->localSymbolsOffset(), header->localSymbolsOffset() + header->localSymbolsSize()); + if ( (header->mappingOffset() >= 0x78) && (header->accelerateInfoSize() != 0) ) + printf("accelerate tab: %3lluKB, address: 0x%08llX -> 0x%08llX\n", + header->accelerateInfoSize()/1024, header->accelerateInfoAddr(), header->accelerateInfoAddr() + header->accelerateInfoSize()); + } + else if ( options.mode == modeAcceleratorInfo ) { + const dyldCacheHeader* header = (dyldCacheHeader*)options.mappedCache; + if ( (header->mappingOffset() < sizeof(dyldCacheHeader)) || (header->accelerateInfoSize() == 0) ) { + printf("no accelerator info\n"); + } + else { + const dyldCacheFileMapping* mappings = (dyldCacheFileMapping*)((char*)options.mappedCache + header->mappingOffset()); + uint64_t aiAddr = header->accelerateInfoAddr(); + dyldCacheAcceleratorInfo* accelInfo = NULL; + for (uint32_t i=0; i < header->mappingCount(); ++i) { + if ( (mappings[i].address() <= aiAddr) && (aiAddr < mappings[i].address()+mappings[i].size()) ) { + uint64_t offset = aiAddr - mappings[i].address() + mappings[i].file_offset(); + accelInfo = (dyldCacheAcceleratorInfo*)((uint8_t*)options.mappedCache + offset); + } + } + if ( accelInfo == NULL ) { + printf("accelerator info not in any mapped range\n"); + } + else { + const dyldCacheImageInfo* images = (dyldCacheImageInfo*)((char*)options.mappedCache + header->imagesOffset()); + const dyldCacheImageInfoExtra* imagesExtra = (dyldCacheImageInfoExtra*)((char*)accelInfo + accelInfo->imagesExtrasOffset()); + const uint16_t* dependencyArray = (uint16_t*)((char*)accelInfo + accelInfo->depListOffset()); + const uint16_t* reExportArray = (uint16_t*)((char*)accelInfo + accelInfo->reExportListOffset()); + printf("extra image info (count=%u):\n", accelInfo->imageExtrasCount()); + for (uint32_t i=0; i < accelInfo->imageExtrasCount(); ++i) { + printf(" image[%3u] %s:\n", i, (char*)options.mappedCache +images[i].pathFileOffset()); + printf(" exports trie: addr=0x%llX, size=0x%08X\n", imagesExtra[i].exportsTrieAddr(), imagesExtra[i].exportsTrieSize()); + if ( imagesExtra[i].weakBindingsSize() ) + printf(" weak bind info: addr=0x%llX, size=0x%08X\n", imagesExtra[i].weakBindingsAddr(), imagesExtra[i].weakBindingsSize()); + printf(" dependents: "); + for (uint32_t d=imagesExtra[i].dependentsStartArrayIndex(); dependencyArray[d] != 0xFFFF; ++d) { + uint16_t depIndex = dependencyArray[d]; + if ( depIndex & 0x8000 ) + printf(" up(%d) ", depIndex & 0x7FFF); + else + printf(" %d ", depIndex); + } + printf("\n"); + printf(" re-exports: "); + for (uint32_t r=imagesExtra[i].reExportsStartArrayIndex(); reExportArray[r] != 0xFFFF; ++r) + printf(" %d ", reExportArray[r]); + printf("\n"); + } + printf("libdyld.dylib:\n"); + printf(" __dyld section address: 0x%llX\n", accelInfo->dyldSectionAddr()); + printf("initializers (count=%u):\n", accelInfo->initializersCount()); + const dyldCacheAcceleratorInitializer* initializers = (dyldCacheAcceleratorInitializer*)((char*)accelInfo + accelInfo->initializersOffset()); + for (uint32_t i=0; i < accelInfo->initializersCount(); ++i) { + printf(" image[%3u] 0x%llX\n", initializers[i].imageIndex(), mappings[0].address() + initializers[i].functionOffset()); + } + printf("DOF sections (count=%u):\n", accelInfo->dofSectionsCount()); + const dyldCacheAcceleratorDOFEntry* dofs = (dyldCacheAcceleratorDOFEntry*)((char*)accelInfo + accelInfo->dofSectionsOffset()); + for (uint32_t i=0; i < accelInfo->dofSectionsCount(); ++i) { + printf(" image[%3u] 0x%llX -> 0x%llX\n", dofs[i].imageIndex(), dofs[i].sectionAddress(), dofs[i].sectionAddress()+dofs[i].sectionSize()); + } + printf("bottom up order (count=%u):\n", accelInfo->imageExtrasCount()); + const uint16_t* bottomUpArray = (uint16_t*)((char*)accelInfo + accelInfo->bottomUpListOffset()); + for (uint32_t i=0; i < accelInfo->imageExtrasCount(); ++i) { + unsigned imageIndex = bottomUpArray[i]; + if ( imageIndex < accelInfo->imageExtrasCount() ) + printf(" image[%3u] %s\n", imageIndex, (char*)options.mappedCache + images[imageIndex].pathFileOffset()); + else + printf(" image[%3u] BAD INDEX\n", imageIndex); + } + printf("range table (count=%u):\n", accelInfo->rangeTableCount()); + const dyldCacheAcceleratorRangeEntry* rangeTable = (dyldCacheAcceleratorRangeEntry*)((char*)accelInfo + accelInfo->rangeTableOffset()); + for (uint32_t i=0; i < accelInfo->rangeTableCount(); ++i) { + const dyldCacheAcceleratorRangeEntry& entry = rangeTable[i]; + printf(" 0x%llX -> 0x%llX %s\n", entry.startAddress(), entry.startAddress() + entry.size(), (char*)options.mappedCache + images[entry.imageIndex()].pathFileOffset()); + } + printf("dylib trie (size=%u):\n", accelInfo->dylibTrieSize()); + const uint8_t* dylibTrieStart = (uint8_t*)accelInfo + accelInfo->dylibTrieOffset(); + const uint8_t* dylibTrieEnd = dylibTrieStart + accelInfo->dylibTrieSize(); + std::vector dylibEntries; + if ( !Trie::parseTrie(dylibTrieStart, dylibTrieEnd, dylibEntries) ) + printf(" malformed dylibs trie\n"); + for (const DylibIndexTrie::Entry& x : dylibEntries) { + printf(" image[%3u] %s\n", x.info.index, x.name.c_str()); + } + } + } + } + else if ( options.mode == modeTextInfo ) { + const dyldCacheHeader* header = (dyldCacheHeader*)options.mappedCache; + if ( (header->mappingOffset() < sizeof(dyldCacheHeader)) || (header->imagesTextCount() == 0) ) { + printf("no text info\n"); + } + else { + const dyldCacheImageTextInfo* imagesText = (dyldCacheImageTextInfo*)((char*)options.mappedCache + header->imagesTextOffset()); + const dyldCacheImageTextInfo* imagesTextEnd = &imagesText[header->imagesTextCount()]; + printf("dylib text infos (count=%llu):\n", header->imagesTextCount()); + for (const dyldCacheImageTextInfo* p=imagesText; p < imagesTextEnd; ++p) { + printf(" 0x%09llX -> 0x%09llX <", p->loadAddress(), p->loadAddress() + p->textSegmentSize()); + for (int i=0; i<16; ++i) { + switch (i) { + case 4: + case 6: + case 8: + case 10: + printf("-"); + break; + } + printf("%02X", p->uuid()[i]); + } + printf("> %s\n", (char*)options.mappedCache + p->pathOffset()); + } + } + } + else if ( options.mode == modeLocalSymbols ) { + const dyldCacheHeader* header = (dyldCacheHeader*)options.mappedCache; + if ( header->localSymbolsOffset() == 0 ) { + fprintf(stderr, "Error: dyld shared cache does not contain local symbols info\n"); + exit(1); + } + const bool is64 = (strstr((char*)options.mappedCache, "64") != NULL); + const dyldCacheImageInfo* imageInfos = (dyldCacheImageInfo*)((char*)options.mappedCache + header->imagesOffset()); + const dyldCacheLocalSymbolsInfo* localsInfo = (dyldCacheLocalSymbolsInfo*)((char*)options.mappedCache + header->localSymbolsOffset()); + const uint32_t nlistFileOffset = (uint32_t)(header->localSymbolsOffset() + localsInfo->nlistOffset()); + const uint32_t nlistCount = localsInfo->nlistCount(); + const uint32_t nlistByteSize = is64 ? nlistCount*16 : nlistCount*12; + const uint32_t stringsFileOffset = (uint32_t)(header->localSymbolsOffset() + localsInfo->stringsOffset()); + const uint32_t stringsSize = localsInfo->stringsSize(); + const uint32_t entriesCount = localsInfo->entriesCount(); + const dyldCacheLocalSymbolEntry* entries = (dyldCacheLocalSymbolEntry*)((char*)localsInfo + localsInfo->entriesOffset()); + printf("local symbols nlist array: %3uMB, file offset: 0x%08X -> 0x%08X\n", nlistByteSize/(1024*1024), nlistFileOffset, nlistFileOffset+nlistByteSize); + printf("local symbols string pool: %3uMB, file offset: 0x%08X -> 0x%08X\n", stringsSize/(1024*1024), stringsFileOffset, stringsFileOffset+stringsSize); + printf("local symbols by dylib (count=%d):\n", entriesCount); + const char* stringPool = (char*)options.mappedCache + stringsFileOffset; + for (int i=0; i < entriesCount; ++i) { + const char* imageName = (char*)options.mappedCache + imageInfos[i].pathFileOffset(); + printf(" nlistStartIndex=%5d, nlistCount=%5d, image=%s\n", entries[i].nlistStartIndex(), entries[i].nlistCount(), imageName); + #if 0 + if ( is64 ) { + const nlist_64* symTab = (nlist_64*)((char*)options.mappedCache + nlistFileOffset); + for (int e=0; e < entries[i].nlistCount(); ++e) { + const nlist_64* entry = &symTab[entries[i].nlistStartIndex()+e]; + printf(" nlist[%d].str=%d, %s\n", e, entry->n_un.n_strx, &stringPool[entry->n_un.n_strx]); + printf(" nlist[%d].value=0x%0llX\n", e, entry->n_value); + } + } + #endif + } + } + else if ( options.mode == modeExtract ) { + char pathBuffer[PATH_MAX]; + uint32_t bufferSize = PATH_MAX; + if ( _NSGetExecutablePath(pathBuffer, &bufferSize) != 0 ) { + fprintf(stderr, "Error: could not get path of program\n"); + return 1; + } + char* last = strrchr(pathBuffer, '/'); + strcpy(last+1, "../../lib/dsc_extractor.bundle"); + void* handle = dlopen(pathBuffer, RTLD_LAZY); + if ( handle == NULL ) { + fprintf(stderr, "Error: dsc_extractor.bundle could not be loaded at %s\n", pathBuffer); + return 1; + } + + typedef int (*extractor_proc)(const char* shared_cache_file_path, const char* extraction_root_path, + void (^progress)(unsigned current, unsigned total)); + + extractor_proc proc = (extractor_proc)dlsym(handle, "dyld_shared_cache_extract_dylibs_progress"); + if ( proc == NULL ) { + fprintf(stderr, "Error: dsc_extractor.bundle did not have dyld_shared_cache_extract_dylibs_progress symbol\n"); + return 1; + } + + int result = (*proc)(sharedCachePath, options.extractionDir, ^(unsigned c, unsigned total) { } ); + return result; + } + else { + segment_callback_t callback; + if ( strcmp((char*)options.mappedCache, "dyld_v1 i386") == 0 ) { + switch ( options.mode ) { + case modeList: + callback = print_list; + break; + case modeMap: + callback = print_map; + break; + case modeDependencies: + callback = print_dependencies; + break; + case modeLinkEdit: + callback = process_linkedit; + break; + case modeSize: + callback = collect_size; + break; + case modeNone: + case modeInfo: + case modeSlideInfo: + case modeAcceleratorInfo: + case modeTextInfo: + case modeLocalSymbols: + case modeExtract: + break; + } + } + else if ( (strcmp((char*)options.mappedCache, "dyld_v1 x86_64") == 0) + || (strcmp((char*)options.mappedCache, "dyld_v1 x86_64h") == 0) ) { + switch ( options.mode ) { + case modeList: + callback = print_list; + break; + case modeMap: + callback = print_map; + break; + case modeDependencies: + callback = print_dependencies; + break; + case modeLinkEdit: + callback = process_linkedit; + break; + case modeSize: + callback = collect_size; + break; + case modeNone: + case modeInfo: + case modeSlideInfo: + case modeAcceleratorInfo: + case modeTextInfo: + case modeLocalSymbols: + case modeExtract: + break; + } + } + else if ( (strncmp((char*)options.mappedCache, "dyld_v1 armv", 14) == 0) + || (strncmp((char*)options.mappedCache, "dyld_v1 armv", 13) == 0) ) { + switch ( options.mode ) { + case modeList: + callback = print_list; + break; + case modeMap: + callback = print_map; + break; + case modeDependencies: + callback = print_dependencies; + break; + case modeLinkEdit: + callback = process_linkedit; + break; + case modeSize: + callback = collect_size; + break; + case modeNone: + case modeInfo: + case modeSlideInfo: + case modeAcceleratorInfo: + case modeTextInfo: + case modeLocalSymbols: + case modeExtract: + break; + } + } + else if ( strcmp((char*)options.mappedCache, "dyld_v1 arm64") == 0 ) { + switch ( options.mode ) { + case modeList: + callback = print_list; + break; + case modeMap: + callback = print_map; + break; + case modeDependencies: + callback = print_dependencies; + break; + case modeLinkEdit: + callback = process_linkedit; + break; + case modeSize: + callback = collect_size; + break; + case modeNone: + case modeInfo: + case modeSlideInfo: + case modeAcceleratorInfo: + case modeTextInfo: + case modeLocalSymbols: + case modeExtract: + break; + } + } + else { + fprintf(stderr, "Error: unrecognized dyld shared cache magic.\n"); + exit(1); + } + + __block Results results; + results.dependentTargetFound = false; + int iterateResult = dyld_shared_cache_iterate(options.mappedCache, (uint32_t)statbuf.st_size, + ^(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo ) { + (callback)(dylibInfo, segInfo, options, results); + }); + if ( iterateResult != 0 ) { + fprintf(stderr, "Error: malformed shared cache file\n"); + exit(1); + } + + if ( options.mode == modeLinkEdit ) { + // dump -linkedit information + for (std::map::iterator it = results.pageToContent.begin(); it != results.pageToContent.end(); ++it) { + printf("0x%08X %s\n", it->first, it->second); + } + } + else if ( options.mode == modeSize ) { + std::sort(results.textSegments.begin(), results.textSegments.end(), TextInfoSorter()); + for (std::vector::iterator it = results.textSegments.begin(); it != results.textSegments.end(); ++it) { + printf(" 0x%08llX %s\n", it->textSize, it->path); + } + } + + if ( (options.mode == modeDependencies) && options.dependentsOfPath && !results.dependentTargetFound) { + fprintf(stderr, "Error: could not find '%s' in the shared cache at\n %s\n", options.dependentsOfPath, sharedCachePath); + exit(1); + } + } + return 0; +} diff --git a/src/dyld-apple/launch-cache/update_dyld_shared_cache_entitlements.plist b/src/dyld-apple/launch-cache/update_dyld_shared_cache_entitlements.plist new file mode 100644 index 000000000..5ed9d1cfd --- /dev/null +++ b/src/dyld-apple/launch-cache/update_dyld_shared_cache_entitlements.plist @@ -0,0 +1,8 @@ + + + + + com.apple.rootless.storage.dyld + + + diff --git a/src/dyld-apple/src/ImageLoader.cpp b/src/dyld-apple/src/ImageLoader.cpp new file mode 100644 index 000000000..3d8fa6e24 --- /dev/null +++ b/src/dyld-apple/src/ImageLoader.cpp @@ -0,0 +1,1347 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2004-2010 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@ + */ + +#define __STDC_LIMIT_MACROS +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ImageLoader.h" + + +uint32_t ImageLoader::fgImagesUsedFromSharedCache = 0; +uint32_t ImageLoader::fgImagesWithUsedPrebinding = 0; +uint32_t ImageLoader::fgImagesRequiringCoalescing = 0; +uint32_t ImageLoader::fgImagesHasWeakDefinitions = 0; +uint32_t ImageLoader::fgTotalRebaseFixups = 0; +uint32_t ImageLoader::fgTotalBindFixups = 0; +uint32_t ImageLoader::fgTotalBindSymbolsResolved = 0; +uint32_t ImageLoader::fgTotalBindImageSearches = 0; +uint32_t ImageLoader::fgTotalLazyBindFixups = 0; +uint32_t ImageLoader::fgTotalPossibleLazyBindFixups = 0; +uint32_t ImageLoader::fgTotalSegmentsMapped = 0; +uint64_t ImageLoader::fgTotalBytesMapped = 0; +uint64_t ImageLoader::fgTotalBytesPreFetched = 0; +uint64_t ImageLoader::fgTotalLoadLibrariesTime; +uint64_t ImageLoader::fgTotalObjCSetupTime = 0; +uint64_t ImageLoader::fgTotalDebuggerPausedTime = 0; +uint64_t ImageLoader::fgTotalRebindCacheTime = 0; +uint64_t ImageLoader::fgTotalRebaseTime; +uint64_t ImageLoader::fgTotalBindTime; +uint64_t ImageLoader::fgTotalWeakBindTime; +uint64_t ImageLoader::fgTotalDOF; +uint64_t ImageLoader::fgTotalInitTime; +uint16_t ImageLoader::fgLoadOrdinal = 0; +uint32_t ImageLoader::fgSymbolTrieSearchs = 0; +std::vectorImageLoader::fgInterposingTuples; +uintptr_t ImageLoader::fgNextPIEDylibAddress = 0; + + + +ImageLoader::ImageLoader(const char* path, unsigned int libCount) + : fPath(path), fRealPath(NULL), fDevice(0), fInode(0), fLastModified(0), + fPathHash(0), fDlopenReferenceCount(0), fInitializerRecursiveLock(NULL), + fDepth(0), fLoadOrder(fgLoadOrdinal++), fState(0), fLibraryCount(libCount), + fAllLibraryChecksumsAndLoadAddressesMatch(false), fLeaveMapped(false), fNeverUnload(false), + fHideSymbols(false), fMatchByInstallName(false), + fInterposed(false), fRegisteredDOF(false), fAllLazyPointersBound(false), + fBeingRemoved(false), fAddFuncNotified(false), + fPathOwnedByImage(false), fIsReferencedDownward(false), + fWeakSymbolsBound(false) +{ + if ( fPath != NULL ) + fPathHash = hash(fPath); + if ( libCount > 512 ) + dyld::throwf("too many dependent dylibs in %s", path); +} + + +void ImageLoader::deleteImage(ImageLoader* image) +{ + delete image; +} + + +ImageLoader::~ImageLoader() +{ + if ( fRealPath != NULL ) + delete [] fRealPath; + if ( fPathOwnedByImage && (fPath != NULL) ) + delete [] fPath; +} + +void ImageLoader::setFileInfo(dev_t device, ino_t inode, time_t modDate) +{ + fDevice = device; + fInode = inode; + fLastModified = modDate; +} + +void ImageLoader::setMapped(const LinkContext& context) +{ + fState = dyld_image_state_mapped; + context.notifySingle(dyld_image_state_mapped, this, NULL); // note: can throw exception +} + +int ImageLoader::compare(const ImageLoader* right) const +{ + if ( this->fDepth == right->fDepth ) { + if ( this->fLoadOrder == right->fLoadOrder ) + return 0; + else if ( this->fLoadOrder < right->fLoadOrder ) + return -1; + else + return 1; + } + else { + if ( this->fDepth < right->fDepth ) + return -1; + else + return 1; + } +} + +void ImageLoader::setPath(const char* path) +{ + if ( fPathOwnedByImage && (fPath != NULL) ) + delete [] fPath; + fPath = new char[strlen(path)+1]; + strcpy((char*)fPath, path); + fPathOwnedByImage = true; // delete fPath when this image is destructed + fPathHash = hash(fPath); + fRealPath = NULL; +} + +void ImageLoader::setPathUnowned(const char* path) +{ + if ( fPathOwnedByImage && (fPath != NULL) ) { + delete [] fPath; + } + fPath = path; + fPathOwnedByImage = false; + fPathHash = hash(fPath); +} + +void ImageLoader::setPaths(const char* path, const char* realPath) +{ + this->setPath(path); + fRealPath = new char[strlen(realPath)+1]; + strcpy((char*)fRealPath, realPath); +} + +const char* ImageLoader::getRealPath() const +{ + if ( fRealPath != NULL ) + return fRealPath; + else + return fPath; +} + + +uint32_t ImageLoader::hash(const char* path) +{ + // this does not need to be a great hash + // it is just used to reduce the number of strcmp() calls + // of existing images when loading a new image + uint32_t h = 0; + for (const char* s=path; *s != '\0'; ++s) + h = h*5 + *s; + return h; +} + +bool ImageLoader::matchInstallPath() const +{ + return fMatchByInstallName; +} + +void ImageLoader::setMatchInstallPath(bool match) +{ + fMatchByInstallName = match; +} + +bool ImageLoader::statMatch(const struct stat& stat_buf) const +{ + return ( (this->fDevice == stat_buf.st_dev) && (this->fInode == stat_buf.st_ino) ); +} + +const char* ImageLoader::shortName(const char* fullName) +{ + // try to return leaf name + if ( fullName != NULL ) { + const char* s = strrchr(fullName, '/'); + if ( s != NULL ) + return &s[1]; + } + return fullName; +} + +const char* ImageLoader::getShortName() const +{ + return shortName(fPath); +} + +void ImageLoader::setLeaveMapped() +{ + fLeaveMapped = true; +} + +void ImageLoader::setHideExports(bool hide) +{ + fHideSymbols = hide; +} + +bool ImageLoader::hasHiddenExports() const +{ + return fHideSymbols; +} + +bool ImageLoader::isLinked() const +{ + return (fState >= dyld_image_state_bound); +} + +time_t ImageLoader::lastModified() const +{ + return fLastModified; +} + +bool ImageLoader::containsAddress(const void* addr) const +{ + for(unsigned int i=0, e=segmentCount(); i < e; ++i) { + const uint8_t* start = (const uint8_t*)segActualLoadAddress(i); + const uint8_t* end = (const uint8_t*)segActualEndAddress(i); + if ( (start <= addr) && (addr < end) && !segUnaccessible(i) ) + return true; + } + return false; +} + +bool ImageLoader::overlapsWithAddressRange(const void* start, const void* end) const +{ + for(unsigned int i=0, e=segmentCount(); i < e; ++i) { + const uint8_t* segStart = (const uint8_t*)segActualLoadAddress(i); + const uint8_t* segEnd = (const uint8_t*)segActualEndAddress(i); + if ( strcmp(segName(i), "__UNIXSTACK") == 0 ) { + // __UNIXSTACK never slides. This is the only place that cares + // and checking for that segment name in segActualLoadAddress() + // is too expensive. + segStart -= getSlide(); + segEnd -= getSlide(); + } + if ( (start <= segStart) && (segStart < end) ) + return true; + if ( (start <= segEnd) && (segEnd < end) ) + return true; + if ( (segStart < start) && (end < segEnd) ) + return true; + } + return false; +} + +void ImageLoader::getMappedRegions(MappedRegion*& regions) const +{ + for(unsigned int i=0, e=segmentCount(); i < e; ++i) { + MappedRegion region; + region.address = segActualLoadAddress(i); + region.size = segSize(i); + *regions++ = region; + } +} + + + +bool ImageLoader::dependsOn(ImageLoader* image) { + for(unsigned int i=0; i < libraryCount(); ++i) { + if ( libImage(i) == image ) + return true; + } + return false; +} + + +static bool notInImgageList(const ImageLoader* image, const ImageLoader** dsiStart, const ImageLoader** dsiCur) +{ + for (const ImageLoader** p = dsiStart; p < dsiCur; ++p) + if ( *p == image ) + return false; + return true; +} + +bool ImageLoader::findExportedSymbolAddress(const LinkContext& context, const char* symbolName, + const ImageLoader* requestorImage, int requestorOrdinalOfDef, + bool runResolver, const ImageLoader** foundIn, uintptr_t* address) const +{ + const Symbol* sym = this->findExportedSymbol(symbolName, true, foundIn); + if ( sym != NULL ) { + *address = (*foundIn)->getExportedSymbolAddress(sym, context, requestorImage, runResolver); + return true; + } + return false; +} + + +// private method that handles circular dependencies by only search any image once +const ImageLoader::Symbol* ImageLoader::findExportedSymbolInDependentImagesExcept(const char* name, + const ImageLoader** dsiStart, const ImageLoader**& dsiCur, const ImageLoader** dsiEnd, const ImageLoader** foundIn) const +{ + const ImageLoader::Symbol* sym; + // search self + if ( notInImgageList(this, dsiStart, dsiCur) ) { + sym = this->findExportedSymbol(name, false, this->getPath(), foundIn); + if ( sym != NULL ) + return sym; + *dsiCur++ = this; + } + + // search directly dependent libraries + for(unsigned int i=0; i < libraryCount(); ++i) { + ImageLoader* dependentImage = libImage(i); + if ( (dependentImage != NULL) && notInImgageList(dependentImage, dsiStart, dsiCur) ) { + sym = dependentImage->findExportedSymbol(name, false, libPath(i), foundIn); + if ( sym != NULL ) + return sym; + } + } + + // search indirectly dependent libraries + for(unsigned int i=0; i < libraryCount(); ++i) { + ImageLoader* dependentImage = libImage(i); + if ( (dependentImage != NULL) && notInImgageList(dependentImage, dsiStart, dsiCur) ) { + *dsiCur++ = dependentImage; + sym = dependentImage->findExportedSymbolInDependentImagesExcept(name, dsiStart, dsiCur, dsiEnd, foundIn); + if ( sym != NULL ) + return sym; + } + } + + return NULL; +} + + +const ImageLoader::Symbol* ImageLoader::findExportedSymbolInDependentImages(const char* name, const LinkContext& context, const ImageLoader** foundIn) const +{ + unsigned int imageCount = context.imageCount()+2; + const ImageLoader* dontSearchImages[imageCount]; + dontSearchImages[0] = this; // don't search this image + const ImageLoader** cur = &dontSearchImages[1]; + return this->findExportedSymbolInDependentImagesExcept(name, &dontSearchImages[0], cur, &dontSearchImages[imageCount], foundIn); +} + +const ImageLoader::Symbol* ImageLoader::findExportedSymbolInImageOrDependentImages(const char* name, const LinkContext& context, const ImageLoader** foundIn) const +{ + unsigned int imageCount = context.imageCount()+2; + const ImageLoader* dontSearchImages[imageCount]; + const ImageLoader** cur = &dontSearchImages[0]; + return this->findExportedSymbolInDependentImagesExcept(name, &dontSearchImages[0], cur, &dontSearchImages[imageCount], foundIn); +} + +// this is called by initializeMainExecutable() to interpose on the initial set of images +void ImageLoader::applyInterposing(const LinkContext& context) +{ + if ( fgInterposingTuples.size() != 0 ) + this->recursiveApplyInterposing(context); +} + + +uintptr_t ImageLoader::interposedAddress(const LinkContext& context, uintptr_t address, const ImageLoader* inImage, const ImageLoader* onlyInImage) +{ + //dyld::log("interposedAddress(0x%08llX), tupleCount=%lu\n", (uint64_t)address, fgInterposingTuples.size()); + for (std::vector::iterator it=fgInterposingTuples.begin(); it != fgInterposingTuples.end(); it++) { + //dyld::log(" interposedAddress: replacee=0x%08llX, replacement=0x%08llX, neverImage=%p, onlyImage=%p, inImage=%p\n", + // (uint64_t)it->replacee, (uint64_t)it->replacement, it->neverImage, it->onlyImage, inImage); + // replace all references to 'replacee' with 'replacement' + if ( (address == it->replacee) && (inImage != it->neverImage) && ((it->onlyImage == NULL) || (inImage == it->onlyImage)) ) { + if ( context.verboseInterposing ) { + dyld::log("dyld interposing: replace 0x%lX with 0x%lX\n", it->replacee, it->replacement); + } + return it->replacement; + } + } + return address; +} + +void ImageLoader::addDynamicInterposingTuples(const struct dyld_interpose_tuple array[], size_t count) +{ + for(size_t i=0; i < count; ++i) { + ImageLoader::InterposeTuple tuple; + tuple.replacement = (uintptr_t)array[i].replacement; + tuple.neverImage = NULL; + tuple.onlyImage = this; + tuple.replacee = (uintptr_t)array[i].replacee; + // chain to any existing interpositions + for (std::vector::iterator it=fgInterposingTuples.begin(); it != fgInterposingTuples.end(); it++) { + if ( (it->replacee == tuple.replacee) && (it->onlyImage == this) ) { + tuple.replacee = it->replacement; + } + } + ImageLoader::fgInterposingTuples.push_back(tuple); + } +} + + +void ImageLoader::link(const LinkContext& context, bool forceLazysBound, bool preflightOnly, bool neverUnload, const RPathChain& loaderRPaths, const char* imagePath) +{ + //dyld::log("ImageLoader::link(%s) refCount=%d, neverUnload=%d\n", imagePath, fDlopenReferenceCount, fNeverUnload); + + // clear error strings + (*context.setErrorStrings)(0, NULL, NULL, NULL); + + uint64_t t0 = mach_absolute_time(); + this->recursiveLoadLibraries(context, preflightOnly, loaderRPaths, imagePath); + context.notifyBatch(dyld_image_state_dependents_mapped, preflightOnly); + + // we only do the loading step for preflights + if ( preflightOnly ) + return; + + uint64_t t1 = mach_absolute_time(); + context.clearAllDepths(); + this->recursiveUpdateDepth(context.imageCount()); + + uint64_t t2 = mach_absolute_time(); + this->recursiveRebase(context); + context.notifyBatch(dyld_image_state_rebased, false); + + uint64_t t3 = mach_absolute_time(); + this->recursiveBind(context, forceLazysBound, neverUnload); + + uint64_t t4 = mach_absolute_time(); + if ( !context.linkingMainExecutable ) + this->weakBind(context); + uint64_t t5 = mach_absolute_time(); + + context.notifyBatch(dyld_image_state_bound, false); + uint64_t t6 = mach_absolute_time(); + + std::vector dofs; + this->recursiveGetDOFSections(context, dofs); + context.registerDOFs(dofs); + uint64_t t7 = mach_absolute_time(); + + // interpose any dynamically loaded images + if ( !context.linkingMainExecutable && (fgInterposingTuples.size() != 0) ) { + this->recursiveApplyInterposing(context); + } + + // clear error strings + (*context.setErrorStrings)(0, NULL, NULL, NULL); + + fgTotalLoadLibrariesTime += t1 - t0; + fgTotalRebaseTime += t3 - t2; + fgTotalBindTime += t4 - t3; + fgTotalWeakBindTime += t5 - t4; + fgTotalDOF += t7 - t6; + + // done with initial dylib loads + fgNextPIEDylibAddress = 0; +} + + +void ImageLoader::printReferenceCounts() +{ + dyld::log(" dlopen=%d for %s\n", fDlopenReferenceCount, getPath() ); +} + + +bool ImageLoader::decrementDlopenReferenceCount() +{ + if ( fDlopenReferenceCount == 0 ) + return true; + --fDlopenReferenceCount; + return false; +} + + +// upward dylib initializers can be run too soon +// To handle dangling dylibs which are upward linked but not downward, all upward linked dylibs +// have their initialization postponed until after the recursion through downward dylibs +// has completed. +void ImageLoader::processInitializers(const LinkContext& context, mach_port_t thisThread, + InitializerTimingList& timingInfo, ImageLoader::UninitedUpwards& images) +{ + uint32_t maxImageCount = context.imageCount()+2; + ImageLoader::UninitedUpwards upsBuffer[maxImageCount]; + ImageLoader::UninitedUpwards& ups = upsBuffer[0]; + ups.count = 0; + // Calling recursive init on all images in images list, building a new list of + // uninitialized upward dependencies. + for (uintptr_t i=0; i < images.count; ++i) { + images.images[i]->recursiveInitialization(context, thisThread, images.images[i]->getPath(), timingInfo, ups); + } + // If any upward dependencies remain, init them. + if ( ups.count > 0 ) + processInitializers(context, thisThread, timingInfo, ups); +} + + +void ImageLoader::runInitializers(const LinkContext& context, InitializerTimingList& timingInfo) +{ + uint64_t t1 = mach_absolute_time(); + mach_port_t thisThread = mach_thread_self(); + ImageLoader::UninitedUpwards up; + up.count = 1; + up.images[0] = this; + processInitializers(context, thisThread, timingInfo, up); + context.notifyBatch(dyld_image_state_initialized, false); + mach_port_deallocate(mach_task_self(), thisThread); + uint64_t t2 = mach_absolute_time(); + fgTotalInitTime += (t2 - t1); +} + + +void ImageLoader::bindAllLazyPointers(const LinkContext& context, bool recursive) +{ + if ( ! fAllLazyPointersBound ) { + fAllLazyPointersBound = true; + + if ( recursive ) { + // bind lower level libraries first + for(unsigned int i=0; i < libraryCount(); ++i) { + ImageLoader* dependentImage = libImage(i); + if ( dependentImage != NULL ) + dependentImage->bindAllLazyPointers(context, recursive); + } + } + // bind lazies in this image + this->doBindJustLazies(context); + } +} + + +bool ImageLoader::allDependentLibrariesAsWhenPreBound() const +{ + return fAllLibraryChecksumsAndLoadAddressesMatch; +} + + +void ImageLoader::markedUsedRecursive(const std::vector& dynamicReferences) +{ + // already visited here + if ( fMarkedInUse ) + return; + fMarkedInUse = true; + + // clear mark on all statically dependent dylibs + for(unsigned int i=0; i < libraryCount(); ++i) { + ImageLoader* dependentImage = libImage(i); + if ( dependentImage != NULL ) { + dependentImage->markedUsedRecursive(dynamicReferences); + } + } + + // clear mark on all dynamically dependent dylibs + for (std::vector::const_iterator it=dynamicReferences.begin(); it != dynamicReferences.end(); ++it) { + if ( it->from == this ) + it->to->markedUsedRecursive(dynamicReferences); + } + +} + +unsigned int ImageLoader::recursiveUpdateDepth(unsigned int maxDepth) +{ + // the purpose of this phase is to make the images sortable such that + // in a sort list of images, every image that an image depends on + // occurs in the list before it. + if ( fDepth == 0 ) { + // break cycles + fDepth = maxDepth; + + // get depth of dependents + unsigned int minDependentDepth = maxDepth; + for(unsigned int i=0; i < libraryCount(); ++i) { + ImageLoader* dependentImage = libImage(i); + if ( (dependentImage != NULL) && !libIsUpward(i) ) { + unsigned int d = dependentImage->recursiveUpdateDepth(maxDepth); + if ( d < minDependentDepth ) + minDependentDepth = d; + } + } + + // make me less deep then all my dependents + fDepth = minDependentDepth - 1; + } + + return fDepth; +} + + +void ImageLoader::recursiveLoadLibraries(const LinkContext& context, bool preflightOnly, const RPathChain& loaderRPaths, const char* loadPath) +{ + if ( fState < dyld_image_state_dependents_mapped ) { + // break cycles + fState = dyld_image_state_dependents_mapped; + + // get list of libraries this image needs + DependentLibraryInfo libraryInfos[fLibraryCount]; + this->doGetDependentLibraries(libraryInfos); + + // get list of rpaths that this image adds + std::vector rpathsFromThisImage; + this->getRPaths(context, rpathsFromThisImage); + const RPathChain thisRPaths(&loaderRPaths, &rpathsFromThisImage); + + // try to load each + bool canUsePrelinkingInfo = true; + for(unsigned int i=0; i < fLibraryCount; ++i){ + ImageLoader* dependentLib; + bool depLibReExported = false; + bool depLibRequired = false; + bool depLibCheckSumsMatch = false; + DependentLibraryInfo& requiredLibInfo = libraryInfos[i]; +#if DYLD_SHARED_CACHE_SUPPORT + if ( preflightOnly && context.inSharedCache(requiredLibInfo.name) ) { + // dlopen_preflight() on image in shared cache leaves it loaded but not objc initialized + // in preflight mode, don't even load dylib that are in the shared cache because they will never be unloaded + setLibImage(i, NULL, false, false); + continue; + } +#endif + try { + unsigned cacheIndex; + dependentLib = context.loadLibrary(requiredLibInfo.name, true, this->getPath(), &thisRPaths, cacheIndex); + if ( dependentLib == this ) { + // found circular reference, perhaps DYLD_LIBARY_PATH is causing this rdar://problem/3684168 + dependentLib = context.loadLibrary(requiredLibInfo.name, false, NULL, NULL, cacheIndex); + if ( dependentLib != this ) + dyld::warn("DYLD_ setting caused circular dependency in %s\n", this->getPath()); + } + if ( fNeverUnload ) + dependentLib->setNeverUnload(); + if ( requiredLibInfo.upward ) { + } + else { + dependentLib->fIsReferencedDownward = true; + } + LibraryInfo actualInfo = dependentLib->doGetLibraryInfo(requiredLibInfo.info); + depLibRequired = requiredLibInfo.required; + depLibCheckSumsMatch = ( actualInfo.checksum == requiredLibInfo.info.checksum ); + depLibReExported = requiredLibInfo.reExported; + if ( ! depLibReExported ) { + // for pre-10.5 binaries that did not use LC_REEXPORT_DYLIB + depLibReExported = dependentLib->isSubframeworkOf(context, this) || this->hasSubLibrary(context, dependentLib); + } + // check found library version is compatible + // 0xFFFFFFFF is wildcard that matches any version + if ( (requiredLibInfo.info.minVersion != 0xFFFFFFFF) && (actualInfo.minVersion < requiredLibInfo.info.minVersion) ) { + // record values for possible use by CrashReporter or Finder + dyld::throwf("Incompatible library version: %s requires version %d.%d.%d or later, but %s provides version %d.%d.%d", + this->getShortName(), requiredLibInfo.info.minVersion >> 16, (requiredLibInfo.info.minVersion >> 8) & 0xff, requiredLibInfo.info.minVersion & 0xff, + dependentLib->getShortName(), actualInfo.minVersion >> 16, (actualInfo.minVersion >> 8) & 0xff, actualInfo.minVersion & 0xff); + } + // prebinding for this image disabled if any dependent library changed + //if ( !depLibCheckSumsMatch ) + // canUsePrelinkingInfo = false; + // prebinding for this image disabled unless both this and dependent are in the shared cache + if ( !dependentLib->inSharedCache() || !this->inSharedCache() ) + canUsePrelinkingInfo = false; + + //if ( context.verbosePrebinding ) { + // if ( !requiredLib.checksumMatches ) + // fprintf(stderr, "dyld: checksum mismatch, (%u v %u) for %s referencing %s\n", + // requiredLibInfo.info.checksum, actualInfo.checksum, this->getPath(), dependentLib->getPath()); + // if ( dependentLib->getSlide() != 0 ) + // fprintf(stderr, "dyld: dependent library slid for %s referencing %s\n", this->getPath(), dependentLib->getPath()); + //} + } + catch (const char* msg) { + //if ( context.verbosePrebinding ) + // fprintf(stderr, "dyld: exception during processing for %s referencing %s\n", this->getPath(), dependentLib->getPath()); + if ( requiredLibInfo.required ) { + fState = dyld_image_state_mapped; + // record values for possible use by CrashReporter or Finder + if ( strstr(msg, "Incompatible library version") != NULL ) + (*context.setErrorStrings)(DYLD_EXIT_REASON_DYLIB_WRONG_VERSION, this->getPath(), requiredLibInfo.name, NULL); + else if ( strstr(msg, "architecture") != NULL ) + (*context.setErrorStrings)(DYLD_EXIT_REASON_DYLIB_WRONG_ARCH, this->getPath(), requiredLibInfo.name, NULL); + else if ( strstr(msg, "file system sandbox") != NULL ) + (*context.setErrorStrings)(DYLD_EXIT_REASON_FILE_SYSTEM_SANDBOX, this->getPath(), requiredLibInfo.name, NULL); + else if ( strstr(msg, "code signature") != NULL ) + (*context.setErrorStrings)(DYLD_EXIT_REASON_CODE_SIGNATURE, this->getPath(), requiredLibInfo.name, NULL); + else if ( strstr(msg, "malformed") != NULL ) + (*context.setErrorStrings)(DYLD_EXIT_REASON_MALFORMED_MACHO, this->getPath(), requiredLibInfo.name, NULL); + else + (*context.setErrorStrings)(DYLD_EXIT_REASON_DYLIB_MISSING, this->getPath(), requiredLibInfo.name, NULL); + const char* newMsg = dyld::mkstringf("Library not loaded: %s\n Referenced from: %s\n Reason: %s", requiredLibInfo.name, this->getRealPath(), msg); + free((void*)msg); // our free() will do nothing if msg is a string literal + throw newMsg; + } + free((void*)msg); // our free() will do nothing if msg is a string literal + // ok if weak library not found + dependentLib = NULL; + canUsePrelinkingInfo = false; // this disables all prebinding, we may want to just slam import vectors for this lib to zero + } + setLibImage(i, dependentLib, depLibReExported, requiredLibInfo.upward); + } + fAllLibraryChecksumsAndLoadAddressesMatch = canUsePrelinkingInfo; + + // tell each to load its dependents + for(unsigned int i=0; i < libraryCount(); ++i) { + ImageLoader* dependentImage = libImage(i); + if ( dependentImage != NULL ) { + dependentImage->recursiveLoadLibraries(context, preflightOnly, thisRPaths, libraryInfos[i].name); + } + } + + // do deep prebind check + if ( fAllLibraryChecksumsAndLoadAddressesMatch ) { + for(unsigned int i=0; i < libraryCount(); ++i){ + ImageLoader* dependentImage = libImage(i); + if ( dependentImage != NULL ) { + if ( !dependentImage->allDependentLibrariesAsWhenPreBound() ) + fAllLibraryChecksumsAndLoadAddressesMatch = false; + } + } + } + + // free rpaths (getRPaths() malloc'ed each string) + for(std::vector::iterator it=rpathsFromThisImage.begin(); it != rpathsFromThisImage.end(); ++it) { + const char* str = *it; + free((void*)str); + } + + } +} + +void ImageLoader::recursiveRebase(const LinkContext& context) +{ + if ( fState < dyld_image_state_rebased ) { + // break cycles + fState = dyld_image_state_rebased; + + try { + // rebase lower level libraries first + for(unsigned int i=0; i < libraryCount(); ++i) { + ImageLoader* dependentImage = libImage(i); + if ( dependentImage != NULL ) + dependentImage->recursiveRebase(context); + } + + // rebase this image + doRebase(context); + + // notify + context.notifySingle(dyld_image_state_rebased, this, NULL); + } + catch (const char* msg) { + // this image is not rebased + fState = dyld_image_state_dependents_mapped; + CRSetCrashLogMessage2(NULL); + throw; + } + } +} + +void ImageLoader::recursiveApplyInterposing(const LinkContext& context) +{ + if ( ! fInterposed ) { + // break cycles + fInterposed = true; + + try { + // interpose lower level libraries first + for(unsigned int i=0; i < libraryCount(); ++i) { + ImageLoader* dependentImage = libImage(i); + if ( dependentImage != NULL ) + dependentImage->recursiveApplyInterposing(context); + } + + // interpose this image + doInterpose(context); + } + catch (const char* msg) { + // this image is not interposed + fInterposed = false; + throw; + } + } +} + + + +void ImageLoader::recursiveBind(const LinkContext& context, bool forceLazysBound, bool neverUnload) +{ + // Normally just non-lazy pointers are bound immediately. + // The exceptions are: + // 1) DYLD_BIND_AT_LAUNCH will cause lazy pointers to be bound immediately + // 2) some API's (e.g. RTLD_NOW) can cause lazy pointers to be bound immediately + if ( fState < dyld_image_state_bound ) { + // break cycles + fState = dyld_image_state_bound; + + try { + // bind lower level libraries first + for(unsigned int i=0; i < libraryCount(); ++i) { + ImageLoader* dependentImage = libImage(i); + if ( dependentImage != NULL ) + dependentImage->recursiveBind(context, forceLazysBound, neverUnload); + } + // bind this image + this->doBind(context, forceLazysBound); + // mark if lazys are also bound + if ( forceLazysBound || this->usablePrebinding(context) ) + fAllLazyPointersBound = true; + // mark as never-unload if requested + if ( neverUnload ) + this->setNeverUnload(); + + context.notifySingle(dyld_image_state_bound, this, NULL); + } + catch (const char* msg) { + // restore state + fState = dyld_image_state_rebased; + CRSetCrashLogMessage2(NULL); + throw; + } + } +} + +void ImageLoader::weakBind(const LinkContext& context) +{ + if ( context.verboseWeakBind ) + dyld::log("dyld: weak bind start:\n"); + uint64_t t1 = mach_absolute_time(); + // get set of ImageLoaders that participate in coalecsing + ImageLoader* imagesNeedingCoalescing[fgImagesRequiringCoalescing]; + unsigned imageIndexes[fgImagesRequiringCoalescing]; + int count = context.getCoalescedImages(imagesNeedingCoalescing, imageIndexes); + + // count how many have not already had weakbinding done + int countNotYetWeakBound = 0; + int countOfImagesWithWeakDefinitionsNotInSharedCache = 0; + for(int i=0; i < count; ++i) { + if ( ! imagesNeedingCoalescing[i]->weakSymbolsBound(imageIndexes[i]) ) + ++countNotYetWeakBound; + if ( ! imagesNeedingCoalescing[i]->inSharedCache() ) + ++countOfImagesWithWeakDefinitionsNotInSharedCache; + } + + // don't need to do any coalescing if only one image has overrides, or all have already been done + if ( (countOfImagesWithWeakDefinitionsNotInSharedCache > 0) && (countNotYetWeakBound > 0) ) { + // make symbol iterators for each + ImageLoader::CoalIterator iterators[count]; + ImageLoader::CoalIterator* sortedIts[count]; + for(int i=0; i < count; ++i) { + imagesNeedingCoalescing[i]->initializeCoalIterator(iterators[i], i, imageIndexes[i]); + sortedIts[i] = &iterators[i]; + if ( context.verboseWeakBind ) + dyld::log("dyld: weak bind load order %d/%d for %s\n", i, count, imagesNeedingCoalescing[i]->getIndexedPath(imageIndexes[i])); + } + + // walk all symbols keeping iterators in sync by + // only ever incrementing the iterator with the lowest symbol + int doneCount = 0; + while ( doneCount != count ) { + //for(int i=0; i < count; ++i) + // dyld::log("sym[%d]=%s ", sortedIts[i]->loadOrder, sortedIts[i]->symbolName); + //dyld::log("\n"); + // increment iterator with lowest symbol + if ( sortedIts[0]->image->incrementCoalIterator(*sortedIts[0]) ) + ++doneCount; + // re-sort iterators + for(int i=1; i < count; ++i) { + int result = strcmp(sortedIts[i-1]->symbolName, sortedIts[i]->symbolName); + if ( result == 0 ) + sortedIts[i-1]->symbolMatches = true; + if ( result > 0 ) { + // new one is bigger then next, so swap + ImageLoader::CoalIterator* temp = sortedIts[i-1]; + sortedIts[i-1] = sortedIts[i]; + sortedIts[i] = temp; + } + if ( result < 0 ) + break; + } + // process all matching symbols just before incrementing the lowest one that matches + if ( sortedIts[0]->symbolMatches && !sortedIts[0]->done ) { + const char* nameToCoalesce = sortedIts[0]->symbolName; + // pick first symbol in load order (and non-weak overrides weak) + uintptr_t targetAddr = 0; + ImageLoader* targetImage = NULL; + unsigned targetImageIndex = 0; + for(int i=0; i < count; ++i) { + if ( strcmp(iterators[i].symbolName, nameToCoalesce) == 0 ) { + if ( context.verboseWeakBind ) + dyld::log("dyld: weak bind, found %s weak=%d in %s \n", nameToCoalesce, iterators[i].weakSymbol, iterators[i].image->getIndexedPath((unsigned)iterators[i].imageIndex)); + if ( iterators[i].weakSymbol ) { + if ( targetAddr == 0 ) { + targetAddr = iterators[i].image->getAddressCoalIterator(iterators[i], context); + if ( targetAddr != 0 ) { + targetImage = iterators[i].image; + targetImageIndex = (unsigned)iterators[i].imageIndex; + } + } + } + else { + targetAddr = iterators[i].image->getAddressCoalIterator(iterators[i], context); + if ( targetAddr != 0 ) { + targetImage = iterators[i].image; + targetImageIndex = (unsigned)iterators[i].imageIndex; + // strong implementation found, stop searching + break; + } + } + } + } + // tell each to bind to this symbol (unless already bound) + if ( targetAddr != 0 ) { + if ( context.verboseWeakBind ) { + dyld::log("dyld: weak binding all uses of %s to copy from %s\n", + nameToCoalesce, targetImage->getIndexedShortName(targetImageIndex)); + } + for(int i=0; i < count; ++i) { + if ( strcmp(iterators[i].symbolName, nameToCoalesce) == 0 ) { + if ( context.verboseWeakBind ) { + dyld::log("dyld: weak bind, setting all uses of %s in %s to 0x%lX from %s\n", + nameToCoalesce, iterators[i].image->getIndexedShortName((unsigned)iterators[i].imageIndex), + targetAddr, targetImage->getIndexedShortName(targetImageIndex)); + } + if ( ! iterators[i].image->weakSymbolsBound(imageIndexes[i]) ) + iterators[i].image->updateUsesCoalIterator(iterators[i], targetAddr, targetImage, targetImageIndex, context); + iterators[i].symbolMatches = false; + } + } + } + + } + } + + // mark all as having all weak symbols bound + for(int i=0; i < count; ++i) { + imagesNeedingCoalescing[i]->setWeakSymbolsBound(imageIndexes[i]); + } + } + uint64_t t2 = mach_absolute_time(); + fgTotalWeakBindTime += t2 - t1; + + if ( context.verboseWeakBind ) + dyld::log("dyld: weak bind end\n"); +} + + + +void ImageLoader::recursiveGetDOFSections(const LinkContext& context, std::vector& dofs) +{ + if ( ! fRegisteredDOF ) { + // break cycles + fRegisteredDOF = true; + + // gather lower level libraries first + for(unsigned int i=0; i < libraryCount(); ++i) { + ImageLoader* dependentImage = libImage(i); + if ( dependentImage != NULL ) + dependentImage->recursiveGetDOFSections(context, dofs); + } + this->doGetDOFSections(context, dofs); + } +} + +void ImageLoader::setNeverUnloadRecursive() { + if ( ! fNeverUnload ) { + // break cycles + fNeverUnload = true; + + // gather lower level libraries first + for(unsigned int i=0; i < libraryCount(); ++i) { + ImageLoader* dependentImage = libImage(i); + if ( dependentImage != NULL ) + dependentImage->setNeverUnloadRecursive(); + } + } +} + +void ImageLoader::recursiveSpinLock(recursive_lock& rlock) +{ + // try to set image's ivar fInitializerRecursiveLock to point to this lock_info + // keep trying until success (spin) + while ( ! OSAtomicCompareAndSwapPtrBarrier(NULL, &rlock, (void**)&fInitializerRecursiveLock) ) { + // if fInitializerRecursiveLock already points to a different lock_info, if it is for + // the same thread we are on, the increment the lock count, otherwise continue to spin + if ( (fInitializerRecursiveLock != NULL) && (fInitializerRecursiveLock->thread == rlock.thread) ) + break; + } + ++(fInitializerRecursiveLock->count); +} + +void ImageLoader::recursiveSpinUnLock() +{ + if ( --(fInitializerRecursiveLock->count) == 0 ) + fInitializerRecursiveLock = NULL; +} + +void ImageLoader::InitializerTimingList::addTime(const char* name, uint64_t time) +{ + for (int i=0; i < count; ++i) { + if ( strcmp(images[i].shortName, name) == 0 ) { + images[i].initTime += time; + return; + } + } + images[count].initTime = time; + images[count].shortName = name; + ++count; +} + +void ImageLoader::recursiveInitialization(const LinkContext& context, mach_port_t this_thread, const char* pathToInitialize, + InitializerTimingList& timingInfo, UninitedUpwards& uninitUps) +{ + recursive_lock lock_info(this_thread); + recursiveSpinLock(lock_info); + + if ( fState < dyld_image_state_dependents_initialized-1 ) { + uint8_t oldState = fState; + // break cycles + fState = dyld_image_state_dependents_initialized-1; + try { + // initialize lower level libraries first + for(unsigned int i=0; i < libraryCount(); ++i) { + ImageLoader* dependentImage = libImage(i); + if ( dependentImage != NULL ) { + // don't try to initialize stuff "above" me yet + if ( libIsUpward(i) ) { + uninitUps.images[uninitUps.count] = dependentImage; + uninitUps.count++; + } + else if ( dependentImage->fDepth >= fDepth ) { + dependentImage->recursiveInitialization(context, this_thread, libPath(i), timingInfo, uninitUps); + } + } + } + + // record termination order + if ( this->needsTermination() ) + context.terminationRecorder(this); + + // let objc know we are about to initialize this image + uint64_t t1 = mach_absolute_time(); + fState = dyld_image_state_dependents_initialized; + oldState = fState; + context.notifySingle(dyld_image_state_dependents_initialized, this, &timingInfo); + + // initialize this image + bool hasInitializers = this->doInitialization(context); + + // let anyone know we finished initializing this image + fState = dyld_image_state_initialized; + oldState = fState; + context.notifySingle(dyld_image_state_initialized, this, NULL); + + if ( hasInitializers ) { + uint64_t t2 = mach_absolute_time(); + timingInfo.addTime(this->getShortName(), t2-t1); + } + } + catch (const char* msg) { + // this image is not initialized + fState = oldState; + recursiveSpinUnLock(); + throw; + } + } + + recursiveSpinUnLock(); +} + + +static void printTime(const char* msg, uint64_t partTime, uint64_t totalTime) +{ + static uint64_t sUnitsPerSecond = 0; + if ( sUnitsPerSecond == 0 ) { + struct mach_timebase_info timeBaseInfo; + if ( mach_timebase_info(&timeBaseInfo) == KERN_SUCCESS ) { + sUnitsPerSecond = 1000000000ULL * timeBaseInfo.denom / timeBaseInfo.numer; + } + } + if ( partTime < sUnitsPerSecond ) { + uint32_t milliSecondsTimesHundred = (uint32_t)((partTime*100000)/sUnitsPerSecond); + uint32_t milliSeconds = (uint32_t)(milliSecondsTimesHundred/100); + uint32_t percentTimesTen = (uint32_t)((partTime*1000)/totalTime); + uint32_t percent = percentTimesTen/10; + if ( milliSeconds >= 100 ) + dyld::log("%s: %u.%02u milliseconds (%u.%u%%)\n", msg, milliSeconds, milliSecondsTimesHundred-milliSeconds*100, percent, percentTimesTen-percent*10); + else if ( milliSeconds >= 10 ) + dyld::log("%s: %u.%02u milliseconds (%u.%u%%)\n", msg, milliSeconds, milliSecondsTimesHundred-milliSeconds*100, percent, percentTimesTen-percent*10); + else + dyld::log("%s: %u.%02u milliseconds (%u.%u%%)\n", msg, milliSeconds, milliSecondsTimesHundred-milliSeconds*100, percent, percentTimesTen-percent*10); + } + else { + uint32_t secondsTimeTen = (uint32_t)((partTime*10)/sUnitsPerSecond); + uint32_t seconds = secondsTimeTen/10; + uint32_t percentTimesTen = (uint32_t)((partTime*1000)/totalTime); + uint32_t percent = percentTimesTen/10; + dyld::log("%s: %u.%u seconds (%u.%u%%)\n", msg, seconds, secondsTimeTen-seconds*10, percent, percentTimesTen-percent*10); + } +} + +static char* commatize(uint64_t in, char* out) +{ + uint64_t div10 = in / 10; + uint8_t delta = in - div10*10; + char* s = &out[32]; + int digitCount = 1; + *s = '\0'; + *(--s) = '0' + delta; + in = div10; + while ( in != 0 ) { + if ( (digitCount % 3) == 0 ) + *(--s) = ','; + div10 = in / 10; + delta = in - div10*10; + *(--s) = '0' + delta; + in = div10; + ++digitCount; + } + return s; +} + + +void ImageLoader::printStatistics(unsigned int imageCount, const InitializerTimingList& timingInfo) +{ + uint64_t totalTime = fgTotalLoadLibrariesTime + fgTotalRebaseTime + fgTotalBindTime + fgTotalWeakBindTime + fgTotalDOF + fgTotalInitTime; + + uint64_t totalDyldTime = totalTime - fgTotalDebuggerPausedTime - fgTotalRebindCacheTime; + printTime("Total pre-main time", totalDyldTime, totalDyldTime); + printTime(" dylib loading time", fgTotalLoadLibrariesTime-fgTotalDebuggerPausedTime, totalDyldTime); + printTime(" rebase/binding time", fgTotalRebaseTime+fgTotalBindTime+fgTotalWeakBindTime-fgTotalRebindCacheTime, totalDyldTime); + printTime(" ObjC setup time", fgTotalObjCSetupTime, totalDyldTime); + printTime(" initializer time", fgTotalInitTime-fgTotalObjCSetupTime, totalDyldTime); + dyld::log(" slowest intializers :\n"); + for (uintptr_t i=0; i < timingInfo.count; ++i) { + uint64_t t = timingInfo.images[i].initTime; + if ( t*50 < totalDyldTime ) + continue; + dyld::log("%30s ", timingInfo.images[i].shortName); + if ( strncmp(timingInfo.images[i].shortName, "libSystem.", 10) == 0 ) + t -= fgTotalObjCSetupTime; + printTime("", t, totalDyldTime); + } + dyld::log("\n"); +} + +void ImageLoader::printStatisticsDetails(unsigned int imageCount, const InitializerTimingList& timingInfo) +{ + uint64_t totalTime = fgTotalLoadLibrariesTime + fgTotalRebaseTime + fgTotalBindTime + fgTotalWeakBindTime + fgTotalDOF + fgTotalInitTime; + char commaNum1[40]; + char commaNum2[40]; + + printTime(" total time", totalTime, totalTime); + dyld::log(" total images loaded: %d (%u from dyld shared cache)\n", imageCount, fgImagesUsedFromSharedCache); + dyld::log(" total segments mapped: %u, into %llu pages with %llu pages pre-fetched\n", fgTotalSegmentsMapped, fgTotalBytesMapped/4096, fgTotalBytesPreFetched/4096); + printTime(" total images loading time", fgTotalLoadLibrariesTime, totalTime); + printTime(" total load time in ObjC", fgTotalObjCSetupTime, totalTime); + printTime(" total debugger pause time", fgTotalDebuggerPausedTime, totalTime); + printTime(" total dtrace DOF registration time", fgTotalDOF, totalTime); + dyld::log(" total rebase fixups: %s\n", commatize(fgTotalRebaseFixups, commaNum1)); + printTime(" total rebase fixups time", fgTotalRebaseTime, totalTime); + dyld::log(" total binding fixups: %s\n", commatize(fgTotalBindFixups, commaNum1)); + if ( fgTotalBindSymbolsResolved != 0 ) { + uint32_t avgTimesTen = (fgTotalBindImageSearches * 10) / fgTotalBindSymbolsResolved; + uint32_t avgInt = fgTotalBindImageSearches / fgTotalBindSymbolsResolved; + uint32_t avgTenths = avgTimesTen - (avgInt*10); + dyld::log("total binding symbol lookups: %s, average images searched per symbol: %u.%u\n", + commatize(fgTotalBindSymbolsResolved, commaNum1), avgInt, avgTenths); + } + printTime(" total binding fixups time", fgTotalBindTime, totalTime); + printTime(" total weak binding fixups time", fgTotalWeakBindTime, totalTime); + printTime(" total redo shared cached bindings time", fgTotalRebindCacheTime, totalTime); + dyld::log(" total bindings lazily fixed up: %s of %s\n", commatize(fgTotalLazyBindFixups, commaNum1), commatize(fgTotalPossibleLazyBindFixups, commaNum2)); + printTime(" total time in initializers and ObjC +load", fgTotalInitTime-fgTotalObjCSetupTime, totalTime); + for (uintptr_t i=0; i < timingInfo.count; ++i) { + uint64_t t = timingInfo.images[i].initTime; + if ( t*1000 < totalTime ) + continue; + dyld::log("%42s ", timingInfo.images[i].shortName); + if ( strncmp(timingInfo.images[i].shortName, "libSystem.", 10) == 0 ) + t -= fgTotalObjCSetupTime; + printTime("", t, totalTime); + } + +} + + +// +// copy path and add suffix to result +// +// /path/foo.dylib _debug => /path/foo_debug.dylib +// foo.dylib _debug => foo_debug.dylib +// foo _debug => foo_debug +// /path/bar _debug => /path/bar_debug +// /path/bar.A.dylib _debug => /path/bar.A_debug.dylib +// +void ImageLoader::addSuffix(const char* path, const char* suffix, char* result) +{ + strcpy(result, path); + + char* start = strrchr(result, '/'); + if ( start != NULL ) + start++; + else + start = result; + + char* dot = strrchr(start, '.'); + if ( dot != NULL ) { + strcpy(dot, suffix); + strcat(&dot[strlen(suffix)], &path[dot-result]); + } + else { + strcat(result, suffix); + } +} + + +// +// This function is the hotspot of symbol lookup. It was pulled out of findExportedSymbol() +// to enable it to be re-written in assembler if needed. +// +const uint8_t* ImageLoader::trieWalk(const uint8_t* start, const uint8_t* end, const char* s) +{ + //dyld::log("trieWalk(%p, %p, %s)\n", start, end, s); + ++fgSymbolTrieSearchs; + const uint8_t* p = start; + while ( p != NULL ) { + uintptr_t terminalSize = *p++; + if ( terminalSize > 127 ) { + // except for re-export-with-rename, all terminal sizes fit in one byte + --p; + terminalSize = read_uleb128(p, end); + } + if ( (*s == '\0') && (terminalSize != 0) ) { + //dyld::log("trieWalk(%p) returning %p\n", start, p); + return p; + } + const uint8_t* children = p + terminalSize; + if ( children > end ) { + dyld::log("trieWalk() malformed trie node, terminalSize=0x%lx extends past end of trie\n", terminalSize); + return NULL; + } + //dyld::log("trieWalk(%p) sym=%s, terminalSize=%lu, children=%p\n", start, s, terminalSize, children); + uint8_t childrenRemaining = *children++; + p = children; + uintptr_t nodeOffset = 0; + for (; childrenRemaining > 0; --childrenRemaining) { + const char* ss = s; + //dyld::log("trieWalk(%p) child str=%s\n", start, (char*)p); + bool wrongEdge = false; + // scan whole edge to get to next edge + // if edge is longer than target symbol name, don't read past end of symbol name + char c = *p; + while ( c != '\0' ) { + if ( !wrongEdge ) { + if ( c != *ss ) + wrongEdge = true; + ++ss; + } + ++p; + c = *p; + } + if ( wrongEdge ) { + // advance to next child + ++p; // skip over zero terminator + // skip over uleb128 until last byte is found + while ( (*p & 0x80) != 0 ) + ++p; + ++p; // skip over last byte of uleb128 + if ( p > end ) { + dyld::log("trieWalk() malformed trie node, child node extends past end of trie\n"); + return NULL; + } + } + else { + // the symbol so far matches this edge (child) + // so advance to the child's node + ++p; + nodeOffset = read_uleb128(p, end); + if ( (nodeOffset == 0) || ( &start[nodeOffset] > end) ) { + dyld::log("trieWalk() malformed trie child, nodeOffset=0x%lx out of range\n", nodeOffset); + return NULL; + } + s = ss; + //dyld::log("trieWalk() found matching edge advancing to node 0x%lx\n", nodeOffset); + break; + } + } + if ( nodeOffset != 0 ) + p = &start[nodeOffset]; + else + p = NULL; + } + //dyld::log("trieWalk(%p) return NULL\n", start); + return NULL; +} + + + +uintptr_t ImageLoader::read_uleb128(const uint8_t*& p, const uint8_t* end) +{ + uint64_t result = 0; + int bit = 0; + do { + if (p == end) + dyld::throwf("malformed uleb128"); + + uint64_t slice = *p & 0x7f; + + if (bit > 63) + dyld::throwf("uleb128 too big for uint64, bit=%d, result=0x%0llX", bit, result); + else { + result |= (slice << bit); + bit += 7; + } + } while (*p++ & 0x80); + return result; +} + + +intptr_t ImageLoader::read_sleb128(const uint8_t*& p, const uint8_t* end) +{ + int64_t result = 0; + int bit = 0; + uint8_t byte; + do { + if (p == end) + throw "malformed sleb128"; + byte = *p++; + result |= (((int64_t)(byte & 0x7f)) << bit); + bit += 7; + } while (byte & 0x80); + // sign extend negative numbers + if ( (byte & 0x40) != 0 ) + result |= (-1LL) << bit; + return result; +} + + +VECTOR_NEVER_DESTRUCTED_IMPL(ImageLoader::InterposeTuple); +//VECTOR_NEVER_DESTRUCTED_IMPL(ImagePair); + + + diff --git a/src/dyld-apple/src/ImageLoader.h b/src/dyld-apple/src/ImageLoader.h new file mode 100644 index 000000000..334b9c7ec --- /dev/null +++ b/src/dyld-apple/src/ImageLoader.h @@ -0,0 +1,870 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2004-2010 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@ + */ + + +#ifndef __IMAGELOADER__ +#define __IMAGELOADER__ + +#include +#include +#include +#include // struct mach_timebase_info +#include // struct mach_thread_self +#include +#include +#include +#include +#include +#include +#include +#include + +#if __arm__ + #include +#endif + +#if __x86_64__ || __i386__ + #include +#else + // work around until iOS has CrashReporterClient.h + #define CRSetCrashLogMessage(x) + #define CRSetCrashLogMessage2(x) +#endif + +#ifndef SHARED_REGION_BASE_ARM64 + #define SHARED_REGION_BASE_ARM64 0x7FFF80000000LL +#endif + +#ifndef SHARED_REGION_SIZE_ARM64 + #define SHARED_REGION_SIZE_ARM64 0x10000000LL +#endif + + +#define LOG_BINDINGS 0 + +#include "mach-o/dyld_images.h" +#include "mach-o/dyld_priv.h" + +#if __i386__ + #define SHARED_REGION_BASE SHARED_REGION_BASE_I386 + #define SHARED_REGION_SIZE SHARED_REGION_SIZE_I386 +#elif __x86_64__ + #define SHARED_REGION_BASE SHARED_REGION_BASE_X86_64 + #define SHARED_REGION_SIZE SHARED_REGION_SIZE_X86_64 +#elif __arm__ + #define SHARED_REGION_BASE SHARED_REGION_BASE_ARM + #define SHARED_REGION_SIZE SHARED_REGION_SIZE_ARM +#elif __arm64__ + #define SHARED_REGION_BASE SHARED_REGION_BASE_ARM64 + #define SHARED_REGION_SIZE SHARED_REGION_SIZE_ARM64 +#endif + +#ifndef EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER + #define EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER 0x10 +#endif +#ifndef EXPORT_SYMBOL_FLAGS_REEXPORT + #define EXPORT_SYMBOL_FLAGS_REEXPORT 0x08 +#endif + +#ifndef LC_MAIN + #define LC_MAIN (0x28|LC_REQ_DYLD) /* replacement for LC_UNIXTHREAD */ + struct entry_point_command { + uint32_t cmd; /* LC_MAIN only used in MH_EXECUTE filetypes */ + uint32_t cmdsize; /* 24 */ + uint64_t entryoff; /* file (__TEXT) offset of main() */ + uint64_t stacksize;/* if not zero, initial stack size */ + }; +#endif + +#if __IPHONE_OS_VERSION_MIN_REQUIRED + #define SPLIT_SEG_SHARED_REGION_SUPPORT 0 + #define SPLIT_SEG_DYLIB_SUPPORT 0 + #define PREBOUND_IMAGE_SUPPORT __arm__ + #define TEXT_RELOC_SUPPORT __i386__ + #define DYLD_SHARED_CACHE_SUPPORT (__arm__ || __arm64__) + #define SUPPORT_OLD_CRT_INITIALIZATION 0 + #define SUPPORT_LC_DYLD_ENVIRONMENT 1 + #define SUPPORT_VERSIONED_PATHS 1 + #define SUPPORT_CLASSIC_MACHO __arm__ + #define SUPPORT_ZERO_COST_EXCEPTIONS (!__USING_SJLJ_EXCEPTIONS__) + #define INITIAL_IMAGE_COUNT 150 + #define SUPPORT_ACCELERATE_TABLES (__arm__ || __arm64__) + #define SUPPORT_ROOT_PATH TARGET_IPHONE_SIMULATOR +#else + #define SPLIT_SEG_SHARED_REGION_SUPPORT 0 + #define SPLIT_SEG_DYLIB_SUPPORT __i386__ + #define PREBOUND_IMAGE_SUPPORT __i386__ + #define TEXT_RELOC_SUPPORT __i386__ + #define DYLD_SHARED_CACHE_SUPPORT 1 + #define SUPPORT_OLD_CRT_INITIALIZATION __i386__ + #define SUPPORT_LC_DYLD_ENVIRONMENT (__i386__ || __x86_64__) + #define SUPPORT_VERSIONED_PATHS 1 + #define SUPPORT_CLASSIC_MACHO 1 + #define SUPPORT_ZERO_COST_EXCEPTIONS 1 + #define INITIAL_IMAGE_COUNT 200 + #define SUPPORT_ACCELERATE_TABLES 0 + #define SUPPORT_ROOT_PATH 1 +#endif + +#define MAX_MACH_O_HEADER_AND_LOAD_COMMANDS_SIZE (32*1024) + +#define MH_HAS_OBJC 0x40000000 + +// optimize away dyld's initializers +#define VECTOR_NEVER_DESTRUCTED(type) \ + namespace std { \ + template <> \ + __vector_base >::~__vector_base() { } \ + } +#define VECTOR_NEVER_DESTRUCTED_EXTERN(type) \ + namespace std { \ + template <> \ + __vector_base >::~__vector_base(); \ + } +#define VECTOR_NEVER_DESTRUCTED_IMPL(type) \ + namespace std { \ + template <> \ + __vector_base >::~__vector_base() { } \ + } + +// utilities +namespace dyld { + extern __attribute__((noreturn)) void throwf(const char* format, ...) __attribute__((format(printf, 1, 2))); + extern void log(const char* format, ...) __attribute__((format(printf, 1, 2))); + extern void warn(const char* format, ...) __attribute__((format(printf, 1, 2))); + extern const char* mkstringf(const char* format, ...) __attribute__((format(printf, 1, 2))); +#if LOG_BINDINGS + extern void logBindings(const char* format, ...) __attribute__((format(printf, 1, 2))); +#endif +} +extern "C" int vm_alloc(vm_address_t* addr, vm_size_t size, uint32_t flags); +extern "C" void* xmmap(void* addr, size_t len, int prot, int flags, int fd, off_t offset); + + +#if __LP64__ + struct macho_header : public mach_header_64 {}; + struct macho_nlist : public nlist_64 {}; +#else + struct macho_header : public mach_header {}; + struct macho_nlist : public nlist {}; +#endif + + +#if __arm64__ + #define dyld_page_trunc(__addr) (__addr & (-16384)) + #define dyld_page_round(__addr) ((__addr + 16383) & (-16384)) + #define dyld_page_size 16384 +#elif __arm__ + #define dyld_page_trunc(__addr) trunc_page_kernel(__addr) + #define dyld_page_round(__addr) round_page_kernel(__addr) + #define dyld_page_size vm_kernel_page_size +#else + #define dyld_page_trunc(__addr) (__addr & (-4096)) + #define dyld_page_round(__addr) ((__addr + 4095) & (-4096)) + #define dyld_page_size 4096 +#endif + + + +struct ProgramVars +{ + const void* mh; + int* NXArgcPtr; + const char*** NXArgvPtr; + const char*** environPtr; + const char** __prognamePtr; +}; + + + +// +// ImageLoader is an abstract base class. To support loading a particular executable +// file format, you make a concrete subclass of ImageLoader. +// +// For each executable file (dynamic shared object) in use, an ImageLoader is instantiated. +// +// The ImageLoader base class does the work of linking together images, but it knows nothing +// about any particular file format. +// +// +class ImageLoader { +public: + + typedef uint32_t DefinitionFlags; + static const DefinitionFlags kNoDefinitionOptions = 0; + static const DefinitionFlags kWeakDefinition = 1; + + typedef uint32_t ReferenceFlags; + static const ReferenceFlags kNoReferenceOptions = 0; + static const ReferenceFlags kWeakReference = 1; + static const ReferenceFlags kTentativeDefinition = 2; + + enum PrebindMode { kUseAllPrebinding, kUseSplitSegPrebinding, kUseAllButAppPredbinding, kUseNoPrebinding }; + enum BindingOptions { kBindingNone, kBindingLazyPointers, kBindingNeverSetLazyPointers }; + enum SharedRegionMode { kUseSharedRegion, kUsePrivateSharedRegion, kDontUseSharedRegion, kSharedRegionIsSharedCache }; + + struct Symbol; // abstact symbol + + struct MappedRegion { + uintptr_t address; + size_t size; + }; + + struct RPathChain { + RPathChain(const RPathChain* n, std::vector* p) : next(n), paths(p) {}; + const RPathChain* next; + std::vector* paths; + }; + + struct DOFInfo { + void* dof; + const mach_header* imageHeader; + const char* imageShortName; + }; + + struct DynamicReference { + ImageLoader* from; + ImageLoader* to; + }; + + struct InitializerTimingList + { + uintptr_t count; + struct { + const char* shortName; + uint64_t initTime; + } images[1]; + + void addTime(const char* name, uint64_t time); + }; + + struct LinkContext { + ImageLoader* (*loadLibrary)(const char* libraryName, bool search, const char* origin, const RPathChain* rpaths, unsigned& cacheIndex); + void (*terminationRecorder)(ImageLoader* image); + bool (*flatExportFinder)(const char* name, const Symbol** sym, const ImageLoader** image); + bool (*coalescedExportFinder)(const char* name, const Symbol** sym, const ImageLoader** image); + unsigned int (*getCoalescedImages)(ImageLoader* images[], unsigned imageIndex[]); + void (*undefinedHandler)(const char* name); + MappedRegion* (*getAllMappedRegions)(MappedRegion*); + void * (*bindingHandler)(const char *, const char *, void *); + void (*notifySingle)(dyld_image_states, const ImageLoader* image, InitializerTimingList*); + void (*notifyBatch)(dyld_image_states state, bool preflightOnly); + void (*removeImage)(ImageLoader* image); + void (*registerDOFs)(const std::vector& dofs); + void (*clearAllDepths)(); + void (*printAllDepths)(); + unsigned int (*imageCount)(); + void (*setNewProgramVars)(const ProgramVars&); + bool (*inSharedCache)(const char* path); + void (*setErrorStrings)(unsigned errorCode, const char* errorClientOfDylibPath, + const char* errorTargetDylibPath, const char* errorSymbol); + ImageLoader* (*findImageContainingAddress)(const void* addr); + void (*addDynamicReference)(ImageLoader* from, ImageLoader* to); +#if SUPPORT_ACCELERATE_TABLES + void (*notifySingleFromCache)(dyld_image_states, const mach_header* mh, const char* path); + dyld_image_state_change_handler (*getPreInitNotifyHandler)(unsigned index); + dyld_image_state_change_handler (*getBoundBatchHandler)(unsigned index); +#endif + +#if SUPPORT_OLD_CRT_INITIALIZATION + void (*setRunInitialzersOldWay)(); +#endif + BindingOptions bindingOptions; + int argc; + const char** argv; + const char** envp; + const char** apple; + const char* progname; + ProgramVars programVars; + ImageLoader* mainExecutable; + const char* imageSuffix; +#if SUPPORT_ROOT_PATH + const char** rootPaths; +#endif + const dyld_interpose_tuple* dynamicInterposeArray; + size_t dynamicInterposeCount; + PrebindMode prebindUsage; + SharedRegionMode sharedRegionMode; + bool dyldLoadedAtSameAddressNeededBySharedCache; + bool strictMachORequired; + bool requireCodeSignature; + bool mainExecutableCodeSigned; + bool preFetchDisabled; + bool prebinding; + bool bindFlat; + bool linkingMainExecutable; + bool startedInitializingMainExecutable; +#if __MAC_OS_X_VERSION_MIN_REQUIRED + bool processIsRestricted; + bool processUsingLibraryValidation; +#endif + bool verboseOpts; + bool verboseEnv; + bool verboseLoading; + bool verboseMapping; + bool verboseRebase; + bool verboseBind; + bool verboseWeakBind; + bool verboseInit; + bool verboseDOF; + bool verbosePrebinding; + bool verboseCoreSymbolication; + bool verboseWarnings; + bool verboseRPaths; + bool verboseInterposing; + bool verboseCodeSignatures; + }; + + struct CoalIterator + { + ImageLoader* image; + const char* symbolName; + unsigned int loadOrder; + bool weakSymbol; + bool symbolMatches; + bool done; + // the following are private to the ImageLoader subclass + uintptr_t curIndex; + uintptr_t endIndex; + uintptr_t address; + uintptr_t type; + uintptr_t addend; + uintptr_t imageIndex; + }; + + virtual void initializeCoalIterator(CoalIterator&, unsigned int loadOrder, unsigned imageIndex) = 0; + virtual bool incrementCoalIterator(CoalIterator&) = 0; + virtual uintptr_t getAddressCoalIterator(CoalIterator&, const LinkContext& context) = 0; + virtual void updateUsesCoalIterator(CoalIterator&, uintptr_t newAddr, ImageLoader* target, unsigned targetIndex, const LinkContext& context) = 0; + + struct UninitedUpwards + { + uintptr_t count; + ImageLoader* images[1]; + }; + + + // constructor is protected, but anyone can delete an image + virtual ~ImageLoader(); + + // link() takes a newly instantiated ImageLoader and does all + // fixups needed to make it usable by the process + void link(const LinkContext& context, bool forceLazysBound, bool preflight, bool neverUnload, const RPathChain& loaderRPaths, const char* imagePath); + + // runInitializers() is normally called in link() but the main executable must + // run crt code before initializers + void runInitializers(const LinkContext& context, InitializerTimingList& timingInfo); + + // called after link() forces all lazy pointers to be bound + void bindAllLazyPointers(const LinkContext& context, bool recursive); + + // used by dyld to see if a requested library is already loaded (might be symlink) + bool statMatch(const struct stat& stat_buf) const; + + // get short name of this image + const char* getShortName() const; + + // returns leaf name + static const char* shortName(const char* fullName); + + // get path used to load this image, not necessarily the "real" path + const char* getPath() const { return fPath; } + + uint32_t getPathHash() const { return fPathHash; } + + // get the "real" path for this image (e.g. no @rpath) + const char* getRealPath() const; + + // get path this image is intended to be placed on disk or NULL if no preferred install location + virtual const char* getInstallPath() const = 0; + + // image was loaded with NSADDIMAGE_OPTION_MATCH_FILENAME_BY_INSTALLNAME and all clients are looking for install path + bool matchInstallPath() const; + void setMatchInstallPath(bool); + + // mark that this image's exported symbols should be ignored when linking other images (e.g. RTLD_LOCAL) + void setHideExports(bool hide = true); + + // check if this image's exported symbols should be ignored when linking other images + bool hasHiddenExports() const; + + // checks if this image is already linked into the process + bool isLinked() const; + + // even if image is deleted, leave segments mapped in + void setLeaveMapped(); + + // even if image is deleted, leave segments mapped in + bool leaveMapped() { return fLeaveMapped; } + + // image resides in dyld shared cache + virtual bool inSharedCache() const = 0; + + // checks if the specifed address is within one of this image's segments + virtual bool containsAddress(const void* addr) const; + + // checks if the specifed symbol is within this image's symbol table + virtual bool containsSymbol(const void* addr) const = 0; + + // checks if the specifed address range overlaps any of this image's segments + virtual bool overlapsWithAddressRange(const void* start, const void* end) const; + + // adds to list of ranges of memory mapped in + void getMappedRegions(MappedRegion*& region) const; + + // st_mtime from stat() on file + time_t lastModified() const; + + // only valid for main executables, returns a pointer its entry point from LC_UNIXTHREAD + virtual void* getThreadPC() const = 0; + + // only valid for main executables, returns a pointer its main from LC_

getPath(), foundIn); + } + + // gets address of implementation (code) of the specified exported symbol + virtual uintptr_t getExportedSymbolAddress(const Symbol* sym, const LinkContext& context, + const ImageLoader* requestor=NULL, bool runResolver=false, const char* symbolName=NULL) const = 0; + + // gets attributes of the specified exported symbol + virtual DefinitionFlags getExportedSymbolInfo(const Symbol* sym) const = 0; + + // gets name of the specified exported symbol + virtual const char* getExportedSymbolName(const Symbol* sym) const = 0; + + // gets how many symbols are exported by this image + virtual uint32_t getExportedSymbolCount() const = 0; + + // gets the i'th exported symbol + virtual const Symbol* getIndexedExportedSymbol(uint32_t index) const = 0; + + // find exported symbol as if imported by this image + // used by RTLD_NEXT + virtual const Symbol* findExportedSymbolInDependentImages(const char* name, const LinkContext& context, const ImageLoader** foundIn) const; + + // find exported symbol as if imported by this image + // used by RTLD_SELF + virtual const Symbol* findExportedSymbolInImageOrDependentImages(const char* name, const LinkContext& context, const ImageLoader** foundIn) const; + + // gets how many symbols are imported by this image + virtual uint32_t getImportedSymbolCount() const = 0; + + // gets the i'th imported symbol + virtual const Symbol* getIndexedImportedSymbol(uint32_t index) const = 0; + + // gets attributes of the specified imported symbol + virtual ReferenceFlags getImportedSymbolInfo(const Symbol* sym) const = 0; + + // gets name of the specified imported symbol + virtual const char* getImportedSymbolName(const Symbol* sym) const = 0; + + // find the closest symbol before addr + virtual const char* findClosestSymbol(const void* addr, const void** closestAddr) const = 0; + + // for use with accelerator tables + virtual const char* getIndexedPath(unsigned) const { return getPath(); } + virtual const char* getIndexedShortName(unsigned) const { return getShortName(); } + + // checks if this image is a bundle and can be loaded but not linked + virtual bool isBundle() const = 0; + + // checks if this image is a dylib + virtual bool isDylib() const = 0; + + // checks if this image is a main executable + virtual bool isExecutable() const = 0; + + // checks if this image is a main executable + virtual bool isPositionIndependentExecutable() const = 0; + + // only for main executable + virtual bool forceFlat() const = 0; + + // called at runtime when a lazily bound function is first called + virtual uintptr_t doBindLazySymbol(uintptr_t* lazyPointer, const LinkContext& context) = 0; + + // called at runtime when a fast lazily bound function is first called + virtual uintptr_t doBindFastLazySymbol(uint32_t lazyBindingInfoOffset, const LinkContext& context, + void (*lock)(), void (*unlock)()) = 0; + + // calls termination routines (e.g. C++ static destructors for image) + virtual void doTermination(const LinkContext& context) = 0; + + // return if this image has initialization routines + virtual bool needsInitialization() = 0; + + // return if this image has specified section and set start and length + virtual bool getSectionContent(const char* segmentName, const char* sectionName, void** start, size_t* length) = 0; + + // fills in info about __eh_frame and __unwind_info sections + virtual void getUnwindInfo(dyld_unwind_sections* info) = 0; + + // given a pointer into an image, find which segment and section it is in + virtual bool findSection(const void* imageInterior, const char** segmentName, const char** sectionName, size_t* sectionOffset) = 0; + + // the image supports being prebound + virtual bool isPrebindable() const = 0; + + // the image is prebindable and its prebinding is valid + virtual bool usablePrebinding(const LinkContext& context) const = 0; + + // add all RPATH paths this image contains + virtual void getRPaths(const LinkContext& context, std::vector&) const = 0; + + // image has or uses weak definitions that need runtime coalescing + virtual bool participatesInCoalescing() const = 0; + + // if image has a UUID, copy into parameter and return true + virtual bool getUUID(uuid_t) const = 0; + + // dynamic interpose values onto this image + virtual void dynamicInterpose(const LinkContext& context) = 0; + + // record interposing for any late binding + void addDynamicInterposingTuples(const struct dyld_interpose_tuple array[], size_t count); + + virtual const char* libPath(unsigned int) const = 0; + + // Image has objc sections, so information objc about when it comes and goes + virtual bool notifyObjC() const { return false; } + +// +// A segment is a chunk of an executable file that is mapped into memory. +// + virtual unsigned int segmentCount() const = 0; + virtual const char* segName(unsigned int) const = 0; + virtual uintptr_t segSize(unsigned int) const = 0; + virtual uintptr_t segFileSize(unsigned int) const = 0; + virtual bool segHasTrailingZeroFill(unsigned int) = 0; + virtual uintptr_t segFileOffset(unsigned int) const = 0; + virtual bool segReadable(unsigned int) const = 0; + virtual bool segWriteable(unsigned int) const = 0; + virtual bool segExecutable(unsigned int) const = 0; + virtual bool segUnaccessible(unsigned int) const = 0; + virtual bool segHasPreferredLoadAddress(unsigned int) const = 0; + virtual uintptr_t segPreferredLoadAddress(unsigned int) const = 0; + virtual uintptr_t segActualLoadAddress(unsigned int) const = 0; + virtual uintptr_t segActualEndAddress(unsigned int) const = 0; + + + // info from LC_VERSION_MIN_MACOSX or LC_VERSION_MIN_IPHONEOS + virtual uint32_t sdkVersion() const = 0; + virtual uint32_t minOSVersion() const = 0; + + // if the image contains interposing functions, register them + virtual void registerInterposing() = 0; + + // when resolving symbols look in subImage if symbol can't be found + void reExport(ImageLoader* subImage); + + void weakBind(const LinkContext& context); + + void applyInterposing(const LinkContext& context); + + dyld_image_states getState() { return (dyld_image_states)fState; } + + ino_t getInode() const { return fInode; } + + // used to sort images bottom-up + int compare(const ImageLoader* right) const; + + void incrementDlopenReferenceCount() { ++fDlopenReferenceCount; } + + bool decrementDlopenReferenceCount(); + + void printReferenceCounts(); + + uint32_t dlopenCount() const { return fDlopenReferenceCount; } + + void setCanUnload() { fNeverUnload = false; fLeaveMapped = false; } + + bool neverUnload() const { return fNeverUnload; } + + void setNeverUnload() { fNeverUnload = true; fLeaveMapped = true; } + void setNeverUnloadRecursive(); + + bool isReferencedDownward() { return fIsReferencedDownward; } + + + // triggered by DYLD_PRINT_STATISTICS to write info on work done and how fast + static void printStatistics(unsigned int imageCount, const InitializerTimingList& timingInfo); + static void printStatisticsDetails(unsigned int imageCount, const InitializerTimingList& timingInfo); + + // used with DYLD_IMAGE_SUFFIX + static void addSuffix(const char* path, const char* suffix, char* result); + + static uint32_t hash(const char*); + + static const uint8_t* trieWalk(const uint8_t* start, const uint8_t* end, const char* stringToFind); + + // used instead of directly deleting image + static void deleteImage(ImageLoader*); + + static bool haveInterposingTuples() { return !fgInterposingTuples.empty(); } + static void clearInterposingTuples() { fgInterposingTuples.clear(); } + + bool dependsOn(ImageLoader* image); + + void setPath(const char* path); + void setPaths(const char* path, const char* realPath); + void setPathUnowned(const char* path); + + void clearDepth() { fDepth = 0; } + int getDepth() { return fDepth; } + + void setBeingRemoved() { fBeingRemoved = true; } + bool isBeingRemoved() const { return fBeingRemoved; } + + void markNotUsed() { fMarkedInUse = false; } + void markedUsedRecursive(const std::vector&); + bool isMarkedInUse() const { return fMarkedInUse; } + + void setAddFuncNotified() { fAddFuncNotified = true; } + bool addFuncNotified() const { return fAddFuncNotified; } + + struct InterposeTuple { + uintptr_t replacement; + ImageLoader* neverImage; // don't apply replacement to this image + ImageLoader* onlyImage; // only apply replacement to this image + uintptr_t replacee; + }; + + static uintptr_t read_uleb128(const uint8_t*& p, const uint8_t* end); + static intptr_t read_sleb128(const uint8_t*& p, const uint8_t* end); + +protected: + // abstract base class so all constructors protected + ImageLoader(const char* path, unsigned int libCount); + ImageLoader(const ImageLoader&); + void operator=(const ImageLoader&); + void operator delete(void* image) throw() { ::free(image); } + + + struct LibraryInfo { + uint32_t checksum; + uint32_t minVersion; + uint32_t maxVersion; + }; + + struct DependentLibrary { + ImageLoader* image; + uint32_t required : 1, + checksumMatches : 1, + isReExported : 1, + isSubFramework : 1; + }; + + struct DependentLibraryInfo { + const char* name; + LibraryInfo info; + bool required; + bool reExported; + bool upward; + }; + + + typedef void (*Initializer)(int argc, const char* argv[], const char* envp[], const char* apple[], const ProgramVars* vars); + typedef void (*Terminator)(void); + + + + unsigned int libraryCount() const { return fLibraryCount; } + virtual ImageLoader* libImage(unsigned int) const = 0; + virtual bool libReExported(unsigned int) const = 0; + virtual bool libIsUpward(unsigned int) const = 0; + virtual void setLibImage(unsigned int, ImageLoader*, bool, bool) = 0; + + // To link() an image, its dependent libraries are loaded, it is rebased, bound, and initialized. + // These methods do the above, exactly once, and it the right order + virtual void recursiveLoadLibraries(const LinkContext& context, bool preflightOnly, const RPathChain& loaderRPaths, const char* loadPath); + virtual unsigned recursiveUpdateDepth(unsigned int maxDepth); + virtual void recursiveRebase(const LinkContext& context); + virtual void recursiveBind(const LinkContext& context, bool forceLazysBound, bool neverUnload); + virtual void recursiveApplyInterposing(const LinkContext& context); + virtual void recursiveGetDOFSections(const LinkContext& context, std::vector& dofs); + virtual void recursiveInitialization(const LinkContext& context, mach_port_t this_thread, const char* pathToInitialize, + ImageLoader::InitializerTimingList&, ImageLoader::UninitedUpwards&); + + // fill in information about dependent libraries (array length is fLibraryCount) + virtual void doGetDependentLibraries(DependentLibraryInfo libs[]) = 0; + + // called on images that are libraries, returns info about itself + virtual LibraryInfo doGetLibraryInfo(const LibraryInfo& requestorInfo) = 0; + + // do any fix ups in this image that depend only on the load address of the image + virtual void doRebase(const LinkContext& context) = 0; + + // do any symbolic fix ups in this image + virtual void doBind(const LinkContext& context, bool forceLazysBound) = 0; + + // called later via API to force all lazy pointer to be bound + virtual void doBindJustLazies(const LinkContext& context) = 0; + + // if image has any dtrace DOF sections, append them to list to be registered + virtual void doGetDOFSections(const LinkContext& context, std::vector& dofs) = 0; + + // do interpose + virtual void doInterpose(const LinkContext& context) = 0; + + // run any initialization routines in this image + virtual bool doInitialization(const LinkContext& context) = 0; + + // return if this image has termination routines + virtual bool needsTermination() = 0; + + // support for runtimes in which segments don't have to maintain their relative positions + virtual bool segmentsMustSlideTogether() const = 0; + + // built with PIC code and can load at any address + virtual bool segmentsCanSlide() const = 0; + + // set how much all segments slide + virtual void setSlide(intptr_t slide) = 0; + + // returns if all dependent libraries checksum's were as expected and none slide + bool allDependentLibrariesAsWhenPreBound() const; + + // in mach-o a child tells it parent to re-export, instead of the other way around... + virtual bool isSubframeworkOf(const LinkContext& context, const ImageLoader* image) const = 0; + + // in mach-o a parent library knows name of sub libraries it re-exports.. + virtual bool hasSubLibrary(const LinkContext& context, const ImageLoader* child) const = 0; + + virtual bool weakSymbolsBound(unsigned index) { return fWeakSymbolsBound; } + virtual void setWeakSymbolsBound(unsigned index) { fWeakSymbolsBound = true; } + + // set fState to dyld_image_state_memory_mapped + void setMapped(const LinkContext& context); + + void setFileInfo(dev_t device, ino_t inode, time_t modDate); + + void setDepth(uint16_t depth) { fDepth = depth; } + + static uintptr_t interposedAddress(const LinkContext& context, uintptr_t address, const ImageLoader* notInImage, const ImageLoader* onlyInImage=NULL); + + static uintptr_t fgNextPIEDylibAddress; + static uint32_t fgImagesWithUsedPrebinding; + static uint32_t fgImagesUsedFromSharedCache; + static uint32_t fgImagesHasWeakDefinitions; + static uint32_t fgImagesRequiringCoalescing; + static uint32_t fgTotalRebaseFixups; + static uint32_t fgTotalBindFixups; + static uint32_t fgTotalBindSymbolsResolved; + static uint32_t fgTotalBindImageSearches; + static uint32_t fgTotalLazyBindFixups; + static uint32_t fgTotalPossibleLazyBindFixups; + static uint32_t fgTotalSegmentsMapped; + static uint32_t fgSymbolTrieSearchs; + static uint64_t fgTotalBytesMapped; + static uint64_t fgTotalBytesPreFetched; + static uint64_t fgTotalLoadLibrariesTime; +public: + static uint64_t fgTotalObjCSetupTime; + static uint64_t fgTotalDebuggerPausedTime; + static uint64_t fgTotalRebindCacheTime; +protected: + static uint64_t fgTotalRebaseTime; + static uint64_t fgTotalBindTime; + static uint64_t fgTotalWeakBindTime; + static uint64_t fgTotalDOF; + static uint64_t fgTotalInitTime; + static std::vector fgInterposingTuples; + + const char* fPath; + const char* fRealPath; + dev_t fDevice; + ino_t fInode; + time_t fLastModified; + uint32_t fPathHash; + uint32_t fDlopenReferenceCount; // count of how many dlopens have been done on this image + + struct recursive_lock { + recursive_lock(mach_port_t t) : thread(t), count(0) {} + mach_port_t thread; + int count; + }; + void recursiveSpinLock(recursive_lock&); + void recursiveSpinUnLock(); + +private: + const ImageLoader::Symbol* findExportedSymbolInDependentImagesExcept(const char* name, const ImageLoader** dsiStart, + const ImageLoader**& dsiCur, const ImageLoader** dsiEnd, const ImageLoader** foundIn) const; + + void processInitializers(const LinkContext& context, mach_port_t this_thread, + InitializerTimingList& timingInfo, ImageLoader::UninitedUpwards& ups); + + + recursive_lock* fInitializerRecursiveLock; + uint16_t fDepth; + uint16_t fLoadOrder; + uint32_t fState : 8, + fLibraryCount : 10, + fAllLibraryChecksumsAndLoadAddressesMatch : 1, + fLeaveMapped : 1, // when unloaded, leave image mapped in cause some other code may have pointers into it + fNeverUnload : 1, // image was statically loaded by main executable + fHideSymbols : 1, // ignore this image's exported symbols when linking other images + fMatchByInstallName : 1,// look at image's install-path not its load path + fInterposed : 1, + fRegisteredDOF : 1, + fAllLazyPointersBound : 1, + fMarkedInUse : 1, + fBeingRemoved : 1, + fAddFuncNotified : 1, + fPathOwnedByImage : 1, + fIsReferencedDownward : 1, + fWeakSymbolsBound : 1; + + static uint16_t fgLoadOrdinal; +}; + + +VECTOR_NEVER_DESTRUCTED_EXTERN(ImageLoader::InterposeTuple); + + +#endif + diff --git a/src/dyld-apple/src/ImageLoaderMachO.cpp b/src/dyld-apple/src/ImageLoaderMachO.cpp new file mode 100644 index 000000000..3369bb77d --- /dev/null +++ b/src/dyld-apple/src/ImageLoaderMachO.cpp @@ -0,0 +1,2737 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2004-2010 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@ + */ + +// work around until conformance work is complete rdar://problem/4508801 +#define __srr0 srr0 +#define __eip eip +#define __rip rip + +#define __STDC_LIMIT_MACROS +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ImageLoaderMachO.h" +#include "ImageLoaderMachOCompressed.h" +#if SUPPORT_CLASSIC_MACHO +#include "ImageLoaderMachOClassic.h" +#endif +#include "mach-o/dyld_images.h" +#include "dyld.h" + +// use stack guard random value to add padding between dylibs +extern "C" long __stack_chk_guard; + +#ifndef LC_LOAD_UPWARD_DYLIB + #define LC_LOAD_UPWARD_DYLIB (0x23|LC_REQ_DYLD) /* load of dylib whose initializers run later */ +#endif + +#ifndef LC_VERSION_MIN_TVOS + #define LC_VERSION_MIN_TVOS 0x2F +#endif + +#ifndef LC_VERSION_MIN_WATCHOS + #define LC_VERSION_MIN_WATCHOS 0x30 +#endif + + +#if TARGET_IPHONE_SIMULATOR + #define LIBSYSTEM_DYLIB_PATH "/usr/lib/libSystem.dylib" +#else + #define LIBSYSTEM_DYLIB_PATH "/usr/lib/libSystem.B.dylib" +#endif + +// relocation_info.r_length field has value 3 for 64-bit executables and value 2 for 32-bit executables +#if __LP64__ + #define LC_SEGMENT_COMMAND LC_SEGMENT_64 + #define LC_ROUTINES_COMMAND LC_ROUTINES_64 + #define LC_SEGMENT_COMMAND_WRONG LC_SEGMENT + struct macho_segment_command : public segment_command_64 {}; + struct macho_section : public section_64 {}; + struct macho_routines_command : public routines_command_64 {}; +#else + #define LC_SEGMENT_COMMAND LC_SEGMENT + #define LC_ROUTINES_COMMAND LC_ROUTINES + #define LC_SEGMENT_COMMAND_WRONG LC_SEGMENT_64 + struct macho_segment_command : public segment_command {}; + struct macho_section : public section {}; + struct macho_routines_command : public routines_command {}; +#endif + +uint32_t ImageLoaderMachO::fgSymbolTableBinarySearchs = 0; + + +ImageLoaderMachO::ImageLoaderMachO(const macho_header* mh, const char* path, unsigned int segCount, + uint32_t segOffsets[], unsigned int libCount) + : ImageLoader(path, libCount), fCoveredCodeLength(0), fMachOData((uint8_t*)mh), fLinkEditBase(NULL), fSlide(0), + fEHFrameSectionOffset(0), fUnwindInfoSectionOffset(0), fDylibIDOffset(0), +fSegmentsCount(segCount), fIsSplitSeg(false), fInSharedCache(false), +#if TEXT_RELOC_SUPPORT + fTextSegmentRebases(false), + fTextSegmentBinds(false), +#endif +#if __i386__ + fReadOnlyImportSegment(false), +#endif + fHasSubLibraries(false), fHasSubUmbrella(false), fInUmbrella(false), fHasDOFSections(false), fHasDashInit(false), + fHasInitializers(false), fHasTerminators(false), fNotifyObjC(false), fRetainForObjC(false), fRegisteredAsRequiresCoalescing(false) +{ + fIsSplitSeg = ((mh->flags & MH_SPLIT_SEGS) != 0); + + // construct SegmentMachO object for each LC_SEGMENT cmd using "placement new" to put + // each SegmentMachO object in array at end of ImageLoaderMachO object + const uint32_t cmd_count = mh->ncmds; + const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; + const struct load_command* cmd = cmds; + for (uint32_t i = 0, segIndex=0; i < cmd_count; ++i) { + if ( cmd->cmd == LC_SEGMENT_COMMAND ) { + const struct macho_segment_command* segCmd = (struct macho_segment_command*)cmd; + // ignore zero-sized segments + if ( segCmd->vmsize != 0 ) { + // record offset of load command + segOffsets[segIndex++] = (uint32_t)((uint8_t*)segCmd - fMachOData); + } + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + +} + +#if __MAC_OS_X_VERSION_MIN_REQUIRED +static uintptr_t pageAlign(uintptr_t value) +{ + return (value + 4095) & (-4096); +} +#endif + +// determine if this mach-o file has classic or compressed LINKEDIT and number of segments it has +void ImageLoaderMachO::sniffLoadCommands(const macho_header* mh, const char* path, bool inCache, bool* compressed, + unsigned int* segCount, unsigned int* libCount, const LinkContext& context, + const linkedit_data_command** codeSigCmd, + const encryption_info_command** encryptCmd) +{ + *compressed = false; + *segCount = 0; + *libCount = 0; + *codeSigCmd = NULL; + *encryptCmd = NULL; + + const uint32_t cmd_count = mh->ncmds; + const uint32_t sizeofcmds = mh->sizeofcmds; + if ( sizeofcmds > (MAX_MACH_O_HEADER_AND_LOAD_COMMANDS_SIZE-sizeof(macho_header)) ) + dyld::throwf("malformed mach-o: load commands size (%u) > %u", sizeofcmds, MAX_MACH_O_HEADER_AND_LOAD_COMMANDS_SIZE); + if ( cmd_count > (sizeofcmds/sizeof(load_command)) ) + dyld::throwf("malformed mach-o: ncmds (%u) too large to fit in sizeofcmds (%u)", cmd_count, sizeofcmds); + const struct load_command* const startCmds = (struct load_command*)(((uint8_t*)mh) + sizeof(macho_header)); + const struct load_command* const endCmds = (struct load_command*)(((uint8_t*)mh) + sizeof(macho_header) + sizeofcmds); + const struct load_command* cmd = startCmds; + bool foundLoadCommandSegment = false; + const macho_segment_command* linkeditSegCmd = NULL; + const macho_segment_command* startOfFileSegCmd = NULL; + const dyld_info_command* dyldInfoCmd = NULL; + const symtab_command* symTabCmd = NULL; + const dysymtab_command* dynSymbTabCmd = NULL; + for (uint32_t i = 0; i < cmd_count; ++i) { + uint32_t cmdLength = cmd->cmdsize; + const macho_segment_command* segCmd; + const dylib_command* dylibCmd; + if ( cmdLength < 8 ) { + dyld::throwf("malformed mach-o image: load command #%d length (%u) too small in %s", + i, cmdLength, path); + } + const struct load_command* const nextCmd = (const struct load_command*)(((char*)cmd)+cmdLength); + if ( (nextCmd > endCmds) || (nextCmd < cmd) ) { + dyld::throwf("malformed mach-o image: load command #%d length (%u) would exceed sizeofcmds (%u) in %s", + i, cmdLength, mh->sizeofcmds, path); + } + switch (cmd->cmd) { + case LC_DYLD_INFO: + case LC_DYLD_INFO_ONLY: + if ( cmd->cmdsize != sizeof(dyld_info_command) ) + throw "malformed mach-o image: LC_DYLD_INFO size wrong"; + dyldInfoCmd = (struct dyld_info_command*)cmd; + *compressed = true; + break; + case LC_SEGMENT_COMMAND: + segCmd = (struct macho_segment_command*)cmd; +#if __MAC_OS_X_VERSION_MIN_REQUIRED + // rdar://problem/19617624 allow unmapped segments on OSX (but not iOS) + if ( ((segCmd->filesize) > pageAlign(segCmd->vmsize)) && (segCmd->vmsize != 0) ) +#else + // dyld should support non-allocatable __LLVM segment + if ( (segCmd->filesize > segCmd->vmsize) && ((segCmd->vmsize != 0) || ((segCmd->flags & SG_NORELOC) == 0)) ) +#endif + dyld::throwf("malformed mach-o image: segment load command %s filesize (0x%0lX) is larger than vmsize (0x%0lX)", segCmd->segname, (long)segCmd->filesize , (long)segCmd->vmsize ); + if ( cmd->cmdsize < sizeof(macho_segment_command) ) + throw "malformed mach-o image: LC_SEGMENT size too small"; + if ( cmd->cmdsize != (sizeof(macho_segment_command) + segCmd->nsects * sizeof(macho_section)) ) + throw "malformed mach-o image: LC_SEGMENT size wrong for number of sections"; + // ignore zero-sized segments + if ( segCmd->vmsize != 0 ) + *segCount += 1; + if ( strcmp(segCmd->segname, "__LINKEDIT") == 0 ) { + #if TARGET_IPHONE_SIMULATOR + // Note: should check on all platforms that __LINKEDIT is read-only, but + if ( segCmd->initprot != VM_PROT_READ ) + throw "malformed mach-o image: __LINKEDIT segment does not have read-only permissions"; + #endif + if ( segCmd->fileoff == 0 ) + throw "malformed mach-o image: __LINKEDIT has fileoff==0 which overlaps mach_header"; + if ( linkeditSegCmd != NULL ) + throw "malformed mach-o image: multiple __LINKEDIT segments"; + linkeditSegCmd = segCmd; + } + else { + if ( segCmd->initprot & 0xFFFFFFF8 ) + dyld::throwf("malformed mach-o image: %s segment has invalid permission bits (0x%X) in initprot", segCmd->segname, segCmd->initprot); + if ( segCmd->maxprot & 0xFFFFFFF8 ) + dyld::throwf("malformed mach-o image: %s segment has invalid permission bits (0x%X) in maxprot", segCmd->segname, segCmd->maxprot); + if ( (segCmd->initprot != 0) && ((segCmd->initprot & VM_PROT_READ) == 0) ) + dyld::throwf("malformed mach-o image: %s segment is not mapped readable", segCmd->segname); + } + if ( (segCmd->fileoff == 0) && (segCmd->filesize != 0) ) { + if ( (segCmd->initprot & VM_PROT_READ) == 0 ) + dyld::throwf("malformed mach-o image: %s segment maps start of file but is not readable", segCmd->segname); + if ( (segCmd->initprot & VM_PROT_WRITE) == VM_PROT_WRITE ) { + if ( context.strictMachORequired ) + dyld::throwf("malformed mach-o image: %s segment maps start of file but is writable", segCmd->segname); + } + if ( segCmd->filesize < (sizeof(macho_header) + mh->sizeofcmds) ) + dyld::throwf("malformed mach-o image: %s segment does not map all of load commands", segCmd->segname); + if ( startOfFileSegCmd != NULL ) + dyld::throwf("malformed mach-o image: multiple segments map start of file: %s %s", startOfFileSegCmd->segname, segCmd->segname); + startOfFileSegCmd = segCmd; + } + if ( context.strictMachORequired ) { + uintptr_t vmStart = segCmd->vmaddr; + uintptr_t vmSize = segCmd->vmsize; + uintptr_t vmEnd = vmStart + vmSize; + uintptr_t fileStart = segCmd->fileoff; + uintptr_t fileSize = segCmd->filesize; + if ( (intptr_t)(vmSize) < 0 ) + dyld::throwf("malformed mach-o image: segment load command %s vmsize too large in %s", segCmd->segname, path); + if ( vmStart > vmEnd ) + dyld::throwf("malformed mach-o image: segment load command %s wraps around address space", segCmd->segname); + if ( vmSize != fileSize ) { + if ( segCmd->initprot == 0 ) { + // allow: fileSize == 0 && initprot == 0 e.g. __PAGEZERO + // allow: vmSize == 0 && initprot == 0 e.g. __LLVM + if ( (fileSize != 0) && (vmSize != 0) ) + dyld::throwf("malformed mach-o image: unaccessable segment %s has non-zero filesize and vmsize", segCmd->segname); + } + else { + // allow: vmSize > fileSize && initprot != X e.g. __DATA + if ( vmSize < fileSize ) { + dyld::throwf("malformed mach-o image: segment %s has vmsize < filesize", segCmd->segname); + } + if ( segCmd->initprot & VM_PROT_EXECUTE ) { + dyld::throwf("malformed mach-o image: segment %s has vmsize != filesize and is executable", segCmd->segname); + } + } + } + if ( inCache ) { + if ( (fileSize != 0) && (segCmd->initprot == (VM_PROT_READ | VM_PROT_EXECUTE)) ) { + if ( foundLoadCommandSegment ) + throw "load commands in multiple segments"; + foundLoadCommandSegment = true; + } + } + else if ( (fileStart < mh->sizeofcmds) && (fileSize != 0) ) { + // all load commands must be in an executable segment + if ( (fileStart != 0) || (fileSize < (mh->sizeofcmds+sizeof(macho_header))) ) + dyld::throwf("malformed mach-o image: segment %s does not span all load commands", segCmd->segname); + if ( segCmd->initprot != (VM_PROT_READ | VM_PROT_EXECUTE) ) + dyld::throwf("malformed mach-o image: load commands found in segment %s with wrong permissions", segCmd->segname); + if ( foundLoadCommandSegment ) + throw "load commands in multiple segments"; + foundLoadCommandSegment = true; + } + + const struct macho_section* const sectionsStart = (struct macho_section*)((char*)segCmd + sizeof(struct macho_segment_command)); + const struct macho_section* const sectionsEnd = §ionsStart[segCmd->nsects]; + for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) { + if (!inCache && sect->offset != 0 && ((sect->offset + sect->size) > (segCmd->fileoff + segCmd->filesize))) + dyld::throwf("malformed mach-o image: section %s,%s of '%s' exceeds segment %s booundary", sect->segname, sect->sectname, path, segCmd->segname); + } + } + break; + case LC_SEGMENT_COMMAND_WRONG: + dyld::throwf("malformed mach-o image: wrong LC_SEGMENT[_64] for architecture"); + break; + case LC_LOAD_DYLIB: + case LC_LOAD_WEAK_DYLIB: + case LC_REEXPORT_DYLIB: + case LC_LOAD_UPWARD_DYLIB: + *libCount += 1; + // fall thru + case LC_ID_DYLIB: + dylibCmd = (dylib_command*)cmd; + if ( dylibCmd->dylib.name.offset > cmdLength ) + dyld::throwf("malformed mach-o image: dylib load command #%d has offset (%u) outside its size (%u)", i, dylibCmd->dylib.name.offset, cmdLength); + if ( (dylibCmd->dylib.name.offset + strlen((char*)dylibCmd + dylibCmd->dylib.name.offset) + 1) > cmdLength ) + dyld::throwf("malformed mach-o image: dylib load command #%d string extends beyond end of load command", i); + break; + case LC_CODE_SIGNATURE: + if ( cmd->cmdsize != sizeof(linkedit_data_command) ) + throw "malformed mach-o image: LC_CODE_SIGNATURE size wrong"; + // only support one LC_CODE_SIGNATURE per image + if ( *codeSigCmd != NULL ) + throw "malformed mach-o image: multiple LC_CODE_SIGNATURE load commands"; + *codeSigCmd = (struct linkedit_data_command*)cmd; + break; + case LC_ENCRYPTION_INFO: + if ( cmd->cmdsize != sizeof(encryption_info_command) ) + throw "malformed mach-o image: LC_ENCRYPTION_INFO size wrong"; + // only support one LC_ENCRYPTION_INFO per image + if ( *encryptCmd != NULL ) + throw "malformed mach-o image: multiple LC_ENCRYPTION_INFO load commands"; + *encryptCmd = (encryption_info_command*)cmd; + break; + case LC_ENCRYPTION_INFO_64: + if ( cmd->cmdsize != sizeof(encryption_info_command_64) ) + throw "malformed mach-o image: LC_ENCRYPTION_INFO_64 size wrong"; + // only support one LC_ENCRYPTION_INFO_64 per image + if ( *encryptCmd != NULL ) + throw "malformed mach-o image: multiple LC_ENCRYPTION_INFO_64 load commands"; + *encryptCmd = (encryption_info_command*)cmd; + break; + case LC_SYMTAB: + if ( cmd->cmdsize != sizeof(symtab_command) ) + throw "malformed mach-o image: LC_SYMTAB size wrong"; + symTabCmd = (symtab_command*)cmd; + break; + case LC_DYSYMTAB: + if ( cmd->cmdsize != sizeof(dysymtab_command) ) + throw "malformed mach-o image: LC_DYSYMTAB size wrong"; + dynSymbTabCmd = (dysymtab_command*)cmd; + break; +#if __MAC_OS_X_VERSION_MIN_REQUIRED + // error when loading iOS Simulator mach-o binary into macOS process + case LC_VERSION_MIN_WATCHOS: + case LC_VERSION_MIN_TVOS: + case LC_VERSION_MIN_IPHONEOS: + throw "mach-o, but built for simulator (not macOS)"; + break; +#endif + } + cmd = nextCmd; + } + + if ( context.strictMachORequired && !foundLoadCommandSegment ) + throw "load commands not in a segment"; + if ( linkeditSegCmd == NULL ) + throw "malformed mach-o image: missing __LINKEDIT segment"; + if ( startOfFileSegCmd == NULL ) + throw "malformed mach-o image: missing __TEXT segment that maps start of file"; + // verify every segment does not overlap another segment + if ( context.strictMachORequired ) { + uintptr_t lastFileStart = 0; + uintptr_t linkeditFileStart = 0; + const struct load_command* cmd1 = startCmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + if ( cmd1->cmd == LC_SEGMENT_COMMAND ) { + struct macho_segment_command* segCmd1 = (struct macho_segment_command*)cmd1; + uintptr_t vmStart1 = segCmd1->vmaddr; + uintptr_t vmEnd1 = segCmd1->vmaddr + segCmd1->vmsize; + uintptr_t fileStart1 = segCmd1->fileoff; + uintptr_t fileEnd1 = segCmd1->fileoff + segCmd1->filesize; + + if (fileStart1 > lastFileStart) + lastFileStart = fileStart1; + + if ( strcmp(&segCmd1->segname[0], "__LINKEDIT") == 0 ) { + linkeditFileStart = fileStart1; + } + + const struct load_command* cmd2 = startCmds; + for (uint32_t j = 0; j < cmd_count; ++j) { + if ( cmd2 == cmd1 ) + continue; + if ( cmd2->cmd == LC_SEGMENT_COMMAND ) { + struct macho_segment_command* segCmd2 = (struct macho_segment_command*)cmd2; + uintptr_t vmStart2 = segCmd2->vmaddr; + uintptr_t vmEnd2 = segCmd2->vmaddr + segCmd2->vmsize; + uintptr_t fileStart2 = segCmd2->fileoff; + uintptr_t fileEnd2 = segCmd2->fileoff + segCmd2->filesize; + if ( ((vmStart2 <= vmStart1) && (vmEnd2 > vmStart1) && (vmEnd1 > vmStart1)) + || ((vmStart2 >= vmStart1) && (vmStart2 < vmEnd1) && (vmEnd2 > vmStart2)) ) + dyld::throwf("malformed mach-o image: segment %s vm overlaps segment %s", segCmd1->segname, segCmd2->segname); + if ( ((fileStart2 <= fileStart1) && (fileEnd2 > fileStart1) && (fileEnd1 > fileStart1)) + || ((fileStart2 >= fileStart1) && (fileStart2 < fileEnd1) && (fileEnd2 > fileStart2)) ) + dyld::throwf("malformed mach-o image: segment %s file content overlaps segment %s", segCmd1->segname, segCmd2->segname); + } + cmd2 = (const struct load_command*)(((char*)cmd2)+cmd2->cmdsize); + } + } + cmd1 = (const struct load_command*)(((char*)cmd1)+cmd1->cmdsize); + } + + if (lastFileStart != linkeditFileStart) + dyld::throwf("malformed mach-o image: __LINKEDIT must be last segment"); + } + + // validate linkedit content + if ( (dyldInfoCmd == NULL) && (symTabCmd == NULL) ) + throw "malformed mach-o image: missing LC_SYMTAB or LC_DYLD_INFO"; + if ( dynSymbTabCmd == NULL ) + throw "malformed mach-o image: missing LC_DYSYMTAB"; + + uint32_t linkeditFileOffsetStart = (uint32_t)linkeditSegCmd->fileoff; + uint32_t linkeditFileOffsetEnd = (uint32_t)linkeditSegCmd->fileoff + (uint32_t)linkeditSegCmd->filesize; + + if ( !inCache && (dyldInfoCmd != NULL) && context.strictMachORequired ) { + // validate all LC_DYLD_INFO chunks fit in LINKEDIT and don't overlap + uint32_t offset = linkeditFileOffsetStart; + if ( dyldInfoCmd->rebase_size != 0 ) { + if ( dyldInfoCmd->rebase_size & 0x80000000 ) + throw "malformed mach-o image: dyld rebase info size overflow"; + if ( dyldInfoCmd->rebase_off < offset ) + throw "malformed mach-o image: dyld rebase info underruns __LINKEDIT"; + offset = dyldInfoCmd->rebase_off + dyldInfoCmd->rebase_size; + if ( offset > linkeditFileOffsetEnd ) + throw "malformed mach-o image: dyld rebase info overruns __LINKEDIT"; + } + if ( dyldInfoCmd->bind_size != 0 ) { + if ( dyldInfoCmd->bind_size & 0x80000000 ) + throw "malformed mach-o image: dyld bind info size overflow"; + if ( dyldInfoCmd->bind_off < offset ) + throw "malformed mach-o image: dyld bind info overlaps rebase info"; + offset = dyldInfoCmd->bind_off + dyldInfoCmd->bind_size; + if ( offset > linkeditFileOffsetEnd ) + throw "malformed mach-o image: dyld bind info overruns __LINKEDIT"; + } + if ( dyldInfoCmd->weak_bind_size != 0 ) { + if ( dyldInfoCmd->weak_bind_size & 0x80000000 ) + throw "malformed mach-o image: dyld weak bind info size overflow"; + if ( dyldInfoCmd->weak_bind_off < offset ) + throw "malformed mach-o image: dyld weak bind info overlaps bind info"; + offset = dyldInfoCmd->weak_bind_off + dyldInfoCmd->weak_bind_size; + if ( offset > linkeditFileOffsetEnd ) + throw "malformed mach-o image: dyld weak bind info overruns __LINKEDIT"; + } + if ( dyldInfoCmd->lazy_bind_size != 0 ) { + if ( dyldInfoCmd->lazy_bind_size & 0x80000000 ) + throw "malformed mach-o image: dyld lazy bind info size overflow"; + if ( dyldInfoCmd->lazy_bind_off < offset ) + throw "malformed mach-o image: dyld lazy bind info overlaps weak bind info"; + offset = dyldInfoCmd->lazy_bind_off + dyldInfoCmd->lazy_bind_size; + if ( offset > linkeditFileOffsetEnd ) + throw "malformed mach-o image: dyld lazy bind info overruns __LINKEDIT"; + } + if ( dyldInfoCmd->export_size != 0 ) { + if ( dyldInfoCmd->export_size & 0x80000000 ) + throw "malformed mach-o image: dyld export info size overflow"; + if ( dyldInfoCmd->export_off < offset ) + throw "malformed mach-o image: dyld export info overlaps lazy bind info"; + offset = dyldInfoCmd->export_off + dyldInfoCmd->export_size; + if ( offset > linkeditFileOffsetEnd ) + throw "malformed mach-o image: dyld export info overruns __LINKEDIT"; + } + } + + if ( symTabCmd != NULL ) { + // validate symbol table fits in LINKEDIT + if ( symTabCmd->symoff < linkeditFileOffsetStart ) + throw "malformed mach-o image: symbol table underruns __LINKEDIT"; + if ( symTabCmd->nsyms > 0x10000000 ) + throw "malformed mach-o image: symbol table too large"; + uint32_t symbolsSize = symTabCmd->nsyms * sizeof(macho_nlist); + if ( symbolsSize > linkeditSegCmd->filesize ) + throw "malformed mach-o image: symbol table overruns __LINKEDIT"; + if ( symTabCmd->symoff + symbolsSize < symTabCmd->symoff ) + throw "malformed mach-o image: symbol table size wraps"; + if ( symTabCmd->symoff + symbolsSize > symTabCmd->stroff ) + throw "malformed mach-o image: symbol table overlaps symbol strings"; + if ( symTabCmd->stroff + symTabCmd->strsize < symTabCmd->stroff ) + throw "malformed mach-o image: symbol string size wraps"; + if ( symTabCmd->stroff + symTabCmd->strsize > linkeditFileOffsetEnd ) { + // let old apps overflow as long as it stays within mapped page + if ( context.strictMachORequired || (symTabCmd->stroff + symTabCmd->strsize > ((linkeditFileOffsetEnd + 4095) & (-4096))) ) + throw "malformed mach-o image: symbol strings overrun __LINKEDIT"; + } + // validate indirect symbol table + if ( dynSymbTabCmd->nindirectsyms != 0 ) { + if ( dynSymbTabCmd->indirectsymoff < linkeditFileOffsetStart ) + throw "malformed mach-o image: indirect symbol table underruns __LINKEDIT"; + if ( dynSymbTabCmd->nindirectsyms > 0x10000000 ) + throw "malformed mach-o image: indirect symbol table too large"; + uint32_t indirectTableSize = dynSymbTabCmd->nindirectsyms * sizeof(uint32_t); + if ( indirectTableSize > linkeditSegCmd->filesize ) + throw "malformed mach-o image: indirect symbol table overruns __LINKEDIT"; + if ( dynSymbTabCmd->indirectsymoff + indirectTableSize < dynSymbTabCmd->indirectsymoff ) + throw "malformed mach-o image: indirect symbol table size wraps"; + if ( context.strictMachORequired && (dynSymbTabCmd->indirectsymoff + indirectTableSize > symTabCmd->stroff) ) + throw "malformed mach-o image: indirect symbol table overruns string pool"; + } + if ( (dynSymbTabCmd->nlocalsym > symTabCmd->nsyms) || (dynSymbTabCmd->ilocalsym > symTabCmd->nsyms) ) + throw "malformed mach-o image: indirect symbol table local symbol count exceeds total symbols"; + if ( dynSymbTabCmd->ilocalsym + dynSymbTabCmd->nlocalsym < dynSymbTabCmd->ilocalsym ) + throw "malformed mach-o image: indirect symbol table local symbol count wraps"; + if ( (dynSymbTabCmd->nextdefsym > symTabCmd->nsyms) || (dynSymbTabCmd->iextdefsym > symTabCmd->nsyms) ) + throw "malformed mach-o image: indirect symbol table extern symbol count exceeds total symbols"; + if ( dynSymbTabCmd->iextdefsym + dynSymbTabCmd->nextdefsym < dynSymbTabCmd->iextdefsym ) + throw "malformed mach-o image: indirect symbol table extern symbol count wraps"; + if ( (dynSymbTabCmd->nundefsym > symTabCmd->nsyms) || (dynSymbTabCmd->iundefsym > symTabCmd->nsyms) ) + throw "malformed mach-o image: indirect symbol table undefined symbol count exceeds total symbols"; + if ( dynSymbTabCmd->iundefsym + dynSymbTabCmd->nundefsym < dynSymbTabCmd->iundefsym ) + throw "malformed mach-o image: indirect symbol table undefined symbol count wraps"; + } + + + // fSegmentsArrayCount is only 8-bits + if ( *segCount > 255 ) + dyld::throwf("malformed mach-o image: more than 255 segments in %s", path); + + // fSegmentsArrayCount is only 8-bits + if ( *libCount > 4095 ) + dyld::throwf("malformed mach-o image: more than 4095 dependent libraries in %s", path); + + if ( needsAddedLibSystemDepency(*libCount, mh) ) + *libCount = 1; +} + + + +// create image for main executable +ImageLoader* ImageLoaderMachO::instantiateMainExecutable(const macho_header* mh, uintptr_t slide, const char* path, const LinkContext& context) +{ + //dyld::log("ImageLoader=%ld, ImageLoaderMachO=%ld, ImageLoaderMachOClassic=%ld, ImageLoaderMachOCompressed=%ld\n", + // sizeof(ImageLoader), sizeof(ImageLoaderMachO), sizeof(ImageLoaderMachOClassic), sizeof(ImageLoaderMachOCompressed)); + bool compressed; + unsigned int segCount; + unsigned int libCount; + const linkedit_data_command* codeSigCmd; + const encryption_info_command* encryptCmd; + sniffLoadCommands(mh, path, false, &compressed, &segCount, &libCount, context, &codeSigCmd, &encryptCmd); + // instantiate concrete class based on content of load commands + if ( compressed ) + return ImageLoaderMachOCompressed::instantiateMainExecutable(mh, slide, path, segCount, libCount, context); + else +#if SUPPORT_CLASSIC_MACHO + return ImageLoaderMachOClassic::instantiateMainExecutable(mh, slide, path, segCount, libCount, context); +#else + throw "missing LC_DYLD_INFO load command"; +#endif +} + + +// create image by mapping in a mach-o file +ImageLoader* ImageLoaderMachO::instantiateFromFile(const char* path, int fd, const uint8_t firstPages[], size_t firstPagesSize, uint64_t offsetInFat, + uint64_t lenInFat, const struct stat& info, const LinkContext& context) +{ + bool compressed; + unsigned int segCount; + unsigned int libCount; + const linkedit_data_command* codeSigCmd; + const encryption_info_command* encryptCmd; + sniffLoadCommands((const macho_header*)firstPages, path, false, &compressed, &segCount, &libCount, context, &codeSigCmd, &encryptCmd); + // instantiate concrete class based on content of load commands + if ( compressed ) + return ImageLoaderMachOCompressed::instantiateFromFile(path, fd, firstPages, firstPagesSize, offsetInFat, lenInFat, info, segCount, libCount, codeSigCmd, encryptCmd, context); + else +#if SUPPORT_CLASSIC_MACHO + return ImageLoaderMachOClassic::instantiateFromFile(path, fd, firstPages, firstPagesSize, offsetInFat, lenInFat, info, segCount, libCount, codeSigCmd, context); +#else + throw "missing LC_DYLD_INFO load command"; +#endif +} + +// create image by using cached mach-o file +ImageLoader* ImageLoaderMachO::instantiateFromCache(const macho_header* mh, const char* path, long slide, const struct stat& info, const LinkContext& context) +{ + // instantiate right concrete class + bool compressed; + unsigned int segCount; + unsigned int libCount; + const linkedit_data_command* codeSigCmd; + const encryption_info_command* encryptCmd; + sniffLoadCommands(mh, path, true, &compressed, &segCount, &libCount, context, &codeSigCmd, &encryptCmd); + // instantiate concrete class based on content of load commands + if ( compressed ) + return ImageLoaderMachOCompressed::instantiateFromCache(mh, path, slide, info, segCount, libCount, context); + else +#if SUPPORT_CLASSIC_MACHO + return ImageLoaderMachOClassic::instantiateFromCache(mh, path, slide, info, segCount, libCount, context); +#else + throw "missing LC_DYLD_INFO load command"; +#endif +} + +// create image by copying an in-memory mach-o file +ImageLoader* ImageLoaderMachO::instantiateFromMemory(const char* moduleName, const macho_header* mh, uint64_t len, const LinkContext& context) +{ + bool compressed; + unsigned int segCount; + unsigned int libCount; + const linkedit_data_command* sigcmd; + const encryption_info_command* encryptCmd; + sniffLoadCommands(mh, moduleName, false, &compressed, &segCount, &libCount, context, &sigcmd, &encryptCmd); + // instantiate concrete class based on content of load commands + if ( compressed ) + return ImageLoaderMachOCompressed::instantiateFromMemory(moduleName, mh, len, segCount, libCount, context); + else +#if SUPPORT_CLASSIC_MACHO + return ImageLoaderMachOClassic::instantiateFromMemory(moduleName, mh, len, segCount, libCount, context); +#else + throw "missing LC_DYLD_INFO load command"; +#endif +} + + +int ImageLoaderMachO::crashIfInvalidCodeSignature() +{ + // Now that segments are mapped in, try reading from first executable segment. + // If code signing is enabled the kernel will validate the code signature + // when paging in, and kill the process if invalid. + for(unsigned int i=0; i < fSegmentsCount; ++i) { + if ( (segFileOffset(i) == 0) && (segFileSize(i) != 0) ) { + // return read value to ensure compiler does not optimize away load + int* p = (int*)segActualLoadAddress(i); + return *p; + } + } + return 0; +} + + +void ImageLoaderMachO::parseLoadCmds(const LinkContext& context) +{ + // now that segments are mapped in, get real fMachOData, fLinkEditBase, and fSlide + for(unsigned int i=0; i < fSegmentsCount; ++i) { + // set up pointer to __LINKEDIT segment + if ( strcmp(segName(i),"__LINKEDIT") == 0 ) { + if ( context.requireCodeSignature && (segFileOffset(i) > fCoveredCodeLength)) + dyld::throwf("cannot load '%s' (segment outside of code signature)", this->getShortName()); + fLinkEditBase = (uint8_t*)(segActualLoadAddress(i) - segFileOffset(i)); + } +#if TEXT_RELOC_SUPPORT + // __TEXT segment always starts at beginning of file and contains mach_header and load commands + if ( segExecutable(i) ) { + if ( segHasRebaseFixUps(i) && (fSlide != 0) ) + fTextSegmentRebases = true; + if ( segHasBindFixUps(i) ) + fTextSegmentBinds = true; + } +#endif +#if __i386__ + if ( segIsReadOnlyImport(i) ) + fReadOnlyImportSegment = true; +#endif + // some segment always starts at beginning of file and contains mach_header and load commands + if ( (segFileOffset(i) == 0) && (segFileSize(i) != 0) ) { + fMachOData = (uint8_t*)(segActualLoadAddress(i)); + } + } + + // keep count of prebound images with weak exports + if ( this->participatesInCoalescing() ) { + ++fgImagesRequiringCoalescing; + fRegisteredAsRequiresCoalescing = true; + if ( this->hasCoalescedExports() ) + ++fgImagesHasWeakDefinitions; + } + + // keep count of images used in shared cache + if ( fInSharedCache ) + ++fgImagesUsedFromSharedCache; + + // walk load commands (mapped in at start of __TEXT segment) + const dyld_info_command* dyldInfo = NULL; + const macho_nlist* symbolTable = NULL; + const char* symbolTableStrings = NULL; + const struct load_command* firstUnknownCmd = NULL; + const struct version_min_command* minOSVersionCmd = NULL; + const dysymtab_command* dynSymbolTable = NULL; + const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds; + const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; + const struct load_command* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd) { + case LC_SYMTAB: + { + const struct symtab_command* symtab = (struct symtab_command*)cmd; + symbolTableStrings = (const char*)&fLinkEditBase[symtab->stroff]; + symbolTable = (macho_nlist*)(&fLinkEditBase[symtab->symoff]); + } + break; + case LC_DYSYMTAB: + dynSymbolTable = (struct dysymtab_command*)cmd; + break; + case LC_SUB_UMBRELLA: + fHasSubUmbrella = true; + break; + case LC_SUB_FRAMEWORK: + fInUmbrella = true; + break; + case LC_SUB_LIBRARY: + fHasSubLibraries = true; + break; + case LC_ROUTINES_COMMAND: + fHasDashInit = true; + break; + case LC_DYLD_INFO: + case LC_DYLD_INFO_ONLY: + dyldInfo = (struct dyld_info_command*)cmd; + break; + case LC_SEGMENT_COMMAND: + { + const struct macho_segment_command* seg = (struct macho_segment_command*)cmd; + const bool isTextSeg = (strcmp(seg->segname, "__TEXT") == 0); + #if __i386__ && __MAC_OS_X_VERSION_MIN_REQUIRED + const bool isObjCSeg = (strcmp(seg->segname, "__OBJC") == 0); + if ( isObjCSeg ) + fNotifyObjC = true; + #else + const bool isDataSeg = (strncmp(seg->segname, "__DATA", 6) == 0); + #endif + const struct macho_section* const sectionsStart = (struct macho_section*)((char*)seg + sizeof(struct macho_segment_command)); + const struct macho_section* const sectionsEnd = §ionsStart[seg->nsects]; + for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) { + const uint8_t type = sect->flags & SECTION_TYPE; + if ( type == S_MOD_INIT_FUNC_POINTERS ) + fHasInitializers = true; + else if ( type == S_MOD_TERM_FUNC_POINTERS ) + fHasTerminators = true; + else if ( type == S_DTRACE_DOF ) + fHasDOFSections = true; + else if ( isTextSeg && (strcmp(sect->sectname, "__eh_frame") == 0) ) + fEHFrameSectionOffset = (uint32_t)((uint8_t*)sect - fMachOData); + else if ( isTextSeg && (strcmp(sect->sectname, "__unwind_info") == 0) ) + fUnwindInfoSectionOffset = (uint32_t)((uint8_t*)sect - fMachOData); + + #if __i386__ && __MAC_OS_X_VERSION_MIN_REQUIRED + else if ( isObjCSeg ) { + if ( strcmp(sect->sectname, "__image_info") == 0 ) { + const uint32_t* imageInfo = (uint32_t*)(sect->addr + fSlide); + uint32_t flags = imageInfo[1]; + if ( (flags & 4) && (((macho_header*)fMachOData)->filetype != MH_EXECUTE) ) + dyld::throwf("cannot load '%s' because Objective-C garbage collection is not supported", getPath()); + } + else if ( ((macho_header*)fMachOData)->filetype == MH_DYLIB ) { + fRetainForObjC = true; + } + } + #else + else if ( isDataSeg && (strncmp(sect->sectname, "__objc_imageinfo", 16) == 0) ) { + #if __MAC_OS_X_VERSION_MIN_REQUIRED + const uint32_t* imageInfo = (uint32_t*)(sect->addr + fSlide); + uint32_t flags = imageInfo[1]; + if ( (flags & 4) && (((macho_header*)fMachOData)->filetype != MH_EXECUTE) ) + dyld::throwf("cannot load '%s' because Objective-C garbage collection is not supported", getPath()); + #endif + fNotifyObjC = true; + } + else if ( isDataSeg && (strncmp(sect->sectname, "__objc_", 7) == 0) && (((macho_header*)fMachOData)->filetype == MH_DYLIB) ) + fRetainForObjC = true; + #endif + } + } + break; + case LC_TWOLEVEL_HINTS: + // no longer supported + break; + case LC_ID_DYLIB: + { + fDylibIDOffset = (uint32_t)((uint8_t*)cmd - fMachOData); + } + break; + case LC_RPATH: + case LC_LOAD_WEAK_DYLIB: + case LC_REEXPORT_DYLIB: + case LC_LOAD_UPWARD_DYLIB: + case LC_MAIN: + // do nothing, just prevent LC_REQ_DYLD exception from occuring + break; + case LC_VERSION_MIN_MACOSX: + case LC_VERSION_MIN_IPHONEOS: + case LC_VERSION_MIN_TVOS: + case LC_VERSION_MIN_WATCHOS: + minOSVersionCmd = (version_min_command*)cmd; + break; + default: + if ( (cmd->cmd & LC_REQ_DYLD) != 0 ) { + if ( firstUnknownCmd == NULL ) + firstUnknownCmd = cmd; + } + break; + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + if ( firstUnknownCmd != NULL ) { + if ( minOSVersionCmd != NULL ) { + dyld::throwf("cannot load '%s' because it was built for OS version %u.%u (load command 0x%08X is unknown)", + this->getShortName(), + minOSVersionCmd->version >> 16, ((minOSVersionCmd->version >> 8) & 0xff), + firstUnknownCmd->cmd); + } + else { + dyld::throwf("cannot load '%s' (load command 0x%08X is unknown)", this->getShortName(), firstUnknownCmd->cmd); + } + } + + + if ( dyldInfo != NULL ) + this->setDyldInfo(dyldInfo); + if ( symbolTable != NULL) + this->setSymbolTableInfo(symbolTable, symbolTableStrings, dynSymbolTable); + +} + +// don't do this work in destructor because we need object to be full subclass +// for UnmapSegments() to work +void ImageLoaderMachO::destroy() +{ + // update count of images with weak exports + if ( fRegisteredAsRequiresCoalescing ) { + --fgImagesRequiringCoalescing; + if ( this->hasCoalescedExports() ) + --fgImagesHasWeakDefinitions; + } + + // keep count of images used in shared cache + if ( fInSharedCache ) + --fgImagesUsedFromSharedCache; + + // unmap image when done + UnmapSegments(); +} + + +unsigned int ImageLoaderMachO::segmentCount() const +{ + return fSegmentsCount; +} + + +const macho_segment_command* ImageLoaderMachO::segLoadCommand(unsigned int segIndex) const +{ + uint32_t* lcOffsets = this->segmentCommandOffsets(); + uint32_t lcOffset = lcOffsets[segIndex]; + return (macho_segment_command*)(&fMachOData[lcOffset]); +} + +const char* ImageLoaderMachO::segName(unsigned int segIndex) const +{ + return segLoadCommand(segIndex)->segname; +} + + +uintptr_t ImageLoaderMachO::segSize(unsigned int segIndex) const +{ + return segLoadCommand(segIndex)->vmsize; +} + + +uintptr_t ImageLoaderMachO::segFileSize(unsigned int segIndex) const +{ + return segLoadCommand(segIndex)->filesize; +} + + +bool ImageLoaderMachO::segHasTrailingZeroFill(unsigned int segIndex) +{ + return ( segWriteable(segIndex) && (segSize(segIndex) > segFileSize(segIndex)) ); +} + + +uintptr_t ImageLoaderMachO::segFileOffset(unsigned int segIndex) const +{ + return segLoadCommand(segIndex)->fileoff; +} + + +bool ImageLoaderMachO::segReadable(unsigned int segIndex) const +{ + return ( (segLoadCommand(segIndex)->initprot & VM_PROT_READ) != 0); +} + + +bool ImageLoaderMachO::segWriteable(unsigned int segIndex) const +{ + return ( (segLoadCommand(segIndex)->initprot & VM_PROT_WRITE) != 0); +} + + +bool ImageLoaderMachO::segExecutable(unsigned int segIndex) const +{ + return ( (segLoadCommand(segIndex)->initprot & VM_PROT_EXECUTE) != 0); +} + + +bool ImageLoaderMachO::segUnaccessible(unsigned int segIndex) const +{ + return (segLoadCommand(segIndex)->initprot == 0); +} + +bool ImageLoaderMachO::segHasPreferredLoadAddress(unsigned int segIndex) const +{ + return (segLoadCommand(segIndex)->vmaddr != 0); +} + +uintptr_t ImageLoaderMachO::segPreferredLoadAddress(unsigned int segIndex) const +{ + return segLoadCommand(segIndex)->vmaddr; +} + +uintptr_t ImageLoaderMachO::segActualLoadAddress(unsigned int segIndex) const +{ + return segLoadCommand(segIndex)->vmaddr + fSlide; +} + + +uintptr_t ImageLoaderMachO::segActualEndAddress(unsigned int segIndex) const +{ + return segActualLoadAddress(segIndex) + segSize(segIndex); +} + +bool ImageLoaderMachO::segHasRebaseFixUps(unsigned int segIndex) const +{ +#if TEXT_RELOC_SUPPORT + // scan sections for fix-up bit + const macho_segment_command* segCmd = segLoadCommand(segIndex); + const struct macho_section* const sectionsStart = (struct macho_section*)((char*)segCmd + sizeof(struct macho_segment_command)); + const struct macho_section* const sectionsEnd = §ionsStart[segCmd->nsects]; + for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) { + if ( (sect->flags & S_ATTR_LOC_RELOC) != 0 ) + return true; + } +#endif + return false; +} + +bool ImageLoaderMachO::segHasBindFixUps(unsigned int segIndex) const +{ +#if TEXT_RELOC_SUPPORT + // scan sections for fix-up bit + const macho_segment_command* segCmd = segLoadCommand(segIndex); + const struct macho_section* const sectionsStart = (struct macho_section*)((char*)segCmd + sizeof(struct macho_segment_command)); + const struct macho_section* const sectionsEnd = §ionsStart[segCmd->nsects]; + for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) { + if ( (sect->flags & S_ATTR_EXT_RELOC) != 0 ) + return true; + } +#endif + return false; +} + +#if __i386__ +bool ImageLoaderMachO::segIsReadOnlyImport(unsigned int segIndex) const +{ + const macho_segment_command* segCmd = segLoadCommand(segIndex); + return ( (segCmd->initprot & VM_PROT_EXECUTE) + && ((segCmd->initprot & VM_PROT_WRITE) == 0) + && (strcmp(segCmd->segname, "__IMPORT") == 0) ); +} +#endif + + +void ImageLoaderMachO::UnmapSegments() +{ + // usually unmap image when done + if ( ! this->leaveMapped() && (this->getState() >= dyld_image_state_mapped) ) { + // unmap TEXT segment last because it contains load command being inspected + unsigned int textSegmentIndex = 0; + for(unsigned int i=0; i < fSegmentsCount; ++i) { + //dyld::log("unmap %s at 0x%08lX\n", seg->getName(), seg->getActualLoadAddress(this)); + if ( (segFileOffset(i) == 0) && (segFileSize(i) != 0) ) { + textSegmentIndex = i; + } + else { + // update stats + --ImageLoader::fgTotalSegmentsMapped; + ImageLoader::fgTotalBytesMapped -= segSize(i); + munmap((void*)segActualLoadAddress(i), segSize(i)); + } + } + // now unmap TEXT + --ImageLoader::fgTotalSegmentsMapped; + ImageLoader::fgTotalBytesMapped -= segSize(textSegmentIndex); + munmap((void*)segActualLoadAddress(textSegmentIndex), segSize(textSegmentIndex)); + } +} + + +// prefetch __DATA/__OBJC pages during launch, but not for dynamically loaded code +void ImageLoaderMachO::preFetchDATA(int fd, uint64_t offsetInFat, const LinkContext& context) +{ + if ( context.linkingMainExecutable ) { + for(unsigned int i=0, e=segmentCount(); i < e; ++i) { + if ( segWriteable(i) && (segFileSize(i) > 0) ) { + // prefetch writable segment that have mmap'ed regions + radvisory advice; + advice.ra_offset = offsetInFat + segFileOffset(i); + advice.ra_count = (int)segFileSize(i); + // limit prefetch to 1MB (256 pages) + if ( advice.ra_count > 1024*1024 ) + advice.ra_count = 1024*1024; + // don't prefetch single pages, let them fault in + fgTotalBytesPreFetched += advice.ra_count; + fcntl(fd, F_RDADVISE, &advice); + if ( context.verboseMapping ) { + dyld::log("%18s prefetching 0x%0lX -> 0x%0lX\n", + segName(i), segActualLoadAddress(i), segActualLoadAddress(i)+advice.ra_count-1); + } + } + } + } +} + + +bool ImageLoaderMachO::segmentsMustSlideTogether() const +{ + return true; +} + +bool ImageLoaderMachO::segmentsCanSlide() const +{ + return (this->isDylib() || this->isBundle() || this->isPositionIndependentExecutable()); +} + +bool ImageLoaderMachO::isBundle() const +{ + const macho_header* mh = (macho_header*)fMachOData; + return ( mh->filetype == MH_BUNDLE ); +} + +bool ImageLoaderMachO::isDylib() const +{ + const macho_header* mh = (macho_header*)fMachOData; + return ( mh->filetype == MH_DYLIB ); +} + +bool ImageLoaderMachO::isExecutable() const +{ + const macho_header* mh = (macho_header*)fMachOData; + return ( mh->filetype == MH_EXECUTE ); +} + +bool ImageLoaderMachO::isPositionIndependentExecutable() const +{ + const macho_header* mh = (macho_header*)fMachOData; + return ( (mh->filetype == MH_EXECUTE) && ((mh->flags & MH_PIE) != 0) ); +} + + +bool ImageLoaderMachO::forceFlat() const +{ + const macho_header* mh = (macho_header*)fMachOData; + return ( (mh->flags & MH_FORCE_FLAT) != 0 ); +} + +bool ImageLoaderMachO::usesTwoLevelNameSpace() const +{ + const macho_header* mh = (macho_header*)fMachOData; + return ( (mh->flags & MH_TWOLEVEL) != 0 ); +} + +bool ImageLoaderMachO::isPrebindable() const +{ + const macho_header* mh = (macho_header*)fMachOData; + return ( (mh->flags & MH_PREBOUND) != 0 ); +} + +bool ImageLoaderMachO::hasCoalescedExports() const +{ + const macho_header* mh = (macho_header*)fMachOData; + return ( (mh->flags & MH_WEAK_DEFINES) != 0 ); +} + +bool ImageLoaderMachO::hasReferencesToWeakSymbols() const +{ + const macho_header* mh = (macho_header*)fMachOData; + return ( (mh->flags & MH_BINDS_TO_WEAK) != 0 ); +} + +bool ImageLoaderMachO::participatesInCoalescing() const +{ + const macho_header* mh = (macho_header*)fMachOData; + // if image is loaded with RTLD_LOCAL, then its symbols' visibility + // is reduced and it can't coalesce with other images + if ( this->hasHiddenExports() ) + return false; + return ( (mh->flags & (MH_WEAK_DEFINES|MH_BINDS_TO_WEAK)) != 0 ); +} + + + +void ImageLoaderMachO::setSlide(intptr_t slide) +{ + fSlide = slide; +} + +void ImageLoaderMachO::loadCodeSignature(const struct linkedit_data_command* codeSigCmd, int fd, uint64_t offsetInFatFile, const LinkContext& context) +{ + // if dylib being loaded has no code signature load command + if ( codeSigCmd == NULL) { + if (context.requireCodeSignature ) { + // if we require dylibs to be codesigned there needs to be a signature. + dyld::throwf("required code signature missing for '%s'\n", this->getPath()); + } else { + disableCoverageCheck(); + } + } + else { +#if __MAC_OS_X_VERSION_MIN_REQUIRED + // ignore code signatures in binaries built with pre-10.9 tools + if ( this->sdkVersion() < DYLD_MACOSX_VERSION_10_9 ) { + return; + } +#endif + fsignatures_t siginfo; + siginfo.fs_file_start=offsetInFatFile; // start of mach-o slice in fat file + siginfo.fs_blob_start=(void*)(long)(codeSigCmd->dataoff); // start of CD in mach-o file + siginfo.fs_blob_size=codeSigCmd->datasize; // size of CD + int result = fcntl(fd, F_ADDFILESIGS_RETURN, &siginfo); + +#if TARGET_IPHONE_SIMULATOR + // rdar://problem/18759224> check range covered by the code directory after loading + // Attempt to fallback only if we are in the simulator + + if ( result == -1 ) { + result = fcntl(fd, F_ADDFILESIGS, &siginfo); + siginfo.fs_file_start = codeSigCmd->dataoff; + } +#endif + + if ( result == -1 ) { + if ( (errno == EPERM) || (errno == EBADEXEC) ) + dyld::throwf("code signature invalid for '%s'\n", this->getPath()); + if ( context.verboseCodeSignatures ) + dyld::log("dyld: Failed registering code signature for %s, errno=%d\n", this->getPath(), errno); + siginfo.fs_file_start = UINT64_MAX; + } else if ( context.verboseCodeSignatures ) { + dyld::log("dyld: Registered code signature for %s\n", this->getPath()); + } + fCoveredCodeLength = siginfo.fs_file_start; + +#if __MAC_OS_X_VERSION_MIN_REQUIRED + if ( context.processUsingLibraryValidation ) { + fchecklv checkInfo; + char messageBuffer[512]; + messageBuffer[0] = '\0'; + checkInfo.lv_file_start = offsetInFatFile; + checkInfo.lv_error_message_size = sizeof(messageBuffer); + checkInfo.lv_error_message = messageBuffer; + int res = fcntl(fd, F_CHECK_LV, &checkInfo); + if ( res == -1 ) { + dyld::throwf("code signature in (%s) not valid for use in process using Library Validation: %s", this->getPath(), messageBuffer); + } + } +#endif + } +} + +void ImageLoaderMachO::validateFirstPages(const struct linkedit_data_command* codeSigCmd, int fd, const uint8_t *fileData, size_t lenFileData, off_t offsetInFat, const LinkContext& context) +{ +#if __MAC_OS_X_VERSION_MIN_REQUIRED + // rdar://problem/21839703> 15A226d: dyld crashes in mageLoaderMachO::validateFirstPages during dlopen() after encountering an mmap failure + // We need to ignore older code signatures because they will be bad. + if ( this->sdkVersion() < DYLD_MACOSX_VERSION_10_9 ) { + return; + } +#endif + if (codeSigCmd != NULL) { + void *fdata = xmmap(NULL, lenFileData, PROT_READ | PROT_EXEC, MAP_SHARED, fd, offsetInFat); + if ( fdata == MAP_FAILED ) { +#if __MAC_OS_X_VERSION_MIN_REQUIRED + if ( context.processUsingLibraryValidation ) { + dyld::throwf("cannot load image with wrong team ID in process using Library Validation"); + } + else +#endif + { + int errnoCopy = errno; + if ( errnoCopy == EPERM ) { + if ( dyld::sandboxBlockedMmap(getPath()) ) + dyld::throwf("file system sandbox blocked mmap() of '%s'", getPath()); + else + dyld::throwf("code signing blocked mmap() of '%s'", getPath()); + } + else + dyld::throwf("mmap() errno=%d validating first page of '%s'", errnoCopy, getPath()); + } + } + if ( memcmp(fdata, fileData, lenFileData) != 0 ) + dyld::throwf("mmap() page compare failed for '%s'", getPath()); + munmap(fdata, lenFileData); + } +} + + +const char* ImageLoaderMachO::getInstallPath() const +{ + if ( fDylibIDOffset != 0 ) { + const dylib_command* dylibID = (dylib_command*)(&fMachOData[fDylibIDOffset]); + return (char*)dylibID + dylibID->dylib.name.offset; + } + return NULL; +} + +void ImageLoaderMachO::registerInterposing() +{ + // mach-o files advertise interposing by having a __DATA __interpose section + struct InterposeData { uintptr_t replacement; uintptr_t replacee; }; + const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds; + const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; + const struct load_command* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd) { + case LC_SEGMENT_COMMAND: + { + const struct macho_segment_command* seg = (struct macho_segment_command*)cmd; + const struct macho_section* const sectionsStart = (struct macho_section*)((char*)seg + sizeof(struct macho_segment_command)); + const struct macho_section* const sectionsEnd = §ionsStart[seg->nsects]; + for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) { + if ( ((sect->flags & SECTION_TYPE) == S_INTERPOSING) || ((strcmp(sect->sectname, "__interpose") == 0) && (strcmp(seg->segname, "__DATA") == 0)) ) { + // Ensure section is within segment + if ( (sect->addr < seg->vmaddr) || (sect->addr+sect->size > seg->vmaddr+seg->vmsize) || (sect->addr+sect->size < sect->addr) ) + dyld::throwf("interpose section has malformed address range for %s\n", this->getPath()); + const InterposeData* interposeArray = (InterposeData*)(sect->addr + fSlide); + const size_t count = sect->size / sizeof(InterposeData); + for (size_t j=0; j < count; ++j) { + ImageLoader::InterposeTuple tuple; + tuple.replacement = interposeArray[j].replacement; + tuple.neverImage = this; + tuple.onlyImage = NULL; + tuple.replacee = interposeArray[j].replacee; + // ignore interposing on a weak function that does not exist + if ( tuple.replacee == 0 ) + continue; + // verify that replacement is in this image + if ( this->containsAddress((void*)tuple.replacement) ) { + // chain to any existing interpositions + for (std::vector::iterator it=fgInterposingTuples.begin(); it != fgInterposingTuples.end(); it++) { + if ( it->replacee == tuple.replacee ) { + tuple.replacee = it->replacement; + } + } + ImageLoader::fgInterposingTuples.push_back(tuple); + } + } + } + } + } + break; + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } +} + +uint32_t ImageLoaderMachO::sdkVersion(const mach_header* mh) +{ + const uint32_t cmd_count = mh->ncmds; + const struct load_command* const cmds = (struct load_command*)(((char*)mh) + sizeof(macho_header)); + const struct load_command* cmd = cmds; + const struct version_min_command* versCmd; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch ( cmd->cmd ) { + case LC_VERSION_MIN_MACOSX: + case LC_VERSION_MIN_IPHONEOS: + case LC_VERSION_MIN_TVOS: + case LC_VERSION_MIN_WATCHOS: + versCmd = (version_min_command*)cmd; + return versCmd->sdk; + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + return 0; +} + +uint32_t ImageLoaderMachO::sdkVersion() const +{ + return ImageLoaderMachO::sdkVersion(machHeader()); +} + +uint32_t ImageLoaderMachO::minOSVersion(const mach_header* mh) +{ + const uint32_t cmd_count = mh->ncmds; + const struct load_command* const cmds = (struct load_command*)(((char*)mh) + sizeof(macho_header)); + const struct load_command* cmd = cmds; + const struct version_min_command* versCmd; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch ( cmd->cmd ) { + case LC_VERSION_MIN_MACOSX: + case LC_VERSION_MIN_IPHONEOS: + case LC_VERSION_MIN_TVOS: + case LC_VERSION_MIN_WATCHOS: + versCmd = (version_min_command*)cmd; + return versCmd->version; + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + return 0; +} + +uint32_t ImageLoaderMachO::minOSVersion() const +{ + return ImageLoaderMachO::minOSVersion(machHeader()); +} + + +void* ImageLoaderMachO::getThreadPC() const +{ + const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds; + const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; + const struct load_command* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + if ( cmd->cmd == LC_MAIN ) { + entry_point_command* mainCmd = (entry_point_command*)cmd; + void* entry = (void*)(mainCmd->entryoff + (char*)fMachOData); + // verify entry point is in image + if ( this->containsAddress(entry) ) + return entry; + else + throw "LC_MAIN entryoff is out of range"; + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + return NULL; +} + + +void* ImageLoaderMachO::getMain() const +{ + const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds; + const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; + const struct load_command* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd) { + case LC_UNIXTHREAD: + { + #if __i386__ + const i386_thread_state_t* registers = (i386_thread_state_t*)(((char*)cmd) + 16); + void* entry = (void*)(registers->eip + fSlide); + #elif __x86_64__ + const x86_thread_state64_t* registers = (x86_thread_state64_t*)(((char*)cmd) + 16); + void* entry = (void*)(registers->rip + fSlide); + #elif __arm__ + const arm_thread_state_t* registers = (arm_thread_state_t*)(((char*)cmd) + 16); + void* entry = (void*)(registers->__pc + fSlide); + #elif __arm64__ + const arm_thread_state64_t* registers = (arm_thread_state64_t*)(((char*)cmd) + 16); + void* entry = (void*)(registers->__pc + fSlide); + #else + #warning need processor specific code + #endif + // verify entry point is in image + if ( this->containsAddress(entry) ) { + return entry; + } + } + break; + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + throw "no valid entry point"; +} + +bool ImageLoaderMachO::needsAddedLibSystemDepency(unsigned int libCount, const macho_header* mh) +{ + // ensure that every image depends on something which depends on libSystem + if ( libCount > 1 ) + return false; + + // dyld implicit-libSystem breaks valgrind + if ( mh->filetype == MH_EXECUTE ) + return false; + + bool isNonOSdylib = false; + const uint32_t cmd_count = mh->ncmds; + const struct load_command* const cmds = (struct load_command*)((uint8_t*)mh+sizeof(macho_header)); + const struct load_command* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd) { + case LC_LOAD_DYLIB: + case LC_LOAD_WEAK_DYLIB: + case LC_REEXPORT_DYLIB: + case LC_LOAD_UPWARD_DYLIB: + return false; + case LC_ID_DYLIB: + { + const dylib_command* dylibID = (dylib_command*)cmd; + const char* installPath = (char*)cmd + dylibID->dylib.name.offset; + // It is OK for OS dylibs (libSystem or libmath or Rosetta shims) to have no dependents + // but all other dylibs must depend on libSystem for initialization to initialize libSystem first + // rosetta circular dependency spew + isNonOSdylib = ( (strncmp(installPath, "/usr/lib/", 9) != 0) && (strncmp(installPath, "/usr/libexec/oah/Shims", 9) != 0) ); + } + break; + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + return isNonOSdylib; +} + + +void ImageLoaderMachO::doGetDependentLibraries(DependentLibraryInfo libs[]) +{ + if ( needsAddedLibSystemDepency(libraryCount(), (macho_header*)fMachOData) ) { + DependentLibraryInfo* lib = &libs[0]; + lib->name = LIBSYSTEM_DYLIB_PATH; + lib->info.checksum = 0; + lib->info.minVersion = 0; + lib->info.maxVersion = 0; + lib->required = false; + lib->reExported = false; + lib->upward = false; + } + else { + uint32_t index = 0; + const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds; + const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; + const struct load_command* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd) { + case LC_LOAD_DYLIB: + case LC_LOAD_WEAK_DYLIB: + case LC_REEXPORT_DYLIB: + case LC_LOAD_UPWARD_DYLIB: + { + const struct dylib_command* dylib = (struct dylib_command*)cmd; + DependentLibraryInfo* lib = &libs[index++]; + lib->name = (char*)cmd + dylib->dylib.name.offset; + //lib->name = strdup((char*)cmd + dylib->dylib.name.offset); + lib->info.checksum = dylib->dylib.timestamp; + lib->info.minVersion = dylib->dylib.compatibility_version; + lib->info.maxVersion = dylib->dylib.current_version; + lib->required = (cmd->cmd != LC_LOAD_WEAK_DYLIB); + lib->reExported = (cmd->cmd == LC_REEXPORT_DYLIB); + lib->upward = (cmd->cmd == LC_LOAD_UPWARD_DYLIB); + } + break; + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + } +} + +ImageLoader::LibraryInfo ImageLoaderMachO::doGetLibraryInfo(const LibraryInfo&) +{ + LibraryInfo info; + if ( fDylibIDOffset != 0 ) { + const dylib_command* dylibID = (dylib_command*)(&fMachOData[fDylibIDOffset]); + info.minVersion = dylibID->dylib.compatibility_version; + info.maxVersion = dylibID->dylib.current_version; + info.checksum = dylibID->dylib.timestamp; + } + else { + info.minVersion = 0; + info.maxVersion = 0; + info.checksum = 0; + } + return info; +} + +void ImageLoaderMachO::getRPaths(const LinkContext& context, std::vector& paths) const +{ + const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds; + const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; + const struct load_command* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd) { + case LC_RPATH: + const char* pathToAdd = NULL; + const char* path = (char*)cmd + ((struct rpath_command*)cmd)->path.offset; + if ( (strncmp(path, "@loader_path", 12) == 0) && ((path[12] == '/') || (path[12] == '\0')) ) { +#if __MAC_OS_X_VERSION_MIN_REQUIRED + if ( context.processIsRestricted && (context.mainExecutable == this) ) { + dyld::warn("LC_RPATH %s in %s being ignored in restricted program because of @loader_path\n", path, this->getPath()); + break; + } +#endif + char resolvedPath[PATH_MAX]; + if ( realpath(this->getPath(), resolvedPath) != NULL ) { + char newRealPath[strlen(resolvedPath) + strlen(path)]; + strcpy(newRealPath, resolvedPath); + char* addPoint = strrchr(newRealPath,'/'); + if ( addPoint != NULL ) { + strcpy(addPoint, &path[12]); + pathToAdd = strdup(newRealPath); + } + } + } + else if ( (strncmp(path, "@executable_path", 16) == 0) && ((path[16] == '/') || (path[16] == '\0')) ) { +#if __MAC_OS_X_VERSION_MIN_REQUIRED + if ( context.processIsRestricted ) { + dyld::warn("LC_RPATH %s in %s being ignored in restricted program because of @executable_path\n", path, this->getPath()); + break; + } +#endif + char resolvedPath[PATH_MAX]; + if ( realpath(context.mainExecutable->getPath(), resolvedPath) != NULL ) { + char newRealPath[strlen(resolvedPath) + strlen(path)]; + strcpy(newRealPath, resolvedPath); + char* addPoint = strrchr(newRealPath,'/'); + if ( addPoint != NULL ) { + strcpy(addPoint, &path[16]); + pathToAdd = strdup(newRealPath); + } + } + } +#if __MAC_OS_X_VERSION_MIN_REQUIRED + else if ( (path[0] != '/') && context.processIsRestricted ) { + dyld::warn("LC_RPATH %s in %s being ignored in restricted program because it is a relative path\n", path, this->getPath()); + break; + } +#endif +#if SUPPORT_ROOT_PATH + else if ( (path[0] == '/') && (context.rootPaths != NULL) ) { + // DYLD_ROOT_PATH should apply to LC_RPATH rpaths + // DYLD_ROOT_PATH can be a list of paths, but at this point we can only support one, so use first combination that exists + bool found = false; + for(const char** rp = context.rootPaths; *rp != NULL; ++rp) { + char newPath[PATH_MAX]; + strlcpy(newPath, *rp, PATH_MAX); + strlcat(newPath, path, PATH_MAX); + struct stat stat_buf; + if ( stat(newPath, &stat_buf) != -1 ) { + //dyld::log("combined DYLD_ROOT_PATH and LC_RPATH: %s\n", newPath); + pathToAdd = strdup(newPath); + found = true; + break; + } + } + if ( ! found ) { + // make copy so that all elements of 'paths' can be freed + pathToAdd = strdup(path); + } + } +#endif + else { + // make copy so that all elements of 'paths' can be freed + pathToAdd = strdup(path); + } + if ( pathToAdd != NULL ) + paths.push_back(pathToAdd); + break; + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } +} + + +bool ImageLoaderMachO::getUUID(uuid_t uuid) const +{ + const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds; + const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; + const struct load_command* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd) { + case LC_UUID: + uuid_command* uc = (uuid_command*)cmd; + memcpy(uuid, uc->uuid, 16); + return true; + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + bzero(uuid, 16); + return false; +} + +void ImageLoaderMachO::doRebase(const LinkContext& context) +{ + // Delay calling setNeverUnload() until we know this is not for dlopen_preflight() + if ( fRetainForObjC ) + this->setNeverUnload(); + + // if prebound and loaded at prebound address, then no need to rebase + if ( this->usablePrebinding(context) ) { + // skip rebasing because prebinding is valid + ++fgImagesWithUsedPrebinding; // bump totals for statistics + return; + } + + // print why prebinding was not used + if ( context.verbosePrebinding ) { + if ( !this->isPrebindable() ) { + dyld::log("dyld: image not prebound, so could not use prebinding in %s\n", this->getPath()); + } + else if ( fSlide != 0 ) { + dyld::log("dyld: image slid, so could not use prebinding in %s\n", this->getPath()); + } + else if ( !this->allDependentLibrariesAsWhenPreBound() ) { + dyld::log("dyld: dependent libraries changed, so could not use prebinding in %s\n", this->getPath()); + } + else if ( !this->usesTwoLevelNameSpace() ){ + dyld::log("dyld: image uses flat-namespace so, parts of prebinding ignored %s\n", this->getPath()); + } + else { + dyld::log("dyld: environment variable disabled use of prebinding in %s\n", this->getPath()); + } + } + + //dyld::log("slide=0x%08lX for %s\n", slide, this->getPath()); + +#if PREBOUND_IMAGE_SUPPORT + // if prebound and we got here, then prebinding is not valid, so reset all lazy pointers + // if this image is in the shared cache, do not reset, they will be bound in doBind() + if ( this->isPrebindable() && !fInSharedCache ) + this->resetPreboundLazyPointers(context); +#endif + + // if loaded at preferred address, no rebasing necessary + if ( this->fSlide == 0 ) + return; + +#if TEXT_RELOC_SUPPORT + // if there are __TEXT fixups, temporarily make __TEXT writable + if ( fTextSegmentRebases ) + this->makeTextSegmentWritable(context, true); +#endif + + // do actual rebasing + this->rebase(context, fSlide); + +#if TEXT_RELOC_SUPPORT + // if there were __TEXT fixups, restore write protection + if ( fTextSegmentRebases ) + this->makeTextSegmentWritable(context, false); + +#endif +} + +#if TEXT_RELOC_SUPPORT +void ImageLoaderMachO::makeTextSegmentWritable(const LinkContext& context, bool writeable) +{ + for(unsigned int i=0; i < fSegmentsCount; ++i) { + if ( segExecutable(i) ) { + if ( writeable ) { + segMakeWritable(i, context); + } + else { + #if !__i386__ && !__x86_64__ + // some processors require range to be invalidated before it is made executable + sys_icache_invalidate((void*)segActualLoadAddress(i), segSize(textSegmentIndex)); + #endif + segProtect(i, context); + } + } + } + +} +#endif + +const ImageLoader::Symbol* ImageLoaderMachO::findExportedSymbol(const char* name, bool searchReExports, const char* thisPath, const ImageLoader** foundIn) const +{ + // look in this image first + const ImageLoader::Symbol* result = this->findShallowExportedSymbol(name, foundIn); + if ( result != NULL ) + return result; + + if ( searchReExports ) { + for(unsigned int i=0; i < libraryCount(); ++i){ + if ( libReExported(i) ) { + ImageLoader* image = libImage(i); + if ( image != NULL ) { + const char* reExPath = libPath(i); + result = image->findExportedSymbol(name, searchReExports, reExPath, foundIn); + if ( result != NULL ) + return result; + } + } + } + } + + + return NULL; +} + + + +uintptr_t ImageLoaderMachO::getExportedSymbolAddress(const Symbol* sym, const LinkContext& context, + const ImageLoader* requestor, bool runResolver, const char* symbolName) const +{ + return this->getSymbolAddress(sym, requestor, context, runResolver); +} + +uintptr_t ImageLoaderMachO::getSymbolAddress(const Symbol* sym, const ImageLoader* requestor, + const LinkContext& context, bool runResolver) const +{ + uintptr_t result = exportedSymbolAddress(context, sym, requestor, runResolver); + // check for interposing overrides + result = interposedAddress(context, result, requestor); + return result; +} + +ImageLoader::DefinitionFlags ImageLoaderMachO::getExportedSymbolInfo(const Symbol* sym) const +{ + if ( exportedSymbolIsWeakDefintion(sym) ) + return kWeakDefinition; + else + return kNoDefinitionOptions; +} + +const char* ImageLoaderMachO::getExportedSymbolName(const Symbol* sym) const +{ + return exportedSymbolName(sym); +} + +uint32_t ImageLoaderMachO::getExportedSymbolCount() const +{ + return exportedSymbolCount(); +} + + +const ImageLoader::Symbol* ImageLoaderMachO::getIndexedExportedSymbol(uint32_t index) const +{ + return exportedSymbolIndexed(index); +} + + +uint32_t ImageLoaderMachO::getImportedSymbolCount() const +{ + return importedSymbolCount(); +} + + +const ImageLoader::Symbol* ImageLoaderMachO::getIndexedImportedSymbol(uint32_t index) const +{ + return importedSymbolIndexed(index); +} + + +ImageLoader::ReferenceFlags ImageLoaderMachO::getImportedSymbolInfo(const ImageLoader::Symbol* sym) const +{ + ImageLoader::ReferenceFlags flags = kNoReferenceOptions; + return flags; +} + + +const char* ImageLoaderMachO::getImportedSymbolName(const ImageLoader::Symbol* sym) const +{ + return importedSymbolName(sym); +} + + +bool ImageLoaderMachO::getSectionContent(const char* segmentName, const char* sectionName, void** start, size_t* length) +{ + const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds; + const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; + const struct load_command* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd) { + case LC_SEGMENT_COMMAND: + { + const struct macho_segment_command* seg = (struct macho_segment_command*)cmd; + const struct macho_section* const sectionsStart = (struct macho_section*)((char*)seg + sizeof(struct macho_segment_command)); + const struct macho_section* const sectionsEnd = §ionsStart[seg->nsects]; + for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) { + if ( (strcmp(sect->segname, segmentName) == 0) && (strcmp(sect->sectname, sectionName) == 0) ) { + *start = (uintptr_t*)(sect->addr + fSlide); + *length = sect->size; + return true; + } + } + } + break; + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + *start = NULL; + *length = 0; + return false; +} + +void ImageLoaderMachO::getUnwindInfo(dyld_unwind_sections* info) +{ + info->mh = this->machHeader(); + info->dwarf_section = 0; + info->dwarf_section_length = 0; + info->compact_unwind_section = 0; + info->compact_unwind_section_length = 0; + if ( fEHFrameSectionOffset != 0 ) { + const macho_section* sect = (macho_section*)&fMachOData[fEHFrameSectionOffset]; + info->dwarf_section = (void*)(sect->addr + fSlide); + info->dwarf_section_length = sect->size; + } + if ( fUnwindInfoSectionOffset != 0 ) { + const macho_section* sect = (macho_section*)&fMachOData[fUnwindInfoSectionOffset]; + info->compact_unwind_section = (void*)(sect->addr + fSlide); + info->compact_unwind_section_length = sect->size; + } +} + +intptr_t ImageLoaderMachO::computeSlide(const mach_header* mh) +{ + const uint32_t cmd_count = mh->ncmds; + const load_command* const cmds = (load_command*)((char*)mh + sizeof(macho_header)); + const load_command* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + if ( cmd->cmd == LC_SEGMENT_COMMAND ) { + const macho_segment_command* seg = (macho_segment_command*)cmd; + if ( (seg->fileoff == 0) && (seg->filesize != 0) ) + return (char*)mh - (char*)(seg->vmaddr); + } + cmd = (const load_command*)(((char*)cmd)+cmd->cmdsize); + } + return 0; +} + +bool ImageLoaderMachO::findSection(const mach_header* mh, const char* segmentName, const char* sectionName, void** sectAddress, size_t* sectSize) +{ + const uint32_t cmd_count = mh->ncmds; + const load_command* const cmds = (load_command*)((char*)mh + sizeof(macho_header)); + const load_command* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd) { + case LC_SEGMENT_COMMAND: + { + const macho_segment_command* seg = (macho_segment_command*)cmd; + const macho_section* const sectionsStart = (macho_section*)((char*)seg + sizeof(macho_segment_command)); + const macho_section* const sectionsEnd = §ionsStart[seg->nsects]; + for (const macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) { + if ( (strcmp(sect->segname, segmentName) == 0) && (strcmp(sect->sectname, sectionName) == 0) ) { + *sectAddress = (void*)(sect->addr + computeSlide(mh)); + *sectSize = sect->size; + return true; + } + } + } + break; + } + cmd = (const load_command*)(((char*)cmd)+cmd->cmdsize); + } + return false; +} + + +bool ImageLoaderMachO::findSection(const void* imageInterior, const char** segmentName, const char** sectionName, size_t* sectionOffset) +{ + const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds; + const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; + const struct load_command* cmd = cmds; + const uintptr_t unslidInteriorAddress = (uintptr_t)imageInterior - this->getSlide(); + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd) { + case LC_SEGMENT_COMMAND: + { + const struct macho_segment_command* seg = (struct macho_segment_command*)cmd; + if ( (unslidInteriorAddress >= seg->vmaddr) && (unslidInteriorAddress < (seg->vmaddr+seg->vmsize)) ) { + const struct macho_section* const sectionsStart = (struct macho_section*)((char*)seg + sizeof(struct macho_segment_command)); + const struct macho_section* const sectionsEnd = §ionsStart[seg->nsects]; + for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) { + if ((sect->addr <= unslidInteriorAddress) && (unslidInteriorAddress < (sect->addr+sect->size))) { + if ( segmentName != NULL ) + *segmentName = sect->segname; + if ( sectionName != NULL ) + *sectionName = sect->sectname; + if ( sectionOffset != NULL ) + *sectionOffset = unslidInteriorAddress - sect->addr; + return true; + } + } + } + } + break; + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + return false; +} + +const char* ImageLoaderMachO::libPath(unsigned int index) const +{ + const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds; + const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; + const struct load_command* cmd = cmds; + unsigned count = 0; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch ( cmd->cmd ) { + case LC_LOAD_DYLIB: + case LC_LOAD_WEAK_DYLIB: + case LC_REEXPORT_DYLIB: + case LC_LOAD_UPWARD_DYLIB: + if ( index == count ) { + const struct dylib_command* dylibCmd = (struct dylib_command*)cmd; + return (char*)cmd + dylibCmd->dylib.name.offset; + } + ++count; + break; + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + + // if image linked with nothing and we implicitly added libSystem.dylib, return that + if ( needsAddedLibSystemDepency(libraryCount(), (macho_header*)fMachOData) ) { + return LIBSYSTEM_DYLIB_PATH; + } + + return NULL; +} + + +void __attribute__((noreturn)) ImageLoaderMachO::throwSymbolNotFound(const LinkContext& context, const char* symbol, + const char* referencedFrom, const char* fromVersMismatch, + const char* expectedIn) +{ + // record values for possible use by CrashReporter or Finder + (*context.setErrorStrings)(DYLD_EXIT_REASON_SYMBOL_MISSING, referencedFrom, expectedIn, symbol); + dyld::throwf("Symbol not found: %s\n Referenced from: %s%s\n Expected in: %s\n", + symbol, referencedFrom, fromVersMismatch, expectedIn); +} + +const mach_header* ImageLoaderMachO::machHeader() const +{ + return (mach_header*)fMachOData; +} + +uintptr_t ImageLoaderMachO::getSlide() const +{ + return fSlide; +} + +// hmm. maybe this should be up in ImageLoader?? +const void* ImageLoaderMachO::getEnd() const +{ + uintptr_t lastAddress = 0; + for(unsigned int i=0; i < fSegmentsCount; ++i) { + uintptr_t segEnd = segActualEndAddress(i); + if ( strcmp(segName(i), "__UNIXSTACK") != 0 ) { + if ( segEnd > lastAddress ) + lastAddress = segEnd; + } + } + return (const void*)lastAddress; +} + + +uintptr_t ImageLoaderMachO::bindLocation(const LinkContext& context, uintptr_t location, uintptr_t value, + uint8_t type, const char* symbolName, + intptr_t addend, const char* inPath, const char* toPath, const char* msg) +{ + // log + if ( context.verboseBind ) { + if ( addend != 0 ) + dyld::log("dyld: %sbind: %s:0x%08lX = %s:%s, *0x%08lX = 0x%08lX + %ld\n", + msg, shortName(inPath), (uintptr_t)location, + ((toPath != NULL) ? shortName(toPath) : ""), + symbolName, (uintptr_t)location, value, addend); + else + dyld::log("dyld: %sbind: %s:0x%08lX = %s:%s, *0x%08lX = 0x%08lX\n", + msg, shortName(inPath), (uintptr_t)location, + ((toPath != NULL) ? shortName(toPath) : ""), + symbolName, (uintptr_t)location, value); + } +#if LOG_BINDINGS +// dyld::logBindings("%s: %s\n", targetImage->getShortName(), symbolName); +#endif + + // do actual update + uintptr_t* locationToFix = (uintptr_t*)location; + uint32_t* loc32; + uintptr_t newValue = value+addend; + uint32_t value32; + switch (type) { + case BIND_TYPE_POINTER: + // test first so we don't needless dirty pages + if ( *locationToFix != newValue ) + *locationToFix = newValue; + break; + case BIND_TYPE_TEXT_ABSOLUTE32: + loc32 = (uint32_t*)locationToFix; + value32 = (uint32_t)newValue; + if ( *loc32 != value32 ) + *loc32 = value32; + break; + case BIND_TYPE_TEXT_PCREL32: + loc32 = (uint32_t*)locationToFix; + value32 = (uint32_t)(newValue - (((uintptr_t)locationToFix) + 4)); + if ( *loc32 != value32 ) + *loc32 = value32; + break; + default: + dyld::throwf("bad bind type %d", type); + } + + // update statistics + ++fgTotalBindFixups; + + return newValue; +} + + + + + +#if SUPPORT_OLD_CRT_INITIALIZATION +// first 16 bytes of "start" in crt1.o +#if __i386__ + static uint8_t sStandardEntryPointInstructions[16] = { 0x6a, 0x00, 0x89, 0xe5, 0x83, 0xe4, 0xf0, 0x83, 0xec, 0x10, 0x8b, 0x5d, 0x04, 0x89, 0x5c, 0x24 }; +#endif +#endif + +struct DATAdyld { + void* dyldLazyBinder; // filled in at launch by dyld to point into dyld to &stub_binding_helper + void* dyldFuncLookup; // filled in at launch by dyld to point into dyld to &_dyld_func_lookup + // the following only exist in main executables built for 10.5 or later + ProgramVars vars; +}; + +// These are defined in dyldStartup.s +extern "C" void stub_binding_helper(); + + +void ImageLoaderMachO::setupLazyPointerHandler(const LinkContext& context) +{ + const macho_header* mh = (macho_header*)fMachOData; + const uint32_t cmd_count = mh->ncmds; + const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; + const struct load_command* cmd; + // There used to be some optimizations to skip this section scan, but we need to handle the + // __dyld section in libdyld.dylib, so everything needs to be scanned for now. + // CrashTracer: 1,295 crashes in bash at bash: getenv + if ( true ) { + cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + if ( cmd->cmd == LC_SEGMENT_COMMAND ) { + const struct macho_segment_command* seg = (struct macho_segment_command*)cmd; + if ( strncmp(seg->segname, "__DATA", 6) == 0 ) { + const struct macho_section* const sectionsStart = (struct macho_section*)((char*)seg + sizeof(struct macho_segment_command)); + const struct macho_section* const sectionsEnd = §ionsStart[seg->nsects]; + for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) { + if ( strcmp(sect->sectname, "__dyld" ) == 0 ) { + struct DATAdyld* dd = (struct DATAdyld*)(sect->addr + fSlide); + #if !__arm64__ && !__ARM_ARCH_7K__ + if ( sect->size > offsetof(DATAdyld, dyldLazyBinder) ) { + if ( dd->dyldLazyBinder != (void*)&stub_binding_helper ) + dd->dyldLazyBinder = (void*)&stub_binding_helper; + } + #endif // !__arm64__ + if ( sect->size > offsetof(DATAdyld, dyldFuncLookup) ) { + if ( dd->dyldFuncLookup != (void*)&_dyld_func_lookup ) + dd->dyldFuncLookup = (void*)&_dyld_func_lookup; + } + if ( mh->filetype == MH_EXECUTE ) { + // there are two ways to get the program variables + if ( (sect->size > offsetof(DATAdyld, vars)) && (dd->vars.mh == mh) ) { + // some really old binaries have space for vars, but it is zero filled + // main executable has 10.5 style __dyld section that has program variable pointers + context.setNewProgramVars(dd->vars); + } + else { + // main executable is pre-10.5 and requires the symbols names to be looked up + this->lookupProgramVars(context); + #if SUPPORT_OLD_CRT_INITIALIZATION + // If the first 16 bytes of the entry point's instructions do not + // match what crt1.o supplies, then the program has a custom entry point. + // This means it might be doing something that needs to be executed before + // initializers are run. + if ( memcmp(this->getMain(), sStandardEntryPointInstructions, 16) != 0 ) { + if ( context.verboseInit ) + dyld::log("dyld: program uses non-standard entry point so delaying running of initializers\n"); + context.setRunInitialzersOldWay(); + } + #endif + } + } + else if ( mh->filetype == MH_DYLIB ) { + const char* installPath = this->getInstallPath(); + if ( (installPath != NULL) && (strncmp(installPath, "/usr/lib/", 9) == 0) ) { + if ( sect->size > offsetof(DATAdyld, vars) ) { + // use ProgramVars from libdyld.dylib but tweak mh field to correct value + dd->vars.mh = context.mainExecutable->machHeader(); + context.setNewProgramVars(dd->vars); + } + } + } + } + else if ( (strcmp(sect->sectname, "__program_vars" ) == 0) && (mh->filetype == MH_EXECUTE) ) { + // this is a Mac OS X 10.6 or later main executable + struct ProgramVars* pv = (struct ProgramVars*)(sect->addr + fSlide); + context.setNewProgramVars(*pv); + } + } + } + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + } +} + + +void ImageLoaderMachO::lookupProgramVars(const LinkContext& context) const +{ + ProgramVars vars = context.programVars; + const ImageLoader::Symbol* sym; + + // get mach header directly + vars.mh = (macho_header*)fMachOData; + + // lookup _NXArgc + sym = this->findShallowExportedSymbol("_NXArgc", NULL); + if ( sym != NULL ) + vars.NXArgcPtr = (int*)this->getExportedSymbolAddress(sym, context, this, false, NULL); + + // lookup _NXArgv + sym = this->findShallowExportedSymbol("_NXArgv", NULL); + if ( sym != NULL ) + vars.NXArgvPtr = (const char***)this->getExportedSymbolAddress(sym, context, this, false, NULL); + + // lookup _environ + sym = this->findShallowExportedSymbol("_environ", NULL); + if ( sym != NULL ) + vars.environPtr = (const char***)this->getExportedSymbolAddress(sym, context, this, false, NULL); + + // lookup __progname + sym = this->findShallowExportedSymbol("___progname", NULL); + if ( sym != NULL ) + vars.__prognamePtr = (const char**)this->getExportedSymbolAddress(sym, context, this, false, NULL); + + context.setNewProgramVars(vars); +} + + +bool ImageLoaderMachO::usablePrebinding(const LinkContext& context) const +{ + // if prebound and loaded at prebound address, and all libraries are same as when this was prebound, then no need to bind + if ( ((this->isPrebindable() && (this->getSlide() == 0)) || fInSharedCache) + && this->usesTwoLevelNameSpace() + && this->allDependentLibrariesAsWhenPreBound() ) { + // allow environment variables to disable prebinding + if ( context.bindFlat ) + return false; + switch ( context.prebindUsage ) { + case kUseAllPrebinding: + return true; + case kUseSplitSegPrebinding: + return this->fIsSplitSeg; + case kUseAllButAppPredbinding: + return (this != context.mainExecutable); + case kUseNoPrebinding: + return false; + } + } + return false; +} + + +void ImageLoaderMachO::doImageInit(const LinkContext& context) +{ + if ( fHasDashInit ) { + const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds; + const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; + const struct load_command* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd) { + case LC_ROUTINES_COMMAND: + Initializer func = (Initializer)(((struct macho_routines_command*)cmd)->init_address + fSlide); + // verify initializers are in image + if ( ! this->containsAddress((void*)func) ) { + dyld::throwf("initializer function %p not in mapped image for %s\n", func, this->getPath()); + } + if ( ! dyld::gProcessInfo->libSystemInitialized ) { + // libSystem initializer must run first + dyld::throwf("-init function in image (%s) that does not link with libSystem.dylib\n", this->getPath()); + } + if ( context.verboseInit ) + dyld::log("dyld: calling -init function %p in %s\n", func, this->getPath()); + func(context.argc, context.argv, context.envp, context.apple, &context.programVars); + break; + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + } +} + +void ImageLoaderMachO::doModInitFunctions(const LinkContext& context) +{ + if ( fHasInitializers ) { + const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds; + const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; + const struct load_command* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + if ( cmd->cmd == LC_SEGMENT_COMMAND ) { + const struct macho_segment_command* seg = (struct macho_segment_command*)cmd; + const struct macho_section* const sectionsStart = (struct macho_section*)((char*)seg + sizeof(struct macho_segment_command)); + const struct macho_section* const sectionsEnd = §ionsStart[seg->nsects]; + for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) { + const uint8_t type = sect->flags & SECTION_TYPE; + if ( type == S_MOD_INIT_FUNC_POINTERS ) { + Initializer* inits = (Initializer*)(sect->addr + fSlide); + const size_t count = sect->size / sizeof(uintptr_t); + // Ensure __mod_init_func section is within segment + if ( (sect->addr < seg->vmaddr) || (sect->addr+sect->size > seg->vmaddr+seg->vmsize) || (sect->addr+sect->size < sect->addr) ) + dyld::throwf("__mod_init_funcs section has malformed address range for %s\n", this->getPath()); + for (size_t j=0; j < count; ++j) { + Initializer func = inits[j]; + // verify initializers are in image + if ( ! this->containsAddress((void*)func) ) { + dyld::throwf("initializer function %p not in mapped image for %s\n", func, this->getPath()); + } + if ( ! dyld::gProcessInfo->libSystemInitialized ) { + // libSystem initializer must run first + const char* installPath = getInstallPath(); + if ( (installPath == NULL) || (strcmp(installPath, LIBSYSTEM_DYLIB_PATH) != 0) ) + dyld::throwf("initializer in image (%s) that does not link with libSystem.dylib\n", this->getPath()); + } + if ( context.verboseInit ) + dyld::log("dyld: calling initializer function %p in %s\n", func, this->getPath()); + bool haveLibSystemHelpersBefore = (dyld::gLibSystemHelpers != NULL); + func(context.argc, context.argv, context.envp, context.apple, &context.programVars); + bool haveLibSystemHelpersAfter = (dyld::gLibSystemHelpers != NULL); + if ( !haveLibSystemHelpersBefore && haveLibSystemHelpersAfter ) { + // now safe to use malloc() and other calls in libSystem.dylib + dyld::gProcessInfo->libSystemInitialized = true; + } + } + } + } + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + } +} + + + +void ImageLoaderMachO::doGetDOFSections(const LinkContext& context, std::vector& dofs) +{ + if ( fHasDOFSections ) { + // walk load commands (mapped in at start of __TEXT segment) + const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds; + const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; + const struct load_command* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd) { + case LC_SEGMENT_COMMAND: + { + const struct macho_segment_command* seg = (struct macho_segment_command*)cmd; + const struct macho_section* const sectionsStart = (struct macho_section*)((char*)seg + sizeof(struct macho_segment_command)); + const struct macho_section* const sectionsEnd = §ionsStart[seg->nsects]; + for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) { + if ( (sect->flags & SECTION_TYPE) == S_DTRACE_DOF ) { + // Ensure section is within segment + if ( (sect->addr < seg->vmaddr) || (sect->addr+sect->size > seg->vmaddr+seg->vmsize) || (sect->addr+sect->size < sect->addr) ) + dyld::throwf("DOF section has malformed address range for %s\n", this->getPath()); + ImageLoader::DOFInfo info; + info.dof = (void*)(sect->addr + fSlide); + info.imageHeader = this->machHeader(); + info.imageShortName = this->getShortName(); + dofs.push_back(info); + } + } + } + break; + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + } +} + + +bool ImageLoaderMachO::doInitialization(const LinkContext& context) +{ + CRSetCrashLogMessage2(this->getPath()); + + // mach-o has -init and static initializers + doImageInit(context); + doModInitFunctions(context); + + CRSetCrashLogMessage2(NULL); + + return (fHasDashInit || fHasInitializers); +} + +bool ImageLoaderMachO::needsInitialization() +{ + return ( fHasDashInit || fHasInitializers ); +} + + +bool ImageLoaderMachO::needsTermination() +{ + return fHasTerminators; +} + + +void ImageLoaderMachO::doTermination(const LinkContext& context) +{ + if ( fHasTerminators ) { + const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds; + const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; + const struct load_command* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + if ( cmd->cmd == LC_SEGMENT_COMMAND ) { + const struct macho_segment_command* seg = (struct macho_segment_command*)cmd; + const struct macho_section* const sectionsStart = (struct macho_section*)((char*)seg + sizeof(struct macho_segment_command)); + const struct macho_section* const sectionsEnd = §ionsStart[seg->nsects]; + for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) { + const uint8_t type = sect->flags & SECTION_TYPE; + if ( type == S_MOD_TERM_FUNC_POINTERS ) { + // Ensure section is within segment + if ( (sect->addr < seg->vmaddr) || (sect->addr+sect->size > seg->vmaddr+seg->vmsize) || (sect->addr+sect->size < sect->addr) ) + dyld::throwf("DOF section has malformed address range for %s\n", this->getPath()); + Terminator* terms = (Terminator*)(sect->addr + fSlide); + const size_t count = sect->size / sizeof(uintptr_t); + for (size_t j=count; j > 0; --j) { + Terminator func = terms[j-1]; + // verify terminators are in image + if ( ! this->containsAddress((void*)func) ) { + dyld::throwf("termination function %p not in mapped image for %s\n", func, this->getPath()); + } + if ( context.verboseInit ) + dyld::log("dyld: calling termination function %p in %s\n", func, this->getPath()); + func(); + } + } + } + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + } +} + + +void ImageLoaderMachO::printStatisticsDetails(unsigned int imageCount, const InitializerTimingList& timingInfo) +{ + ImageLoader::printStatisticsDetails(imageCount, timingInfo); + dyld::log("total symbol trie searches: %d\n", fgSymbolTrieSearchs); + dyld::log("total symbol table binary searches: %d\n", fgSymbolTableBinarySearchs); + dyld::log("total images defining weak symbols: %u\n", fgImagesHasWeakDefinitions); + dyld::log("total images using weak symbols: %u\n", fgImagesRequiringCoalescing); +} + + +intptr_t ImageLoaderMachO::assignSegmentAddresses(const LinkContext& context) +{ + // preflight and calculate slide if needed + const bool inPIE = (fgNextPIEDylibAddress != 0); + intptr_t slide = 0; + if ( this->segmentsCanSlide() && this->segmentsMustSlideTogether() ) { + bool needsToSlide = false; + bool imageHasPreferredLoadAddress = segHasPreferredLoadAddress(0); + uintptr_t lowAddr = (unsigned long)(-1); + uintptr_t highAddr = 0; + for(unsigned int i=0, e=segmentCount(); i < e; ++i) { + const uintptr_t segLow = segPreferredLoadAddress(i); + const uintptr_t segHigh = dyld_page_round(segLow + segSize(i)); + if ( segLow < highAddr ) { + if ( dyld_page_size > 4096 ) + dyld::throwf("can't map segments into 16KB pages"); + else + dyld::throwf("overlapping segments"); + } + if ( segLow < lowAddr ) + lowAddr = segLow; + if ( segHigh > highAddr ) + highAddr = segHigh; + + if ( needsToSlide || !imageHasPreferredLoadAddress || inPIE || !reserveAddressRange(segPreferredLoadAddress(i), segSize(i)) ) + needsToSlide = true; + } + if ( needsToSlide ) { + // find a chunk of address space to hold all segments + uintptr_t addr = reserveAnAddressRange(highAddr-lowAddr, context); + slide = addr - lowAddr; + } + } + else if ( ! this->segmentsCanSlide() ) { + for(unsigned int i=0, e=segmentCount(); i < e; ++i) { + if ( (strcmp(segName(i), "__PAGEZERO") == 0) && (segFileSize(i) == 0) && (segPreferredLoadAddress(i) == 0) ) + continue; + if ( !reserveAddressRange(segPreferredLoadAddress(i), segSize(i)) ) + dyld::throwf("can't map unslidable segment %s to 0x%lX with size 0x%lX", segName(i), segPreferredLoadAddress(i), segSize(i)); + } + } + else { + throw "mach-o does not support independently sliding segments"; + } + return slide; +} + + +uintptr_t ImageLoaderMachO::reserveAnAddressRange(size_t length, const ImageLoader::LinkContext& context) +{ + vm_address_t addr = 0; + vm_size_t size = length; + // in PIE programs, load initial dylibs after main executable so they don't have fixed addresses either + if ( fgNextPIEDylibAddress != 0 ) { + // add small (0-3 pages) random padding between dylibs + addr = fgNextPIEDylibAddress + (__stack_chk_guard/fgNextPIEDylibAddress & (sizeof(long)-1))*dyld_page_size; + //dyld::log("padding 0x%08llX, guard=0x%08llX\n", (long long)(addr - fgNextPIEDylibAddress), (long long)(__stack_chk_guard)); + kern_return_t r = vm_alloc(&addr, size, VM_FLAGS_FIXED | VM_MAKE_TAG(VM_MEMORY_DYLIB)); + if ( r == KERN_SUCCESS ) { + fgNextPIEDylibAddress = addr + size; + return addr; + } + fgNextPIEDylibAddress = 0; + } + kern_return_t r = vm_alloc(&addr, size, VM_FLAGS_ANYWHERE | VM_MAKE_TAG(VM_MEMORY_DYLIB)); + if ( r != KERN_SUCCESS ) + throw "out of address space"; + + return addr; +} + +bool ImageLoaderMachO::reserveAddressRange(uintptr_t start, size_t length) +{ + vm_address_t addr = start; + vm_size_t size = length; + kern_return_t r = vm_alloc(&addr, size, VM_FLAGS_FIXED | VM_MAKE_TAG(VM_MEMORY_DYLIB)); + if ( r != KERN_SUCCESS ) + return false; + return true; +} + + + +void ImageLoaderMachO::mapSegments(int fd, uint64_t offsetInFat, uint64_t lenInFat, uint64_t fileLen, const LinkContext& context) +{ + // find address range for image + intptr_t slide = this->assignSegmentAddresses(context); + if ( context.verboseMapping ) { + if ( offsetInFat != 0 ) + dyld::log("dyld: Mapping %s (slice offset=%llu)\n", this->getPath(), (unsigned long long)offsetInFat); + else + dyld::log("dyld: Mapping %s\n", this->getPath()); + } + // map in all segments + for(unsigned int i=0, e=segmentCount(); i < e; ++i) { + vm_offset_t fileOffset = segFileOffset(i) + offsetInFat; + vm_size_t size = segFileSize(i); + uintptr_t requestedLoadAddress = segPreferredLoadAddress(i) + slide; + int protection = 0; + if ( !segUnaccessible(i) ) { + // If has text-relocs, don't set x-bit initially. + // Instead set it later after text-relocs have been done. + if ( segExecutable(i) && !(segHasRebaseFixUps(i) && (slide != 0)) ) + protection |= PROT_EXEC; + if ( segReadable(i) ) + protection |= PROT_READ; + if ( segWriteable(i) ) { + protection |= PROT_WRITE; + // rdar://problem/22525618 force __LINKEDIT to always be mapped read-only + if ( strcmp(segName(i), "__LINKEDIT") == 0 ) + protection = PROT_READ; + } + } + #if __i386__ + // initially map __IMPORT segments R/W so dyld can update them + if ( segIsReadOnlyImport(i) ) + protection |= PROT_WRITE; + #endif + // wholly zero-fill segments have nothing to mmap() in + if ( size > 0 ) { + if ( (fileOffset+size) > fileLen ) { + dyld::throwf("truncated mach-o error: segment %s extends to %llu which is past end of file %llu", + segName(i), (uint64_t)(fileOffset+size), fileLen); + } + void* loadAddress = xmmap((void*)requestedLoadAddress, size, protection, MAP_FIXED | MAP_PRIVATE, fd, fileOffset); + if ( loadAddress == ((void*)(-1)) ) { + int mmapErr = errno; + if ( mmapErr == EPERM ) { + if ( dyld::sandboxBlockedMmap(getPath()) ) + dyld::throwf("file system sandbox blocked mmap() of '%s'", this->getPath()); + else + dyld::throwf("code signing blocked mmap() of '%s'", this->getPath()); + } + else + dyld::throwf("mmap() errno=%d at address=0x%08lX, size=0x%08lX segment=%s in Segment::map() mapping %s", + mmapErr, requestedLoadAddress, (uintptr_t)size, segName(i), getPath()); + } + } + // update stats + ++ImageLoader::fgTotalSegmentsMapped; + ImageLoader::fgTotalBytesMapped += size; + if ( context.verboseMapping ) + dyld::log("%18s at 0x%08lX->0x%08lX with permissions %c%c%c\n", segName(i), requestedLoadAddress, requestedLoadAddress+size-1, + (protection & PROT_READ) ? 'r' : '.', (protection & PROT_WRITE) ? 'w' : '.', (protection & PROT_EXEC) ? 'x' : '.' ); + } + + // update slide to reflect load location + this->setSlide(slide); +} + +void ImageLoaderMachO::mapSegments(const void* memoryImage, uint64_t imageLen, const LinkContext& context) +{ + // find address range for image + intptr_t slide = this->assignSegmentAddresses(context); + if ( context.verboseMapping ) + dyld::log("dyld: Mapping memory %p\n", memoryImage); + // map in all segments + for(unsigned int i=0, e=segmentCount(); i < e; ++i) { + vm_address_t loadAddress = segPreferredLoadAddress(i) + slide; + vm_address_t srcAddr = (uintptr_t)memoryImage + segFileOffset(i); + vm_size_t size = segFileSize(i); + kern_return_t r = vm_copy(mach_task_self(), srcAddr, size, loadAddress); + if ( r != KERN_SUCCESS ) + throw "can't map segment"; + if ( context.verboseMapping ) + dyld::log("%18s at 0x%08lX->0x%08lX\n", segName(i), (uintptr_t)loadAddress, (uintptr_t)loadAddress+size-1); + } + // update slide to reflect load location + this->setSlide(slide); + // set R/W permissions on all segments at slide location + for(unsigned int i=0, e=segmentCount(); i < e; ++i) { + segProtect(i, context); + } +} + + +void ImageLoaderMachO::segProtect(unsigned int segIndex, const ImageLoader::LinkContext& context) +{ + vm_prot_t protection = 0; + if ( !segUnaccessible(segIndex) ) { + if ( segExecutable(segIndex) ) + protection |= PROT_EXEC; + if ( segReadable(segIndex) ) + protection |= PROT_READ; + if ( segWriteable(segIndex) ) + protection |= PROT_WRITE; + } + vm_address_t addr = segActualLoadAddress(segIndex); + vm_size_t size = segSize(segIndex); + const bool setCurrentPermissions = false; + kern_return_t r = vm_protect(mach_task_self(), addr, size, setCurrentPermissions, protection); + if ( r != KERN_SUCCESS ) { + dyld::throwf("vm_protect(0x%08llX, 0x%08llX, false, 0x%02X) failed, result=%d for segment %s in %s", + (long long)addr, (long long)size, protection, r, segName(segIndex), this->getPath()); + } + if ( context.verboseMapping ) { + dyld::log("%18s at 0x%08lX->0x%08lX altered permissions to %c%c%c\n", segName(segIndex), (uintptr_t)addr, (uintptr_t)addr+size-1, + (protection & PROT_READ) ? 'r' : '.', (protection & PROT_WRITE) ? 'w' : '.', (protection & PROT_EXEC) ? 'x' : '.' ); + } +} + +void ImageLoaderMachO::segMakeWritable(unsigned int segIndex, const ImageLoader::LinkContext& context) +{ + vm_address_t addr = segActualLoadAddress(segIndex); + vm_size_t size = segSize(segIndex); + const bool setCurrentPermissions = false; + vm_prot_t protection = VM_PROT_WRITE | VM_PROT_READ; + if ( segExecutable(segIndex) && !segHasRebaseFixUps(segIndex) ) + protection |= VM_PROT_EXECUTE; + kern_return_t r = vm_protect(mach_task_self(), addr, size, setCurrentPermissions, protection); + if ( r != KERN_SUCCESS ) { + dyld::throwf("vm_protect(0x%08llX, 0x%08llX, false, 0x%02X) failed, result=%d for segment %s in %s", + (long long)addr, (long long)size, protection, r, segName(segIndex), this->getPath()); + } + if ( context.verboseMapping ) { + dyld::log("%18s at 0x%08lX->0x%08lX altered permissions to %c%c%c\n", segName(segIndex), (uintptr_t)addr, (uintptr_t)addr+size-1, + (protection & PROT_READ) ? 'r' : '.', (protection & PROT_WRITE) ? 'w' : '.', (protection & PROT_EXEC) ? 'x' : '.' ); + } +} + + +const char* ImageLoaderMachO::findClosestSymbol(const mach_header* mh, const void* addr, const void** closestAddr) +{ + // called by dladdr() + // only works with compressed LINKEDIT if classic symbol table is also present + const dysymtab_command* dynSymbolTable = NULL; + const symtab_command* symtab = NULL; + const macho_segment_command* seg; + const uint8_t* unslidLinkEditBase = NULL; + bool linkEditBaseFound = false; + intptr_t slide = 0; + const uint32_t cmd_count = mh->ncmds; + const load_command* const cmds = (load_command*)((char*)mh + sizeof(macho_header)); + const load_command* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd) { + case LC_SEGMENT_COMMAND: + seg = (macho_segment_command*)cmd; + if ( strcmp(seg->segname, "__LINKEDIT") == 0 ) { + unslidLinkEditBase = (uint8_t*)(seg->vmaddr - seg->fileoff); + linkEditBaseFound = true; + } + else if ( (seg->fileoff == 0) && (seg->filesize != 0) ) + slide = (uintptr_t)mh - seg->vmaddr; + break; + case LC_SYMTAB: + symtab = (symtab_command*)cmd; + break; + case LC_DYSYMTAB: + dynSymbolTable = (dysymtab_command*)cmd; + break; + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + // no symbol table => no lookup by address + if ( (symtab == NULL) || (dynSymbolTable == NULL) || !linkEditBaseFound ) + return NULL; + + const uint8_t* linkEditBase = unslidLinkEditBase + slide; + const char* symbolTableStrings = (const char*)&linkEditBase[symtab->stroff]; + const macho_nlist* symbolTable = (macho_nlist*)(&linkEditBase[symtab->symoff]); + + uintptr_t targetAddress = (uintptr_t)addr - slide; + const struct macho_nlist* bestSymbol = NULL; + // first walk all global symbols + const struct macho_nlist* const globalsStart = &symbolTable[dynSymbolTable->iextdefsym]; + const struct macho_nlist* const globalsEnd= &globalsStart[dynSymbolTable->nextdefsym]; + for (const struct macho_nlist* s = globalsStart; s < globalsEnd; ++s) { + if ( (s->n_type & N_TYPE) == N_SECT ) { + if ( bestSymbol == NULL ) { + if ( s->n_value <= targetAddress ) + bestSymbol = s; + } + else if ( (s->n_value <= targetAddress) && (bestSymbol->n_value < s->n_value) ) { + bestSymbol = s; + } + } + } + // next walk all local symbols + const struct macho_nlist* const localsStart = &symbolTable[dynSymbolTable->ilocalsym]; + const struct macho_nlist* const localsEnd= &localsStart[dynSymbolTable->nlocalsym]; + for (const struct macho_nlist* s = localsStart; s < localsEnd; ++s) { + if ( ((s->n_type & N_TYPE) == N_SECT) && ((s->n_type & N_STAB) == 0) ) { + if ( bestSymbol == NULL ) { + if ( s->n_value <= targetAddress ) + bestSymbol = s; + } + else if ( (s->n_value <= targetAddress) && (bestSymbol->n_value < s->n_value) ) { + bestSymbol = s; + } + } + } + if ( bestSymbol != NULL ) { +#if __arm__ + if (bestSymbol->n_desc & N_ARM_THUMB_DEF) + *closestAddr = (void*)((bestSymbol->n_value | 1) + slide); + else + *closestAddr = (void*)(bestSymbol->n_value + slide); +#else + *closestAddr = (void*)(bestSymbol->n_value + slide); +#endif + return &symbolTableStrings[bestSymbol->n_un.n_strx]; + } + return NULL; +} + +bool ImageLoaderMachO::getLazyBindingInfo(uint32_t& lazyBindingInfoOffset, const uint8_t* lazyInfoStart, const uint8_t* lazyInfoEnd, + uint8_t* segIndex, uintptr_t* segOffset, int* ordinal, const char** symbolName, bool* doneAfterBind) +{ + if ( lazyBindingInfoOffset > (lazyInfoEnd-lazyInfoStart) ) + return false; + uint8_t type = BIND_TYPE_POINTER; + uint8_t symboFlags = 0; + bool done = false; + const uint8_t* p = &lazyInfoStart[lazyBindingInfoOffset]; + while ( !done && (p < lazyInfoEnd) ) { + uint8_t immediate = *p & BIND_IMMEDIATE_MASK; + uint8_t opcode = *p & BIND_OPCODE_MASK; + ++p; + switch (opcode) { + case BIND_OPCODE_DONE: + *doneAfterBind = false; + return true; + break; + case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM: + *ordinal = immediate; + break; + case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB: + *ordinal = (int)read_uleb128(p, lazyInfoEnd); + break; + case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM: + // the special ordinals are negative numbers + if ( immediate == 0 ) + *ordinal = 0; + else { + int8_t signExtended = BIND_OPCODE_MASK | immediate; + *ordinal = signExtended; + } + break; + case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: + *symbolName = (char*)p; + symboFlags = immediate; + while (*p != '\0') + ++p; + ++p; + break; + case BIND_OPCODE_SET_TYPE_IMM: + type = immediate; + break; + case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: + *segIndex = immediate; + *segOffset = read_uleb128(p, lazyInfoEnd); + break; + case BIND_OPCODE_DO_BIND: + *doneAfterBind = ((*p & BIND_OPCODE_MASK) == BIND_OPCODE_DONE); + lazyBindingInfoOffset += p - &lazyInfoStart[lazyBindingInfoOffset]; + return true; + break; + case BIND_OPCODE_SET_ADDEND_SLEB: + case BIND_OPCODE_ADD_ADDR_ULEB: + case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: + case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: + case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: + default: + return false; + } + } + return false; +} + +const dyld_info_command* ImageLoaderMachO::findDyldInfoLoadCommand(const mach_header* mh) +{ + const uint32_t cmd_count = mh->ncmds; + const load_command* const cmds = (load_command*)((char*)mh + sizeof(macho_header)); + const load_command* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd) { + case LC_DYLD_INFO: + case LC_DYLD_INFO_ONLY: + return (dyld_info_command*)cmd; + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + return NULL; +} + + +uintptr_t ImageLoaderMachO::segPreferredAddress(const mach_header* mh, unsigned segIndex) +{ + const uint32_t cmd_count = mh->ncmds; + const load_command* const cmds = (load_command*)((char*)mh + sizeof(macho_header)); + const load_command* cmd = cmds; + unsigned curSegIndex = 0; + for (uint32_t i = 0; i < cmd_count; ++i) { + if ( cmd->cmd == LC_SEGMENT_COMMAND ) { + if ( segIndex == curSegIndex ) { + const macho_segment_command* segCmd = (macho_segment_command*)cmd; + return segCmd->vmaddr; + } + ++curSegIndex; + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + return 0; +} + + + diff --git a/src/dyld-apple/src/ImageLoaderMachO.h b/src/dyld-apple/src/ImageLoaderMachO.h new file mode 100644 index 000000000..5c485892f --- /dev/null +++ b/src/dyld-apple/src/ImageLoaderMachO.h @@ -0,0 +1,253 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2004-2009 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@ + */ + + +#ifndef __IMAGELOADERMACHO__ +#define __IMAGELOADERMACHO__ + +#include +#include +#include + +#include "ImageLoader.h" +#include "mach-o/dyld_images.h" + + +// +// ImageLoaderMachO is a subclass of ImageLoader which loads mach-o format files. +// +// +class ImageLoaderMachO : public ImageLoader { +public: + static ImageLoader* instantiateMainExecutable(const macho_header* mh, uintptr_t slide, const char* path, const LinkContext& context); + static ImageLoader* instantiateFromFile(const char* path, int fd, const uint8_t firstPages[], size_t firstPagesSize, uint64_t offsetInFat, + uint64_t lenInFat, const struct stat& info, const LinkContext& context); + static ImageLoader* instantiateFromCache(const macho_header* mh, const char* path, long slide, const struct stat& info, const LinkContext& context); + static ImageLoader* instantiateFromMemory(const char* moduleName, const macho_header* mh, uint64_t len, const LinkContext& context); + + + bool inSharedCache() const { return fInSharedCache; } + void disableCoverageCheck() { fCoveredCodeLength = UINT64_MAX; } + + const char* getInstallPath() const; + virtual void* getMain() const; + virtual void* getThreadPC() const; + virtual const struct mach_header* machHeader() const; + virtual uintptr_t getSlide() const; + virtual const void* getEnd() const; + virtual bool hasCoalescedExports() const; + virtual const Symbol* findExportedSymbol(const char* name, bool searchReExports, const char* thisPath, const ImageLoader** foundIn) const; + virtual uintptr_t getExportedSymbolAddress(const Symbol* sym, const LinkContext& context, + const ImageLoader* requestor, bool runResolver, const char*) const; + virtual DefinitionFlags getExportedSymbolInfo(const Symbol* sym) const; + virtual const char* getExportedSymbolName(const Symbol* sym) const; + virtual uint32_t getExportedSymbolCount() const; + virtual const Symbol* getIndexedExportedSymbol(uint32_t index) const; + virtual uint32_t getImportedSymbolCount() const; + virtual const Symbol* getIndexedImportedSymbol(uint32_t index) const; + virtual ReferenceFlags getImportedSymbolInfo(const Symbol* sym) const; + virtual const char* getImportedSymbolName(const Symbol* sym) const; + virtual bool isBundle() const; + virtual bool isDylib() const; + virtual bool isExecutable() const; + virtual bool isPositionIndependentExecutable() const; + virtual bool forceFlat() const; + virtual bool participatesInCoalescing() const; + virtual const char* findClosestSymbol(const void* addr, const void** closestAddr) const = 0; + virtual void initializeCoalIterator(CoalIterator&, unsigned int loadOrder, unsigned) = 0; + virtual bool incrementCoalIterator(CoalIterator&) = 0; + virtual uintptr_t getAddressCoalIterator(CoalIterator&, const LinkContext& contex) = 0; + virtual void updateUsesCoalIterator(CoalIterator&, uintptr_t newAddr, ImageLoader* target, unsigned targetIndex, const LinkContext& context) = 0; + virtual uintptr_t doBindLazySymbol(uintptr_t* lazyPointer, const LinkContext& context) = 0; + virtual uintptr_t doBindFastLazySymbol(uint32_t lazyBindingInfoOffset, const LinkContext& context, void (*lock)(), void (*unlock)()) = 0; + virtual void doTermination(const LinkContext& context); + virtual bool needsInitialization(); + virtual bool getSectionContent(const char* segmentName, const char* sectionName, void** start, size_t* length); + virtual void getUnwindInfo(dyld_unwind_sections* info); + virtual bool findSection(const void* imageInterior, const char** segmentName, const char** sectionName, size_t* sectionOffset); + virtual bool usablePrebinding(const LinkContext& context) const; + virtual unsigned int segmentCount() const; + virtual const char* segName(unsigned int) const; + virtual uintptr_t segSize(unsigned int) const; + virtual uintptr_t segFileSize(unsigned int) const; + virtual bool segHasTrailingZeroFill(unsigned int); + virtual uintptr_t segFileOffset(unsigned int) const; + virtual bool segReadable(unsigned int) const; + virtual bool segWriteable(unsigned int) const; + virtual bool segExecutable(unsigned int) const; + virtual bool segUnaccessible(unsigned int) const; + virtual bool segHasPreferredLoadAddress(unsigned int) const; + virtual uintptr_t segActualLoadAddress(unsigned int) const; + virtual uintptr_t segPreferredLoadAddress(unsigned int) const; + virtual uintptr_t segActualEndAddress(unsigned int) const; + virtual void registerInterposing(); + virtual uint32_t sdkVersion() const; + virtual uint32_t minOSVersion() const; + virtual const char* libPath(unsigned int) const; + virtual bool notifyObjC() const { return fNotifyObjC; } + + static void printStatisticsDetails(unsigned int imageCount, const InitializerTimingList&); + static uint32_t minOSVersion(const mach_header*); + static uint32_t sdkVersion(const mach_header* mh); + static intptr_t computeSlide(const mach_header* mh); + static bool findSection(const mach_header* mh, const char* segmentName, const char* sectionName, void** sectAddress, uintptr_t* sectSize); + static const dyld_info_command* findDyldInfoLoadCommand(const mach_header* mh); + static const char* findClosestSymbol(const mach_header* mh, const void* addr, const void** closestAddr); + static bool getLazyBindingInfo(uint32_t& lazyBindingInfoOffset, const uint8_t* lazyInfoStart, const uint8_t* lazyInfoEnd, + uint8_t* segIndex, uintptr_t* segOffset, int* ordinal, const char** symbolName, bool* doneAfterBind); + static uintptr_t segPreferredAddress(const mach_header* mh, unsigned segIndex); + static uintptr_t bindLocation(const LinkContext& context, uintptr_t location, uintptr_t value, + uint8_t type, const char* symbolName, + intptr_t addend, const char* inPath, const char* toPath, const char* msg); + virtual void rebase(const LinkContext& context, uintptr_t slide) = 0; + + + +protected: + ImageLoaderMachO(const ImageLoaderMachO&); + ImageLoaderMachO(const macho_header* mh, const char* path, unsigned int segCount, + uint32_t segOffsets[], unsigned int libCount); + virtual ~ImageLoaderMachO() {} + + void operator=(const ImageLoaderMachO&); + + virtual void setDyldInfo(const struct dyld_info_command*) = 0; + virtual void setSymbolTableInfo(const macho_nlist*, const char*, const dysymtab_command*) = 0; + virtual bool isSubframeworkOf(const LinkContext& context, const ImageLoader* image) const = 0; + virtual bool hasSubLibrary(const LinkContext& context, const ImageLoader* child) const = 0; + virtual uint32_t* segmentCommandOffsets() const = 0; + virtual const ImageLoader::Symbol* findShallowExportedSymbol(const char* name, const ImageLoader** foundIn) const = 0; + virtual bool containsSymbol(const void* addr) const = 0; + virtual uintptr_t exportedSymbolAddress(const LinkContext& context, const Symbol* symbol, const ImageLoader* requestor, bool runResolver) const = 0; + virtual bool exportedSymbolIsWeakDefintion(const Symbol* symbol) const = 0; + virtual const char* exportedSymbolName(const Symbol* symbol) const = 0; + virtual unsigned int exportedSymbolCount() const = 0; + virtual const ImageLoader::Symbol* exportedSymbolIndexed(unsigned int) const = 0; + virtual unsigned int importedSymbolCount() const = 0; + virtual const ImageLoader::Symbol* importedSymbolIndexed(unsigned int) const = 0; + virtual const char* importedSymbolName(const Symbol* symbol) const = 0; +#if PREBOUND_IMAGE_SUPPORT + virtual void resetPreboundLazyPointers(const LinkContext& context) = 0; +#endif + + + virtual void doGetDependentLibraries(DependentLibraryInfo libs[]); + virtual LibraryInfo doGetLibraryInfo(const LibraryInfo& requestorInfo); + virtual void getRPaths(const LinkContext& context, std::vector&) const; + virtual bool getUUID(uuid_t) const; + virtual void doRebase(const LinkContext& context); + virtual void doBind(const LinkContext& context, bool forceLazysBound) = 0; + virtual void doBindJustLazies(const LinkContext& context) = 0; + virtual bool doInitialization(const LinkContext& context); + virtual void doGetDOFSections(const LinkContext& context, std::vector& dofs); + virtual bool needsTermination(); + virtual bool segmentsMustSlideTogether() const; + virtual bool segmentsCanSlide() const; + virtual void setSlide(intptr_t slide); + virtual bool usesTwoLevelNameSpace() const; + virtual bool isPrebindable() const; + +protected: + + void destroy(); + static void sniffLoadCommands(const macho_header* mh, const char* path, bool inCache, bool* compressed, + unsigned int* segCount, unsigned int* libCount, const LinkContext& context, + const linkedit_data_command** codeSigCmd, + const encryption_info_command** encryptCmd); + static bool needsAddedLibSystemDepency(unsigned int libCount, const macho_header* mh); + void loadCodeSignature(const struct linkedit_data_command* codeSigCmd, int fd, uint64_t offsetInFatFile, const LinkContext& context); + void validateFirstPages(const struct linkedit_data_command* codeSigCmd, int fd, const uint8_t *fileData, size_t lenFileData, off_t offsetInFat, const LinkContext& context); + const struct macho_segment_command* segLoadCommand(unsigned int segIndex) const; + void parseLoadCmds(const ImageLoader::LinkContext& context); + int crashIfInvalidCodeSignature(); + bool segHasRebaseFixUps(unsigned int) const; + bool segHasBindFixUps(unsigned int) const; + void segProtect(unsigned int segIndex, const ImageLoader::LinkContext& context); + void segMakeWritable(unsigned int segIndex, const ImageLoader::LinkContext& context); +#if __i386__ + bool segIsReadOnlyImport(unsigned int) const; +#endif + intptr_t assignSegmentAddresses(const LinkContext& context); + uintptr_t reserveAnAddressRange(size_t length, const ImageLoader::LinkContext& context); + bool reserveAddressRange(uintptr_t start, size_t length); + void mapSegments(int fd, uint64_t offsetInFat, uint64_t lenInFat, uint64_t fileLen, const LinkContext& context); + void mapSegments(const void* memoryImage, uint64_t imageLen, const LinkContext& context); + void UnmapSegments(); + void __attribute__((noreturn)) throwSymbolNotFound(const LinkContext& context, const char* symbol, + const char* referencedFrom, const char* fromVersMismatch, + const char* expectedIn); + void doImageInit(const LinkContext& context); + void doModInitFunctions(const LinkContext& context); + void setupLazyPointerHandler(const LinkContext& context); + void lookupProgramVars(const LinkContext& context) const; + + void makeTextSegmentWritable(const LinkContext& context, bool writeable); + void preFetchDATA(int fd, uint64_t offsetInFat, const LinkContext& context); + + + void doInterpose(const LinkContext& context) = 0; + bool hasReferencesToWeakSymbols() const; + uintptr_t getSymbolAddress(const Symbol* sym, const ImageLoader* requestor, + const LinkContext& context, bool runResolver) const; + + static uintptr_t bindLazySymbol(const mach_header*, uintptr_t* lazyPointer); +protected: + uint64_t fCoveredCodeLength; + const uint8_t* fMachOData; + const uint8_t* fLinkEditBase; // add any internal "offset" to this to get mapped address + uintptr_t fSlide; + uint32_t fEHFrameSectionOffset; + uint32_t fUnwindInfoSectionOffset; + uint32_t fDylibIDOffset; + uint32_t fSegmentsCount : 8, + fIsSplitSeg : 1, + fInSharedCache : 1, +#if TEXT_RELOC_SUPPORT + fTextSegmentRebases : 1, + fTextSegmentBinds : 1, +#endif +#if __i386__ + fReadOnlyImportSegment : 1, +#endif + fHasSubLibraries : 1, + fHasSubUmbrella : 1, + fInUmbrella : 1, + fHasDOFSections : 1, + fHasDashInit : 1, + fHasInitializers : 1, + fHasTerminators : 1, + fNotifyObjC : 1, + fRetainForObjC : 1, + fRegisteredAsRequiresCoalescing : 1; // Loading MH_DYLIB_STUB causing coalescable miscount + + + static uint32_t fgSymbolTableBinarySearchs; +}; + + +#endif // __IMAGELOADERMACHO__ + + + + diff --git a/src/dyld-apple/src/ImageLoaderMachOClassic.cpp b/src/dyld-apple/src/ImageLoaderMachOClassic.cpp new file mode 100644 index 000000000..fa71b097f --- /dev/null +++ b/src/dyld-apple/src/ImageLoaderMachOClassic.cpp @@ -0,0 +1,2129 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2004-2010 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@ + */ + +// work around until conformance work is complete rdar://problem/4508801 +#define __srr0 srr0 +#define __eip eip +#define __rip rip + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if __x86_64__ + #include +#endif +#if __arm__ + #include +#endif + +#include "ImageLoaderMachOClassic.h" +#include "mach-o/dyld_images.h" + +// in dyldStartup.s +extern "C" void stub_binding_helper_i386_old(); + + +#if __x86_64__ + #define POINTER_RELOC X86_64_RELOC_UNSIGNED +#else + #define POINTER_RELOC GENERIC_RELOC_VANILLA +#endif + + +// relocation_info.r_length field has value 3 for 64-bit executables and value 2 for 32-bit executables +#if __LP64__ + #define RELOC_SIZE 3 + #define LC_SEGMENT_COMMAND LC_SEGMENT_64 + #define LC_ROUTINES_COMMAND LC_ROUTINES_64 + struct macho_segment_command : public segment_command_64 {}; + struct macho_section : public section_64 {}; + struct macho_routines_command : public routines_command_64 {}; +#else + #define RELOC_SIZE 2 + #define LC_SEGMENT_COMMAND LC_SEGMENT + #define LC_ROUTINES_COMMAND LC_ROUTINES + struct macho_segment_command : public segment_command {}; + struct macho_section : public section {}; + struct macho_routines_command : public routines_command {}; +#endif + + + + +// create image for main executable +ImageLoaderMachOClassic* ImageLoaderMachOClassic::instantiateMainExecutable(const macho_header* mh, uintptr_t slide, const char* path, + unsigned int segCount, unsigned int libCount, const LinkContext& context) +{ + ImageLoaderMachOClassic* image = ImageLoaderMachOClassic::instantiateStart(mh, path, segCount, libCount); + + // set slide for PIE programs + image->setSlide(slide); + + // for PIE record end of program, to know where to start loading dylibs + if ( slide != 0 ) + fgNextPIEDylibAddress = (uintptr_t)image->getEnd(); + + image->disableCoverageCheck(); + image->instantiateFinish(context); + image->setMapped(context); + +#if __i386__ + // kernel may have mapped in __IMPORT segment read-only, we need it read/write to do binding + if ( image->fReadOnlyImportSegment ) { + for(unsigned int i=0; i < image->fSegmentsCount; ++i) { + if ( image->segIsReadOnlyImport(i) ) + image->segMakeWritable(i, context); + } + } +#endif + + if ( context.verboseMapping ) { + dyld::log("dyld: Main executable mapped %s\n", path); + for(unsigned int i=0, e=image->segmentCount(); i < e; ++i) { + const char* name = image->segName(i); + if ( (strcmp(name, "__PAGEZERO") == 0) || (strcmp(name, "__UNIXSTACK") == 0) ) + dyld::log("%18s at 0x%08lX->0x%08lX\n", name, image->segPreferredLoadAddress(i), image->segPreferredLoadAddress(i)+image->segSize(i)); + else + dyld::log("%18s at 0x%08lX->0x%08lX\n", name, image->segActualLoadAddress(i), image->segActualEndAddress(i)); + } + } + + return image; +} + +// create image by mapping in a mach-o file +ImageLoaderMachOClassic* ImageLoaderMachOClassic::instantiateFromFile(const char* path, int fd, const uint8_t* fileData, size_t lenFileData, + uint64_t offsetInFat, uint64_t lenInFat, const struct stat& info, + unsigned int segCount, unsigned int libCount, + const struct linkedit_data_command* codeSigCmd, const LinkContext& context) +{ + ImageLoaderMachOClassic* image = ImageLoaderMachOClassic::instantiateStart((macho_header*)fileData, path, segCount, libCount); + try { + // record info about file + image->setFileInfo(info.st_dev, info.st_ino, info.st_mtime); + + // if this image is code signed, let kernel validate signature before mapping any pages from image + image->loadCodeSignature(codeSigCmd, fd, offsetInFat, context); + + // Validate that first data we read with pread actually matches with code signature + image->validateFirstPages(codeSigCmd, fd, fileData, lenFileData, offsetInFat, context); + + // mmap segments + image->mapSegmentsClassic(fd, offsetInFat, lenInFat, info.st_size, context); + + // finish up + image->instantiateFinish(context); + + // if path happens to be same as in LC_DYLIB_ID load command use that, otherwise malloc a copy of the path + const char* installName = image->getInstallPath(); + if ( (installName != NULL) && (strcmp(installName, path) == 0) && (path[0] == '/') ) + image->setPathUnowned(installName); + else if ( (path[0] != '/') || (strstr(path, "../") != NULL) ) { + // rdar://problem/10733082 Fix up @path based paths during introspection + // rdar://problem/5135363 turn relative paths into absolute paths so gdb, Symbolication can later find them + char realPath[MAXPATHLEN]; + if ( fcntl(fd, F_GETPATH, realPath) == 0 ) + image->setPaths(path, realPath); + else + image->setPath(path); + } + else + image->setPath(path); + + // make sure path is stable before recording in dyld_all_image_infos + image->setMapped(context); + + // pre-fetch content of __DATA segment for faster launches + // don't do this on prebound images or if prefetching is disabled + if ( !context.preFetchDisabled && !image->isPrebindable()) + image->preFetchDATA(fd, offsetInFat, context); + + } + catch (...) { + // ImageLoader::setMapped() can throw an exception to block loading of image + // Leaked fSegmentsArray and image segments during failed dlopen_preflight + delete image; + throw; + } + + return image; +} + +// create image by using cached mach-o file +ImageLoaderMachOClassic* ImageLoaderMachOClassic::instantiateFromCache(const macho_header* mh, const char* path, long slide, const struct stat& info, + unsigned int segCount, unsigned int libCount, const LinkContext& context) +{ + ImageLoaderMachOClassic* image = ImageLoaderMachOClassic::instantiateStart(mh, path, segCount, libCount); + try { + // record info about file + image->setFileInfo(info.st_dev, info.st_ino, info.st_mtime); + + // remember this is from shared cache and cannot be unloaded + image->fInSharedCache = true; + image->setNeverUnload(); + image->disableCoverageCheck(); + + // segments already mapped in cache + if ( context.verboseMapping ) { + dyld::log("dyld: Using shared cached for %s\n", path); + for(unsigned int i=0, e=image->segmentCount(); i < e; ++i) { + dyld::log("%18s at 0x%08lX->0x%08lX\n", image->segName(i), image->segActualLoadAddress(i), image->segActualEndAddress(i)); + } + } + + image->instantiateFinish(context); + image->setMapped(context); + } + catch (...) { + // ImageLoader::setMapped() can throw an exception to block loading of image + // Leaked fSegmentsArray and image segments during failed dlopen_preflight + delete image; + throw; + } + + return image; +} + +// create image by copying an in-memory mach-o file +ImageLoaderMachOClassic* ImageLoaderMachOClassic::instantiateFromMemory(const char* moduleName, const macho_header* mh, uint64_t len, + unsigned int segCount, unsigned int libCount, const LinkContext& context) +{ + ImageLoaderMachOClassic* image = ImageLoaderMachOClassic::instantiateStart(mh, moduleName, segCount, libCount); + try { + // map segments + if ( mh->filetype == MH_EXECUTE ) + throw "can't load another MH_EXECUTE"; + + // vmcopy segments + image->ImageLoaderMachO::mapSegments((const void*)mh, len, context); + + // for compatibility, never unload dylibs loaded from memory + image->setNeverUnload(); + + image->disableCoverageCheck(); + + // bundle loads need path copied + if ( moduleName != NULL ) + image->setPath(moduleName); + + image->instantiateFinish(context); + image->setMapped(context); + } + catch (...) { + // ImageLoader::setMapped() can throw an exception to block loading of image + // Leaked fSegmentsArray and image segments during failed dlopen_preflight + delete image; + throw; + } + + return image; +} + + +ImageLoaderMachOClassic::ImageLoaderMachOClassic(const macho_header* mh, const char* path, + unsigned int segCount, uint32_t segOffsets[], unsigned int libCount) + : ImageLoaderMachO(mh, path, segCount, segOffsets, libCount), fStrings(NULL), fSymbolTable(NULL), fDynamicInfo(NULL) +{ +} + +// construct ImageLoaderMachOClassic using "placement new" with SegmentMachO objects array at end +ImageLoaderMachOClassic* ImageLoaderMachOClassic::instantiateStart(const macho_header* mh, const char* path, + unsigned int segCount, unsigned int libCount) +{ + size_t size = sizeof(ImageLoaderMachOClassic) + segCount * sizeof(uint32_t) + libCount * sizeof(ImageLoader*); + ImageLoaderMachOClassic* allocatedSpace = static_cast(malloc(size)); + if ( allocatedSpace == NULL ) + throw "malloc failed"; + uint32_t* segOffsets = ((uint32_t*)(((uint8_t*)allocatedSpace) + sizeof(ImageLoaderMachOClassic))); + bzero(&segOffsets[segCount], libCount*sizeof(void*)); // zero out lib array + return new (allocatedSpace) ImageLoaderMachOClassic(mh, path, segCount, segOffsets, libCount); +} + + + +// common code to finish initializing object +void ImageLoaderMachOClassic::instantiateFinish(const LinkContext& context) +{ + // now that segments are mapped in, get real fMachOData, fLinkEditBase, and fSlide + this->parseLoadCmds(context); +} + +ImageLoaderMachOClassic::~ImageLoaderMachOClassic() +{ + // don't do clean up in ~ImageLoaderMachO() because virtual call to segmentCommandOffsets() won't work + destroy(); +} + +uint32_t* ImageLoaderMachOClassic::segmentCommandOffsets() const +{ + return ((uint32_t*)(((uint8_t*)this) + sizeof(ImageLoaderMachOClassic))); +} + + +ImageLoader* ImageLoaderMachOClassic::libImage(unsigned int libIndex) const +{ + const uintptr_t* images = ((uintptr_t*)(((uint8_t*)this) + sizeof(ImageLoaderMachOClassic) + fSegmentsCount*sizeof(uint32_t))); + // mask off low bits + return (ImageLoader*)(images[libIndex] & (-4)); +} + +bool ImageLoaderMachOClassic::libReExported(unsigned int libIndex) const +{ + const uintptr_t* images = ((uintptr_t*)(((uint8_t*)this) + sizeof(ImageLoaderMachOClassic) + fSegmentsCount*sizeof(uint32_t))); + // re-export flag is low bit + return ((images[libIndex] & 1) != 0); +} + +bool ImageLoaderMachOClassic::libIsUpward(unsigned int libIndex) const +{ + const uintptr_t* images = ((uintptr_t*)(((uint8_t*)this) + sizeof(ImageLoaderMachOClassic) + fSegmentsCount*sizeof(uint32_t))); + // upward flag is second bit + return ((images[libIndex] & 2) != 0); +} + + +void ImageLoaderMachOClassic::setLibImage(unsigned int libIndex, ImageLoader* image, bool reExported, bool upward) +{ + uintptr_t* images = ((uintptr_t*)(((uint8_t*)this) + sizeof(ImageLoaderMachOClassic) + fSegmentsCount*sizeof(uint32_t))); + uintptr_t value = (uintptr_t)image; + if ( reExported ) + value |= 1; + if ( upward ) + value |= 2; + images[libIndex] = value; +} + + +void ImageLoaderMachOClassic::setSymbolTableInfo(const macho_nlist* symbols, const char* strings, const dysymtab_command* dynSym) +{ + fSymbolTable = symbols; + fStrings = strings; + fDynamicInfo = dynSym; +} + +void ImageLoaderMachOClassic::prefetchLINKEDIT(const LinkContext& context) +{ + // always prefetch a subrange of __LINKEDIT pages + uintptr_t symbolTableStart = (uintptr_t)fSymbolTable; + uintptr_t stringTableStart = (uintptr_t)fStrings; + uintptr_t start; + // if image did not load at preferred address + if ( segPreferredLoadAddress(0) != (uintptr_t)fMachOData ) { + // local relocations will be processed, so start pre-fetch at local symbols + start = (uintptr_t)fMachOData + fDynamicInfo->locreloff; + } + else { + // otherwise start pre-fetch at global symbols section of symbol table + start = symbolTableStart + fDynamicInfo->iextdefsym * sizeof(macho_nlist); + } + // prefetch ends at end of last undefined string in string pool + uintptr_t end = stringTableStart; + if ( fDynamicInfo->nundefsym != 0 ) + end += fSymbolTable[fDynamicInfo->iundefsym+fDynamicInfo->nundefsym-1].n_un.n_strx; + else if ( fDynamicInfo->nextdefsym != 0 ) + end += fSymbolTable[fDynamicInfo->iextdefsym+fDynamicInfo->nextdefsym-1].n_un.n_strx; + + // round to whole pages + start = dyld_page_trunc(start); + end = dyld_page_round(end); + + // skip if there is only one page + if ( (end-start) > dyld_page_size ) { + madvise((void*)start, end-start, MADV_WILLNEED); + fgTotalBytesPreFetched += (end-start); + if ( context.verboseMapping ) { + dyld::log("%18s prefetching 0x%0lX -> 0x%0lX\n", "__LINKEDIT", start, end-1); + } + } +} + + +#if SPLIT_SEG_DYLIB_SUPPORT +unsigned int +ImageLoaderMachOClassic::getExtraZeroFillEntriesCount() +{ + // calculate mapping entries + unsigned int extraZeroFillEntries = 0; + for(unsigned int i=0; i < fSegmentsCount; ++i) { + if ( segHasTrailingZeroFill(i) ) + ++extraZeroFillEntries; + } + + return extraZeroFillEntries; +} + +void +ImageLoaderMachOClassic::initMappingTable(uint64_t offsetInFat, + shared_file_mapping_np *mappingTable) +{ + for(unsigned int i=0,entryIndex=0; i < fSegmentsCount; ++i, ++entryIndex) { + shared_file_mapping_np* entry = &mappingTable[entryIndex]; + entry->sfm_address = segActualLoadAddress(i); + entry->sfm_size = segFileSize(i); + entry->sfm_file_offset = segFileOffset(i) + offsetInFat; + entry->sfm_init_prot = VM_PROT_NONE; + if ( !segUnaccessible(i) ) { + if ( segExecutable(i) ) + entry->sfm_init_prot |= VM_PROT_EXECUTE; + if ( segReadable(i) ) + entry->sfm_init_prot |= VM_PROT_READ; + if ( segWriteable(i) ) + entry->sfm_init_prot |= VM_PROT_WRITE | VM_PROT_COW; + } + entry->sfm_max_prot = entry->sfm_init_prot; + if ( segHasTrailingZeroFill(i) ) { + shared_file_mapping_np* zfentry = &mappingTable[++entryIndex]; + zfentry->sfm_address = entry->sfm_address + segFileSize(i); + zfentry->sfm_size = segSize(i) - segFileSize(i); + zfentry->sfm_file_offset = 0; + zfentry->sfm_init_prot = entry->sfm_init_prot | VM_PROT_COW | VM_PROT_ZF; + zfentry->sfm_max_prot = zfentry->sfm_init_prot; + } + } +} + +int +ImageLoaderMachOClassic::mapSplitSegDylibOutsideSharedRegion(int fd, + uint64_t offsetInFat, + uint64_t lenInFat, + uint64_t fileLen, + const LinkContext& context) +{ + uintptr_t nextAltLoadAddress = 0; + const unsigned int segmentCount = fSegmentsCount; + const unsigned int extraZeroFillEntries = getExtraZeroFillEntriesCount(); + const unsigned int regionCount = segmentCount+extraZeroFillEntries; + shared_file_mapping_np regions[regionCount]; + initMappingTable(offsetInFat, regions); + int r = -1; + // find space somewhere to allocate split seg + bool foundRoom = false; + while ( ! foundRoom ) { + foundRoom = true; + for(unsigned int i=0; i < regionCount; ++i) { + vm_address_t addr = nextAltLoadAddress + regions[i].sfm_address - regions[0].sfm_address; + vm_size_t size = regions[i].sfm_size ; + r = vm_allocate(mach_task_self(), &addr, size, false /*only this range*/); + if ( 0 != r ) { + // no room here, deallocate what has succeeded so far + for(unsigned int j=0; j < i; ++j) { + addr = nextAltLoadAddress + regions[j].sfm_address - regions[0].sfm_address; + size = regions[j].sfm_size ; + (void)vm_deallocate(mach_task_self(), addr, size); + } + nextAltLoadAddress += 0x00100000; // skip ahead 1MB and try again + // skip over shared region + if ( (SHARED_REGION_BASE <= nextAltLoadAddress) && (nextAltLoadAddress < (SHARED_REGION_BASE + SHARED_REGION_SIZE)) ) + nextAltLoadAddress = (SHARED_REGION_BASE + SHARED_REGION_SIZE); + if ( nextAltLoadAddress > 0xFF000000 ) + throw "can't map split seg anywhere"; + foundRoom = false; + break; + } + } + } + + // map in each region + uintptr_t slide = nextAltLoadAddress - regions[0].sfm_address; + this->setSlide(slide); + for(unsigned int i=0; i < regionCount; ++i) { + if ( ((regions[i].sfm_init_prot & VM_PROT_ZF) != 0) || (regions[i].sfm_size == 0) ) { + // nothing to mmap for zero-fills areas, they are just vm_allocated + } + else { + void* mmapAddress = (void*)(uintptr_t)(regions[i].sfm_address + slide); + size_t size = regions[i].sfm_size; + int protection = 0; + if ( regions[i].sfm_init_prot & VM_PROT_EXECUTE ) + protection |= PROT_EXEC; + if ( regions[i].sfm_init_prot & VM_PROT_READ ) + protection |= PROT_READ; + if ( regions[i].sfm_init_prot & VM_PROT_WRITE ) + protection |= PROT_WRITE; + off_t offset = regions[i].sfm_file_offset; + //dyld::log("mmap(%p, 0x%08lX, %s\n", mmapAddress, size, fPath); + mmapAddress = mmap(mmapAddress, size, protection, MAP_FIXED | MAP_PRIVATE, fd, offset); + if ( mmapAddress == ((void*)(-1)) ) + throw "mmap error"; + } + } + + // logging + if ( context.verboseMapping ) { + dyld::log("dyld: Mapping split-seg outside shared region, slid by 0x%08lX %s\n", this->fSlide, this->getPath()); + for(unsigned int segIndex=0,entryIndex=0; segIndex < segmentCount; ++segIndex, ++entryIndex){ + const shared_file_mapping_np* entry = ®ions[entryIndex]; + if ( (entry->sfm_init_prot & VM_PROT_ZF) == 0 ) + dyld::log("%18s at 0x%08lX->0x%08lX\n", + segName(segIndex), segActualLoadAddress(segIndex), segActualEndAddress(segIndex)-1); + if ( entryIndex < (regionCount-1) ) { + const shared_file_mapping_np* nextEntry = ®ions[entryIndex+1]; + if ( (nextEntry->sfm_init_prot & VM_PROT_ZF) != 0 ) { + uint64_t segOffset = nextEntry->sfm_address - entry->sfm_address; + dyld::log("%18s at 0x%08lX->0x%08lX (zerofill)\n", + segName(segIndex), (uintptr_t)(segActualLoadAddress(segIndex) + segOffset), (uintptr_t)(segActualLoadAddress(segIndex) + segOffset + nextEntry->sfm_size - 1)); + ++entryIndex; + } + } + } + } + + return r; +} +#endif // SPLIT_SEG_DYLIB_SUPPORT + + +void ImageLoaderMachOClassic::mapSegmentsClassic(int fd, uint64_t offsetInFat, uint64_t lenInFat, uint64_t fileLen, const LinkContext& context) +{ + // non-split segment libraries handled by super class + if ( !fIsSplitSeg ) + return ImageLoaderMachO::mapSegments(fd, offsetInFat, lenInFat, fileLen, context); + +#if SPLIT_SEG_SHARED_REGION_SUPPORT + // don't map split-seg dylibs into shared region if shared cache is in use + if ( ! context.dyldLoadedAtSameAddressNeededBySharedCache ) { + // try to map into shared region at preferred address + if ( mapSplitSegDylibInfoSharedRegion(fd, offsetInFat, lenInFat, fileLen, context) == 0) + return; + } + // if there is a problem, fall into case where we map file somewhere outside the shared region +#endif + +#if SPLIT_SEG_DYLIB_SUPPORT + // support old split-seg dylibs by mapping them where ever we find space + if ( mapSplitSegDylibOutsideSharedRegion(fd, offsetInFat, lenInFat, fileLen, context) != 0 ) +#endif + throw "mapping error"; +} + + +#if SPLIT_SEG_SHARED_REGION_SUPPORT +static int _shared_region_map_np(int fd, uint32_t count, const shared_file_mapping_np mappings[]) +{ + return syscall(295, fd, count, mappings); +} + +int +ImageLoaderMachOClassic::mapSplitSegDylibInfoSharedRegion(int fd, + uint64_t offsetInFat, + uint64_t lenInFat, + uint64_t fileLen, + const LinkContext& context) +{ + // build table of segments to map + const unsigned int segmentCount = fSegmentsCount; + const unsigned int extraZeroFillEntries = getExtraZeroFillEntriesCount(); + const unsigned int mappingTableCount = segmentCount+extraZeroFillEntries; + shared_file_mapping_np mappingTable[mappingTableCount]; + initMappingTable(offsetInFat, mappingTable); + + // try to map it in shared + int r = _shared_region_map_np(fd, mappingTableCount, mappingTable); + if ( 0 == r ) { + this->setNeverUnload(); + if ( context.verboseMapping ) { + dyld::log("dyld: Mapping split-seg shared %s\n", this->getPath()); + for(unsigned int segIndex=0,entryIndex=0; segIndex < segmentCount; ++segIndex, ++entryIndex){ + const shared_file_mapping_np* entry = &mappingTable[entryIndex]; + if ( (entry->sfm_init_prot & VM_PROT_ZF) == 0 ) + dyld::log("%18s at 0x%08lX->0x%08lX\n", + segName(segIndex), segActualLoadAddress(segIndex), segActualEndAddress(segIndex)-1); + if ( entryIndex < (mappingTableCount-1) ) { + const shared_file_mapping_np* nextEntry = &mappingTable[entryIndex+1]; + if ( (nextEntry->sfm_init_prot & VM_PROT_ZF) != 0 ) { + uint64_t segOffset = nextEntry->sfm_address - entry->sfm_address; + dyld::log("%18s at 0x%08lX->0x%08lX\n", + segName(segIndex), (uintptr_t)(segActualLoadAddress(segIndex) + segOffset), + (uintptr_t)(segActualLoadAddress(segIndex) + segOffset + nextEntry->sfm_size - 1)); + ++entryIndex; + } + } + } + } + } + return r; +} + +#endif // SPLIT_SEG_SHARED_REGION_SUPPORT + +// test if this image is re-exported through parent (the image that loaded this one) +bool ImageLoaderMachOClassic::isSubframeworkOf(const LinkContext& context, const ImageLoader* parent) const +{ + if ( fInUmbrella ) { + const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds; + const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; + const struct load_command* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + if (cmd->cmd == LC_SUB_FRAMEWORK) { + const struct sub_framework_command* subf = (struct sub_framework_command*)cmd; + const char* exportThruName = (char*)cmd + subf->umbrella.offset; + // need to match LC_SUB_FRAMEWORK string against the leaf name of the install location of parent... + const char* parentInstallPath = parent->getInstallPath(); + if ( parentInstallPath != NULL ) { + const char* lastSlash = strrchr(parentInstallPath, '/'); + if ( lastSlash != NULL ) { + if ( strcmp(&lastSlash[1], exportThruName) == 0 ) + return true; + if ( context.imageSuffix != NULL ) { + // when DYLD_IMAGE_SUFFIX is used, lastSlash string needs imageSuffix removed from end + char reexportAndSuffix[strlen(context.imageSuffix)+strlen(exportThruName)+1]; + strcpy(reexportAndSuffix, exportThruName); + strcat(reexportAndSuffix, context.imageSuffix); + if ( strcmp(&lastSlash[1], reexportAndSuffix) == 0 ) + return true; + } + } + } + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + } + return false; +} + +// test if child is re-exported +bool ImageLoaderMachOClassic::hasSubLibrary(const LinkContext& context, const ImageLoader* child) const +{ + if ( fHasSubLibraries ) { + // need to match LC_SUB_LIBRARY string against the leaf name (without extension) of the install location of child... + const char* childInstallPath = child->getInstallPath(); + if ( childInstallPath != NULL ) { + const char* lastSlash = strrchr(childInstallPath, '/'); + if ( lastSlash != NULL ) { + const char* firstDot = strchr(lastSlash, '.'); + size_t len; + if ( firstDot == NULL ) + len = strlen(lastSlash); + else + len = firstDot-lastSlash-1; + char childLeafName[len+1]; + strncpy(childLeafName, &lastSlash[1], len); + childLeafName[len] = '\0'; + const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds; + const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; + const struct load_command* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd) { + case LC_SUB_LIBRARY: + { + const struct sub_library_command* lib = (struct sub_library_command*)cmd; + const char* aSubLibName = (char*)cmd + lib->sub_library.offset; + if ( strcmp(aSubLibName, childLeafName) == 0 ) + return true; + if ( context.imageSuffix != NULL ) { + // when DYLD_IMAGE_SUFFIX is used, childLeafName string needs imageSuffix removed from end + char aSubLibNameAndSuffix[strlen(context.imageSuffix)+strlen(aSubLibName)+1]; + strcpy(aSubLibNameAndSuffix, aSubLibName); + strcat(aSubLibNameAndSuffix, context.imageSuffix); + if ( strcmp(aSubLibNameAndSuffix, childLeafName) == 0 ) + return true; + } + } + break; + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + } + } + } + if ( fHasSubUmbrella ) { + // need to match LC_SUB_UMBRELLA string against the leaf name of install location of child... + const char* childInstallPath = child->getInstallPath(); + if ( childInstallPath != NULL ) { + const char* lastSlash = strrchr(childInstallPath, '/'); + if ( lastSlash != NULL ) { + const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds; + const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; + const struct load_command* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd) { + case LC_SUB_UMBRELLA: + { + const struct sub_umbrella_command* um = (struct sub_umbrella_command*)cmd; + const char* aSubUmbrellaName = (char*)cmd + um->sub_umbrella.offset; + if ( strcmp(aSubUmbrellaName, &lastSlash[1]) == 0 ) + return true; + if ( context.imageSuffix != NULL ) { + // when DYLD_IMAGE_SUFFIX is used, lastSlash string needs imageSuffix removed from end + char umbrellaAndSuffix[strlen(context.imageSuffix)+strlen(aSubUmbrellaName)+1]; + strcpy(umbrellaAndSuffix, aSubUmbrellaName); + strcat(umbrellaAndSuffix, context.imageSuffix); + if ( strcmp(umbrellaAndSuffix, &lastSlash[1]) == 0 ) + return true; + } + } + break; + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + } + } + } + return false; +} + + +uintptr_t ImageLoaderMachOClassic::getFirstWritableSegmentAddress() +{ + // in split segment libraries r_address is offset from first writable segment + for(unsigned int i=0; i < fSegmentsCount; ++i) { + if ( segWriteable(i) ) + return segActualLoadAddress(i); + } + throw "no writable segment"; +} + +uintptr_t ImageLoaderMachOClassic::getRelocBase() +{ + // r_address is either an offset from the first segment address + // or from the first writable segment address +#if __x86_64__ + return getFirstWritableSegmentAddress(); +#else + if ( fIsSplitSeg ) + return getFirstWritableSegmentAddress(); + else + return segActualLoadAddress(0); +#endif +} + + +#if PREBOUND_IMAGE_SUPPORT +void ImageLoaderMachOClassic::resetPreboundLazyPointers(const LinkContext& context) +{ + // loop through all local (internal) relocation records looking for pre-bound-lazy-pointer values + const uintptr_t relocBase = this->getRelocBase(); + register const uintptr_t slide = this->fSlide; + const relocation_info* const relocsStart = (struct relocation_info*)(&fLinkEditBase[fDynamicInfo->locreloff]); + const relocation_info* const relocsEnd = &relocsStart[fDynamicInfo->nlocrel]; + for (const relocation_info* reloc=relocsStart; reloc < relocsEnd; ++reloc) { + if ( (reloc->r_address & R_SCATTERED) != 0 ) { + const struct scattered_relocation_info* sreloc = (struct scattered_relocation_info*)reloc; + if (sreloc->r_length == RELOC_SIZE) { + uintptr_t* locationToFix = (uintptr_t*)(sreloc->r_address + relocBase); + switch(sreloc->r_type) { + #if __i386__ + case GENERIC_RELOC_PB_LA_PTR: + *locationToFix = sreloc->r_value + slide; + break; + #endif + #if __arm__ + case ARM_RELOC_PB_LA_PTR: + *locationToFix = sreloc->r_value + slide; + break; + #endif + } + } + } + } +} +#endif + + + + +void ImageLoaderMachOClassic::rebase(const LinkContext& context, uintptr_t slide) +{ + CRSetCrashLogMessage2(this->getPath()); + const uintptr_t relocBase = this->getRelocBase(); + + // prefetch any LINKEDIT pages needed + if ( !context.preFetchDisabled && !this->isPrebindable()) + this->prefetchLINKEDIT(context); + + // loop through all local (internal) relocation records + const relocation_info* const relocsStart = (struct relocation_info*)(&fLinkEditBase[fDynamicInfo->locreloff]); + const relocation_info* const relocsEnd = &relocsStart[fDynamicInfo->nlocrel]; + for (const relocation_info* reloc=relocsStart; reloc < relocsEnd; ++reloc) { + uintptr_t rebaseAddr; + try { + #if LINKEDIT_USAGE_DEBUG + noteAccessedLinkEditAddress(reloc); + #endif + #if __x86_64__ + // only one kind of local relocation supported for x86_64 + if ( reloc->r_length != 3 ) + throw "bad local relocation length"; + if ( reloc->r_type != X86_64_RELOC_UNSIGNED ) + throw "unknown local relocation type"; + if ( reloc->r_pcrel != 0 ) + throw "bad local relocation pc_rel"; + if ( reloc->r_extern != 0 ) + throw "extern relocation found with local relocations"; + rebaseAddr = reloc->r_address + relocBase; + if ( ! this->containsAddress((void*)rebaseAddr) ) + dyld::throwf("local reloc %p not in mapped image\n", (void*)rebaseAddr); + *((uintptr_t*)rebaseAddr) += slide; + if ( context.verboseRebase ) + dyld::log("dyld: rebase: %s:*0x%08lX += 0x%08lX\n", this->getShortName(), rebaseAddr, slide); + #else + if ( (reloc->r_address & R_SCATTERED) == 0 ) { + if ( reloc->r_symbolnum == R_ABS ) { + // ignore absolute relocations + } + else if (reloc->r_length == RELOC_SIZE) { + switch(reloc->r_type) { + case GENERIC_RELOC_VANILLA: + rebaseAddr = reloc->r_address + relocBase; + if ( ! this->containsAddress((void*)rebaseAddr) ) + dyld::throwf("local reloc %p not in mapped image\n", (void*)rebaseAddr); + *((uintptr_t*)rebaseAddr) += slide; + if ( context.verboseRebase ) + dyld::log("dyld: rebase: %s:*0x%08lX += 0x%08lX\n", this->getShortName(), rebaseAddr, slide); + break; + default: + throw "unknown local relocation type"; + } + } + else { + throw "bad local relocation length"; + } + } + else { + const struct scattered_relocation_info* sreloc = (struct scattered_relocation_info*)reloc; + if (sreloc->r_length == RELOC_SIZE) { + uintptr_t* locationToFix = (uintptr_t*)(sreloc->r_address + relocBase); + switch(sreloc->r_type) { + case GENERIC_RELOC_VANILLA: + if ( ! this->containsAddress((void*)locationToFix) ) + dyld::throwf("local scattered reloc %p not in mapped image\n", locationToFix); + *locationToFix += slide; + if ( context.verboseRebase ) + dyld::log("dyld: rebase: %s:*0x%08lX += 0x%08lX\n", this->getShortName(), (uintptr_t)locationToFix, slide); + break; + #if __i386__ + case GENERIC_RELOC_PB_LA_PTR: + // do nothing + break; + #elif __arm__ + case ARM_RELOC_PB_LA_PTR: + // do nothing + break; + #endif + default: + throw "unknown local scattered relocation type"; + } + } + else { + throw "bad local scattered relocation length"; + } + } + #endif // x86_64 + } + catch (const char* msg) { + const uint8_t* r = (uint8_t*)reloc; + dyld::throwf("%s in %s. reloc record at %p: 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X", + msg, this->getPath(), reloc, r[0], r[1], r[2], r[3], r[4], r[5], r[6], r[7]); + } + } + + // update stats + fgTotalRebaseFixups += fDynamicInfo->nlocrel; + CRSetCrashLogMessage2(NULL); +} + + + +const struct macho_nlist* ImageLoaderMachOClassic::binarySearchWithToc(const char* key, const char stringPool[], const struct macho_nlist symbols[], + const struct dylib_table_of_contents toc[], uint32_t symbolCount, uint32_t hintIndex) const +{ + int32_t high = symbolCount-1; + int32_t mid = hintIndex; + + // handle out of range hint + if ( mid >= (int32_t)symbolCount ) + mid = symbolCount/2; + ++ImageLoaderMachO::fgSymbolTableBinarySearchs; + ++fgTotalBindImageSearches; + + //dyld::log("dyld: binarySearchWithToc for %s in %s\n", key, this->getShortName()); + + for (int32_t low = 0; low <= high; mid = (low+high)/2) { + const uint32_t index = toc[mid].symbol_index; + const struct macho_nlist* pivot = &symbols[index]; + const char* pivotStr = &stringPool[pivot->n_un.n_strx]; +#if LINKEDIT_USAGE_DEBUG + noteAccessedLinkEditAddress(&toc[mid]); + noteAccessedLinkEditAddress(pivot); + noteAccessedLinkEditAddress(pivotStr); +#endif + int cmp = strcmp(key, pivotStr); + if ( cmp == 0 ) + return pivot; + if ( cmp > 0 ) { + // key > pivot + low = mid + 1; + } + else { + // key < pivot + high = mid - 1; + } + } + return NULL; +} + +const struct macho_nlist* ImageLoaderMachOClassic::binarySearch(const char* key, const char stringPool[], const struct macho_nlist symbols[], uint32_t symbolCount) const +{ + // update stats + ++fgTotalBindImageSearches; + ++ImageLoaderMachO::fgSymbolTableBinarySearchs; + + //dyld::log("dyld: binarySearch for %s in %s, stringpool=%p, symbols=%p, symbolCount=%u\n", + // key, this->getShortName(), stringPool, symbols, symbolCount); + + const struct macho_nlist* base = symbols; + for (uint32_t n = symbolCount; n > 0; n /= 2) { + const struct macho_nlist* pivot = &base[n/2]; + const char* pivotStr = &stringPool[pivot->n_un.n_strx]; +#if LINKEDIT_USAGE_DEBUG + noteAccessedLinkEditAddress(pivot); + noteAccessedLinkEditAddress(pivotStr); +#endif + int cmp = strcmp(key, pivotStr); + if ( cmp == 0 ) + return pivot; + if ( cmp > 0 ) { + // key > pivot + // move base to symbol after pivot + base = &pivot[1]; + --n; + } + else { + // key < pivot + // keep same base + } + } + return NULL; +} + + +const ImageLoader::Symbol* ImageLoaderMachOClassic::findShallowExportedSymbol(const char* name, const ImageLoader** foundIn) const +{ + const struct macho_nlist* sym = NULL; + if ( fDynamicInfo->tocoff == 0 ) + sym = binarySearch(name, fStrings, &fSymbolTable[fDynamicInfo->iextdefsym], fDynamicInfo->nextdefsym); + else + sym = binarySearchWithToc(name, fStrings, fSymbolTable, (dylib_table_of_contents*)&fLinkEditBase[fDynamicInfo->tocoff], + fDynamicInfo->ntoc, fDynamicInfo->nextdefsym); + if ( sym != NULL ) { + if ( foundIn != NULL ) + *foundIn = (ImageLoader*)this; + return (const Symbol*)sym; + } + return NULL; +} + + + +bool ImageLoaderMachOClassic::containsSymbol(const void* addr) const +{ + return ( (fSymbolTable <= addr) && (addr < fStrings) ); +} + + +uintptr_t ImageLoaderMachOClassic::exportedSymbolAddress(const LinkContext& context, const Symbol* symbol, const ImageLoader* requestor, bool runResolver) const +{ + const struct macho_nlist* sym = (macho_nlist*)symbol; + uintptr_t result = sym->n_value + fSlide; + #if __arm__ + // processor assumes code address with low bit set is thumb + if (sym->n_desc & N_ARM_THUMB_DEF) + result |= 1; + #endif + return result; +} + +bool ImageLoaderMachOClassic::exportedSymbolIsWeakDefintion(const Symbol* symbol) const +{ + const struct macho_nlist* nlistSym = (const struct macho_nlist*)symbol; + return ( (nlistSym->n_desc & N_WEAK_DEF) != 0 ); +} + +const char* ImageLoaderMachOClassic::exportedSymbolName(const Symbol* symbol) const +{ + const struct macho_nlist* nlistSym = (const struct macho_nlist*)symbol; + return &fStrings[nlistSym->n_un.n_strx]; +} + +unsigned int ImageLoaderMachOClassic::exportedSymbolCount() const +{ + return fDynamicInfo->nextdefsym; +} + +const ImageLoader::Symbol* ImageLoaderMachOClassic::exportedSymbolIndexed(unsigned int index) const +{ + if ( index < fDynamicInfo->nextdefsym ) { + const struct macho_nlist* sym = &fSymbolTable[fDynamicInfo->iextdefsym + index]; + return (const ImageLoader::Symbol*)sym; + } + return NULL; +} + +unsigned int ImageLoaderMachOClassic::importedSymbolCount() const +{ + return fDynamicInfo->nundefsym; +} + +const ImageLoader::Symbol* ImageLoaderMachOClassic::importedSymbolIndexed(unsigned int index) const +{ + if ( index < fDynamicInfo->nundefsym ) { + const struct macho_nlist* sym = &fSymbolTable[fDynamicInfo->iundefsym + index]; + return (const ImageLoader::Symbol*)sym; + } + return NULL; +} + +const char* ImageLoaderMachOClassic::importedSymbolName(const Symbol* symbol) const +{ + const struct macho_nlist* nlistSym = (const struct macho_nlist*)symbol; + return &fStrings[nlistSym->n_un.n_strx]; +} + + + +bool ImageLoaderMachOClassic::symbolIsWeakDefinition(const struct macho_nlist* symbol) +{ + // if a define and weak ==> coalesced + if ( ((symbol->n_type & N_TYPE) == N_SECT) && ((symbol->n_desc & N_WEAK_DEF) != 0) ) + return true; + + // regular symbol + return false; +} + +bool ImageLoaderMachOClassic::symbolIsWeakReference(const struct macho_nlist* symbol) +{ + // if an undefine and not referencing a weak symbol ==> coalesced + if ( ((symbol->n_type & N_TYPE) != N_SECT) && ((symbol->n_desc & N_REF_TO_WEAK) != 0) ) + return true; + + // regular symbol + return false; +} + +uintptr_t ImageLoaderMachOClassic::getSymbolAddress(const macho_nlist* sym, const LinkContext& context, bool runResolver) const +{ + return ImageLoaderMachO::getSymbolAddress((Symbol*)sym, this, context, runResolver); +} + +uintptr_t ImageLoaderMachOClassic::resolveUndefined(const LinkContext& context, const struct macho_nlist* undefinedSymbol, + bool twoLevel, bool dontCoalesce, bool runResolver, const ImageLoader** foundIn) +{ + ++fgTotalBindSymbolsResolved; + const char* symbolName = &fStrings[undefinedSymbol->n_un.n_strx]; + +#if LINKEDIT_USAGE_DEBUG + noteAccessedLinkEditAddress(undefinedSymbol); + noteAccessedLinkEditAddress(symbolName); +#endif + if ( context.bindFlat || !twoLevel ) { + // flat lookup + if ( ((undefinedSymbol->n_type & N_PEXT) != 0) && ((undefinedSymbol->n_type & N_TYPE) == N_SECT) ) { + // is a multi-module private_extern internal reference that the linker did not optimize away + uintptr_t addr = this->getSymbolAddress(undefinedSymbol, context, false); + *foundIn = this; + return addr; + } + const Symbol* sym; + if ( context.flatExportFinder(symbolName, &sym, foundIn) ) { + if ( *foundIn != this ) + context.addDynamicReference(this, const_cast(*foundIn)); + return (*foundIn)->getExportedSymbolAddress(sym, context, this); + } + // if a bundle is loaded privately the above will not find its exports + if ( this->isBundle() && this->hasHiddenExports() ) { + // look in self for needed symbol + sym = this->findShallowExportedSymbol(symbolName, foundIn); + if ( sym != NULL ) + return (*foundIn)->getExportedSymbolAddress(sym, context, this); + } + if ( (undefinedSymbol->n_desc & N_WEAK_REF) != 0 ) { + // definition can't be found anywhere + // if reference is weak_import, then it is ok, just return 0 + return 0; + } + throwSymbolNotFound(context, symbolName, this->getPath(), "", "flat namespace"); + } + else { + // symbol requires searching images with coalesced symbols (not done during prebinding) + if ( !context.prebinding && !dontCoalesce && (symbolIsWeakReference(undefinedSymbol) || symbolIsWeakDefinition(undefinedSymbol)) ) { + const Symbol* sym; + if ( context.coalescedExportFinder(symbolName, &sym, foundIn) ) { + if ( *foundIn != this ) + context.addDynamicReference(this, const_cast(*foundIn)); + return (*foundIn)->getExportedSymbolAddress(sym, context, this); + } + //throwSymbolNotFound(context, symbolName, this->getPath(), "coalesced namespace"); + //dyld::log("dyld: coalesced symbol %s not found in any coalesced image, falling back to two-level lookup", symbolName); + } + + // if this is a real definition (not an undefined symbol) there is no ordinal + if ( (undefinedSymbol->n_type & N_TYPE) == N_SECT ) { + // static linker should never generate this case, but if it does, do something sane + uintptr_t addr = this->getSymbolAddress(undefinedSymbol, context, false); + *foundIn = this; + return addr; + } + + // two level lookup + ImageLoader* target = NULL; + uint8_t ord = GET_LIBRARY_ORDINAL(undefinedSymbol->n_desc); + if ( ord == EXECUTABLE_ORDINAL ) { + target = context.mainExecutable; + } + else if ( ord == SELF_LIBRARY_ORDINAL ) { + target = this; + } + else if ( ord == DYNAMIC_LOOKUP_ORDINAL ) { + // rnielsen: HACKHACK + // flat lookup + const Symbol* sym; + if ( context.flatExportFinder(symbolName, &sym, foundIn) ) + return (*foundIn)->getExportedSymbolAddress(sym, context, this); + // no image has exports this symbol + // report error + context.undefinedHandler(symbolName); + // try looking again + if ( context.flatExportFinder(symbolName, &sym, foundIn) ) + return (*foundIn)->getExportedSymbolAddress(sym, context, this); + + throwSymbolNotFound(context, symbolName, this->getPath(), "", "dynamic lookup"); + } + else if ( ord <= libraryCount() ) { + target = libImage(ord-1); + if ( target == NULL ) { + // if target library not loaded and reference is weak or library is weak return 0 + return 0; + } + } + else { + dyld::throwf("bad mach-o binary, library ordinal (%u) too big (max %u) for symbol %s in %s", + ord, libraryCount(), symbolName, this->getPath()); + } + + if ( target == NULL ) { + //dyld::log("resolveUndefined(%s) in %s\n", symbolName, this->getPath()); + throw "symbol not found"; + } + + uintptr_t address; + if ( target->findExportedSymbolAddress(context, symbolName, this, ord, runResolver, foundIn, &address) ) + return address; + + if ( (undefinedSymbol->n_type & N_PEXT) != 0 ) { + // don't know why the static linker did not eliminate the internal reference to a private extern definition + *foundIn = this; + return this->getSymbolAddress(undefinedSymbol, context, false); + } + else if ( (undefinedSymbol->n_desc & N_WEAK_REF) != 0 ) { + // if definition not found and reference is weak return 0 + return 0; + } + + // nowhere to be found + throwSymbolNotFound(context, symbolName, this->getPath(), "", target->getPath()); + } +} + + + +// returns if 'addr' is within the address range of section 'sectionIndex' +// fSlide is not used. 'addr' is assumed to be a prebound address in this image +bool ImageLoaderMachOClassic::isAddrInSection(uintptr_t addr, uint8_t sectionIndex) +{ + uint8_t currentSectionIndex = 1; + const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds; + const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; + const struct load_command* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + if ( cmd->cmd == LC_SEGMENT_COMMAND ) { + const struct macho_segment_command* seg = (struct macho_segment_command*)cmd; + if ( (currentSectionIndex <= sectionIndex) && (sectionIndex < currentSectionIndex+seg->nsects) ) { + // 'sectionIndex' is in this segment, get section info + const struct macho_section* const sectionsStart = (struct macho_section*)((char*)seg + sizeof(struct macho_segment_command)); + const struct macho_section* const section = §ionsStart[sectionIndex-currentSectionIndex]; + return ( (section->addr <= addr) && (addr < section->addr+section->size) ); + } + else { + // 'sectionIndex' not in this segment, skip to next segment + currentSectionIndex += seg->nsects; + } + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + + return false; +} + +void ImageLoaderMachOClassic::doBindExternalRelocations(const LinkContext& context) +{ + const uintptr_t relocBase = this->getRelocBase(); + const bool twoLevel = this->usesTwoLevelNameSpace(); + const bool prebound = this->isPrebindable(); + +#if TEXT_RELOC_SUPPORT + // if there are __TEXT fixups, temporarily make __TEXT writable + if ( fTextSegmentBinds ) + this->makeTextSegmentWritable(context, true); +#endif + // cache last lookup + const struct macho_nlist* lastUndefinedSymbol = NULL; + uintptr_t symbolAddr = 0; + const ImageLoader* image = NULL; + + // loop through all external relocation records and bind each + const relocation_info* const relocsStart = (struct relocation_info*)(&fLinkEditBase[fDynamicInfo->extreloff]); + const relocation_info* const relocsEnd = &relocsStart[fDynamicInfo->nextrel]; + for (const relocation_info* reloc=relocsStart; reloc < relocsEnd; ++reloc) { + if (reloc->r_length == RELOC_SIZE) { + switch(reloc->r_type) { + case POINTER_RELOC: + { + const struct macho_nlist* undefinedSymbol = &fSymbolTable[reloc->r_symbolnum]; + uintptr_t* location = ((uintptr_t*)(reloc->r_address + relocBase)); + if ( ! this->containsAddress((void*)location) ) + dyld::throwf("external reloc %p not in mapped image %s\n", (void*)location, this->getPath()); + uintptr_t value = *location; + bool symbolAddrCached = true; + #if __i386__ + if ( reloc->r_pcrel ) { + value += (uintptr_t)location + 4 - fSlide; + } + #endif + if ( prebound ) { + // we are doing relocations, so prebinding was not usable + // in a prebound executable, the n_value field of an undefined symbol is set to the address where the symbol was found when prebound + // so, subtracting that gives the initial displacement which we need to add to the newly found symbol address + // if mach-o relocation structs had an "addend" field this complication would not be necessary. + if ( ((undefinedSymbol->n_type & N_TYPE) == N_SECT) && ((undefinedSymbol->n_desc & N_WEAK_DEF) != 0) ) { + // weak symbols need special casing, since *location may have been prebound to a definition in another image. + // If *location is currently prebound to somewhere in the same section as the weak definition, we assume + // that we can subtract off the weak symbol address to get the addend. + // If prebound elsewhere, we've lost the addend and have to assume it is zero. + // The prebinding to elsewhere only happens with 10.4+ update_prebinding which only operates on a small set of Apple dylibs + if ( (value == undefinedSymbol->n_value) || this->isAddrInSection(value, undefinedSymbol->n_sect) ) { + value -= undefinedSymbol->n_value; + #if __arm__ + // if weak and thumb subtract off extra thumb bit + if ( (undefinedSymbol->n_desc & N_ARM_THUMB_DEF) != 0 ) + value -= 1; + #endif + } + else + value = 0; + } + #if __arm__ + else if ( ((undefinedSymbol->n_type & N_TYPE) == N_SECT) && ((undefinedSymbol->n_desc & N_ARM_THUMB_DEF) != 0) ) { + // it was prebound to a defined symbol for thumb code in the same linkage unit + // we need to subtract off one to get real addend + value -= (undefinedSymbol->n_value+1); + } + #endif + else { + // is undefined or non-weak symbol, so do subtraction to get addend + value -= undefinedSymbol->n_value; + } + } + // if undefinedSymbol is same as last time, then symbolAddr and image will resolve to the same too + if ( undefinedSymbol != lastUndefinedSymbol ) { + bool dontCoalesce = true; + if ( symbolIsWeakReference(undefinedSymbol) ) { + // when weakbind() is run on a classic mach-o encoding, it won't try + // to coalesce N_REF_TO_WEAK symbols because they are not in the sorted + // range of global symbols. To handle that case we do the coalesing now. + dontCoalesce = false; + } + symbolAddr = this->resolveUndefined(context, undefinedSymbol, twoLevel, dontCoalesce, false, &image); + lastUndefinedSymbol = undefinedSymbol; + symbolAddrCached = false; + } + if ( context.verboseBind ) { + const char *path = NULL; + if ( image != NULL ) { + path = image->getShortName(); + } + const char* cachedString = "(cached)"; + if ( !symbolAddrCached ) + cachedString = ""; + if ( value == 0 ) { + dyld::log("dyld: bind: %s:0x%08lX = %s:%s, *0x%08lX = 0x%08lX%s\n", + this->getShortName(), (uintptr_t)location, + path, &fStrings[undefinedSymbol->n_un.n_strx], (uintptr_t)location, symbolAddr, cachedString); + } + else { + dyld::log("dyld: bind: %s:0x%08lX = %s:%s, *0x%08lX = 0x%08lX%s + %ld\n", + this->getShortName(), (uintptr_t)location, + path, &fStrings[undefinedSymbol->n_un.n_strx], (uintptr_t)location, symbolAddr, cachedString, value); + } + } + value += symbolAddr; + #if __i386__ + if ( reloc->r_pcrel ) { + *location = value - ((uintptr_t)location + 4); + } + else { + // don't dirty page if prebound value was correct + if ( !prebound || (*location != value) ) + *location = value; + } + #else + // don't dirty page if prebound value was correct + if ( !prebound || (*location != value) ) + *location = value; + #endif + // update stats + ++fgTotalBindFixups; + } + break; + default: + throw "unknown external relocation type"; + } + } + else { + throw "bad external relocation length"; + } + } + +#if TEXT_RELOC_SUPPORT + // if there were __TEXT fixups, restore write protection + if ( fTextSegmentBinds ) { + this->makeTextSegmentWritable(context, true); + } +#endif +} + + + +uintptr_t ImageLoaderMachOClassic::bindIndirectSymbol(uintptr_t* ptrToBind, const struct macho_section* sect, const char* symbolName, uintptr_t targetAddr, const ImageLoader* targetImage, const LinkContext& context) +{ + if ( context.verboseBind ) { + const char* path = NULL; + if ( targetImage != NULL ) + path = targetImage->getShortName(); + dyld::log("dyld: bind indirect sym: %s:%s$%s = %s:%s, *0x%08lx = 0x%08lx\n", + this->getShortName(), symbolName, (((sect->flags & SECTION_TYPE)==S_NON_LAZY_SYMBOL_POINTERS) ? "non_lazy_ptr" : "lazy_ptr"), + ((path != NULL) ? path : ""), symbolName, (uintptr_t)ptrToBind, targetAddr); + } + if ( context.bindingHandler != NULL ) { + const char* path = NULL; + if ( targetImage != NULL ) + path = targetImage->getShortName(); + targetAddr = (uintptr_t)context.bindingHandler(path, symbolName, (void *)targetAddr); + } +#if __i386__ + // i386 has special self-modifying stubs that change from "CALL rel32" to "JMP rel32" + if ( ((sect->flags & SECTION_TYPE) == S_SYMBOL_STUBS) && ((sect->flags & S_ATTR_SELF_MODIFYING_CODE) != 0) && (sect->reserved2 == 5) ) { + uint32_t rel32 = targetAddr - (((uint32_t)ptrToBind)+5); + // re-write instruction in a thread-safe manner + // use 8-byte compare-and-swap to alter 5-byte jump table entries + // loop is required in case the extra three bytes that cover the next entry are altered by another thread + bool done = false; + while ( !done ) { + volatile int64_t* jumpPtr = (int64_t*)ptrToBind; + int pad = 0; + // By default the three extra bytes swapped follow the 5-byte JMP. + // But, if the 5-byte jump is up against the end of the __IMPORT segment + // We don't want to access bytes off the end of the segment, so we shift + // the extra bytes to precede the 5-byte JMP. + if ( (((uint32_t)ptrToBind + 8) & 0x00000FFC) == 0x00000000 ) { + jumpPtr = (int64_t*)((uint32_t)ptrToBind - 3); + pad = 3; + } + int64_t oldEntry = *jumpPtr; + union { + int64_t int64; + uint8_t bytes[8]; + } newEntry; + newEntry.int64 = oldEntry; + newEntry.bytes[pad+0] = 0xE9; // JMP rel32 + newEntry.bytes[pad+1] = rel32 & 0xFF; + newEntry.bytes[pad+2] = (rel32 >> 8) & 0xFF; + newEntry.bytes[pad+3] = (rel32 >> 16) & 0xFF; + newEntry.bytes[pad+4] = (rel32 >> 24) & 0xFF; + done = OSAtomicCompareAndSwap64Barrier(oldEntry, newEntry.int64, (int64_t*)jumpPtr); + } + } + else +#endif + *ptrToBind = targetAddr; + return targetAddr; +} + +uintptr_t ImageLoaderMachOClassic::doBindFastLazySymbol(uint32_t lazyBindingInfoOffset, const LinkContext& context, void (*lock)(), void (*unlock)()) +{ + throw "compressed LINKEDIT lazy binder called with classic LINKEDIT"; +} + +uintptr_t ImageLoaderMachOClassic::doBindLazySymbol(uintptr_t* lazyPointer, const LinkContext& context) +{ + // scan for all lazy-pointer sections + const bool twoLevel = this->usesTwoLevelNameSpace(); + const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds; + const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; + const struct load_command* cmd = cmds; + const uint32_t* const indirectTable = (uint32_t*)&fLinkEditBase[fDynamicInfo->indirectsymoff]; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd) { + case LC_SEGMENT_COMMAND: + { + const struct macho_segment_command* seg = (struct macho_segment_command*)cmd; + const struct macho_section* const sectionsStart = (struct macho_section*)((char*)seg + sizeof(struct macho_segment_command)); + const struct macho_section* const sectionsEnd = §ionsStart[seg->nsects]; + for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) { + const uint8_t type = sect->flags & SECTION_TYPE; + uint32_t symbolIndex = INDIRECT_SYMBOL_LOCAL; + if ( type == S_LAZY_SYMBOL_POINTERS ) { + const size_t pointerCount = sect->size / sizeof(uintptr_t); + uintptr_t* const symbolPointers = (uintptr_t*)(sect->addr + fSlide); + if ( (lazyPointer >= symbolPointers) && (lazyPointer < &symbolPointers[pointerCount]) ) { + const uint32_t indirectTableOffset = sect->reserved1; + const size_t lazyIndex = lazyPointer - symbolPointers; + symbolIndex = indirectTable[indirectTableOffset + lazyIndex]; + } + } + #if __i386__ + else if ( (type == S_SYMBOL_STUBS) && (sect->flags & S_ATTR_SELF_MODIFYING_CODE) && (sect->reserved2 == 5) ) { + // 5 bytes stubs on i386 are new "fast stubs" + uint8_t* const jmpTableBase = (uint8_t*)(sect->addr + fSlide); + uint8_t* const jmpTableEnd = jmpTableBase + sect->size; + // initial CALL instruction in jump table leaves pointer to next entry, so back up + uint8_t* const jmpTableEntryToPatch = ((uint8_t*)lazyPointer) - 5; + lazyPointer = (uintptr_t*)jmpTableEntryToPatch; + if ( (jmpTableEntryToPatch >= jmpTableBase) && (jmpTableEntryToPatch < jmpTableEnd) ) { + const uint32_t indirectTableOffset = sect->reserved1; + const uint32_t entryIndex = (jmpTableEntryToPatch - jmpTableBase)/5; + symbolIndex = indirectTable[indirectTableOffset + entryIndex]; + } + } + #endif + if ( symbolIndex != INDIRECT_SYMBOL_ABS && symbolIndex != INDIRECT_SYMBOL_LOCAL ) { + const char* symbolName = &fStrings[fSymbolTable[symbolIndex].n_un.n_strx]; + const ImageLoader* image = NULL; + uintptr_t symbolAddr = this->resolveUndefined(context, &fSymbolTable[symbolIndex], twoLevel, false, true, &image); + symbolAddr = this->bindIndirectSymbol(lazyPointer, sect, symbolName, symbolAddr, image, context); + ++fgTotalLazyBindFixups; + return symbolAddr; + } + } + } + break; + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + dyld::throwf("lazy pointer not found at address %p in image %s", lazyPointer, this->getPath()); +} + + + +void ImageLoaderMachOClassic::initializeCoalIterator(CoalIterator& it, unsigned int loadOrder, unsigned) +{ + it.image = this; + it.symbolName = " "; + it.loadOrder = loadOrder; + it.weakSymbol = false; + it.symbolMatches = false; + it.done = false; + it.type = 0; + if ( fDynamicInfo->tocoff != 0 ) { + it.curIndex = 0; + it.endIndex = fDynamicInfo->ntoc; + } + else { + it.curIndex = 0; + it.endIndex = fDynamicInfo->nextdefsym; + } +} + + +bool ImageLoaderMachOClassic::incrementCoalIterator(CoalIterator& it) +{ + if ( it.done ) + return false; + + if ( fDynamicInfo->tocoff != 0 ) { + if ( it.curIndex >= fDynamicInfo->ntoc ) { + it.done = true; + it.symbolName = "~~~"; + return true; + } + else { + const dylib_table_of_contents* toc = (dylib_table_of_contents*)&fLinkEditBase[fDynamicInfo->tocoff]; + const uint32_t index = toc[it.curIndex].symbol_index; + const struct macho_nlist* sym = &fSymbolTable[index]; + const char* symStr = &fStrings[sym->n_un.n_strx]; + it.symbolName = symStr; + it.weakSymbol = (sym->n_desc & N_WEAK_DEF); + it.symbolMatches = false; + it.type = 0; // clear flag that says we applied updates for this symbol + //dyld::log("incrementCoalIterator() curIndex=%ld, symbolName=%s in %s\n", it.curIndex, symStr, this->getPath()); + it.curIndex++; + return false; + } + } + else { + if ( it.curIndex >= fDynamicInfo->nextdefsym ) { + it.done = true; + it.symbolName = "~~~"; + return true; + } + else { + const struct macho_nlist* sym = &fSymbolTable[fDynamicInfo->iextdefsym+it.curIndex]; + const char* symStr = &fStrings[sym->n_un.n_strx]; + it.symbolName = symStr; + it.weakSymbol = (sym->n_desc & N_WEAK_DEF); + it.symbolMatches = false; + it.type = 0; // clear flag that says we applied updates for this symbol + //dyld::log("incrementCoalIterator() curIndex=%ld, symbolName=%s in %s\n", it.curIndex, symStr, this->getPath()); + it.curIndex++; + return false; + } + } + + return false; +} + +uintptr_t ImageLoaderMachOClassic::getAddressCoalIterator(CoalIterator& it, const LinkContext& context) +{ + uint32_t symbol_index = 0; + if ( fDynamicInfo->tocoff != 0 ) { + const dylib_table_of_contents* toc = (dylib_table_of_contents*)&fLinkEditBase[fDynamicInfo->tocoff]; + symbol_index = toc[it.curIndex-1].symbol_index; + } + else { + symbol_index = fDynamicInfo->iextdefsym + (uint32_t)it.curIndex - 1; + } + const struct macho_nlist* sym = &fSymbolTable[symbol_index]; + //dyld::log("getAddressCoalIterator() => 0x%llX, %s symbol_index=%d, in %s\n", (uint64_t)(sym->n_value + fSlide), &fStrings[sym->n_un.n_strx], symbol_index, this->getPath()); +#if __arm__ + // processor assumes code address with low bit set is thumb + if (sym->n_desc & N_ARM_THUMB_DEF) + return (sym->n_value | 1) + fSlide ; + else + return sym->n_value + fSlide; +#else + return sym->n_value + fSlide; +#endif +} + + +void ImageLoaderMachOClassic::updateUsesCoalIterator(CoalIterator& it, uintptr_t value, ImageLoader* targetImage, unsigned targetIndex, const LinkContext& context) +{ + // flat_namespace images with classic LINKEDIT do not need late coalescing. + // They still need to be iterated becuase they may implement + // something needed by other coalescing images. + // But they need no updating because during the bind phase every symbol lookup is a full scan. + if ( !this->usesTwoLevelNameSpace() ) + return; + + // weak binding done too early with inserted libraries + if ( this->getState() < dyld_image_state_bound ) + return; + + uint32_t symbol_index = 0; + if ( fDynamicInfo->tocoff != 0 ) { + const dylib_table_of_contents* toc = (dylib_table_of_contents*)&fLinkEditBase[fDynamicInfo->tocoff]; + symbol_index = toc[it.curIndex-1].symbol_index; + } + else { + symbol_index = fDynamicInfo->iextdefsym + (uint32_t)it.curIndex - 1; + } + + // if this image's copy of the symbol is not a weak definition nor a weak reference then nothing to coalesce here + if ( !symbolIsWeakReference(&fSymbolTable[symbol_index]) && !symbolIsWeakDefinition(&fSymbolTable[symbol_index]) ) { + return; + } + + // malformed dylib with duplicate weak symbols causes re-binding + if ( it.type ) + return; + + bool boundSomething = false; + // scan external relocations for uses of symbol_index + const uintptr_t relocBase = this->getRelocBase(); + const bool prebound = this->isPrebindable(); + const relocation_info* const relocsStart = (struct relocation_info*)(&fLinkEditBase[fDynamicInfo->extreloff]); + const relocation_info* const relocsEnd = &relocsStart[fDynamicInfo->nextrel]; + for (const relocation_info* reloc=relocsStart; reloc < relocsEnd; ++reloc) { + if ( reloc->r_symbolnum == symbol_index ) { + //dyld::log("found external reloc using symbol_index=%d in %s\n",symbol_index, this->getPath()); + const struct macho_nlist* undefinedSymbol = &fSymbolTable[reloc->r_symbolnum]; + const char* symbolName = &fStrings[undefinedSymbol->n_un.n_strx]; + uintptr_t* location = ((uintptr_t*)(reloc->r_address + relocBase)); + const uintptr_t initialValue = *location; + uintptr_t addend = 0; + if ( prebound ) { + // we are doing relocations, so prebinding was not usable + // in a prebound executable, the n_value field of an undefined symbol is set to the address where the symbol was found when prebound + // so, subtracting that gives the initial displacement which we need to add to the newly found symbol address + // if mach-o relocation structs had an "addend" field this complication would not be necessary. + if ( ((undefinedSymbol->n_type & N_TYPE) == N_SECT) && ((undefinedSymbol->n_desc & N_WEAK_DEF) != 0) ) { + // weak symbols need special casing, since *location may have been prebound to a definition in another image. + // If *location is currently prebound to somewhere in the same section as the weak definition, we assume + // that we can subtract off the weak symbol address to get the addend. + // If prebound elsewhere, we've lost the addend and have to assume it is zero. + // The prebinding to elsewhere only happens with 10.4+ update_prebinding which only operates on a small set of Apple dylibs + if ( (initialValue == undefinedSymbol->n_value) || this->isAddrInSection(initialValue, undefinedSymbol->n_sect) ) { + addend = initialValue - undefinedSymbol->n_value; + #if __arm__ + // if weak and thumb subtract off extra thumb bit + if ( (undefinedSymbol->n_desc & N_ARM_THUMB_DEF) != 0 ) + addend &= -2; + #endif + } + } + #if __arm__ + else if ( ((undefinedSymbol->n_type & N_TYPE) == N_SECT) && ((undefinedSymbol->n_desc & N_ARM_THUMB_DEF) != 0) ) { + // it was prebound to a defined symbol for thumb code in the same linkage unit + // we need to subtract off one to get real addend + addend = initialValue - (undefinedSymbol->n_value+1); + } + #endif + else { + // is undefined or non-weak symbol, so do subtraction to get addend + addend = initialValue - undefinedSymbol->n_value; + } + } + else { + // non-prebound case + if ( ((undefinedSymbol->n_type & N_TYPE) == N_SECT) && ((undefinedSymbol->n_desc & N_WEAK_DEF) != 0) ) { + // if target is weak-def in same linkage unit, then bind phase has already set initialValue + // to be definition address plus addend + //dyld::log("weak def, initialValue=0x%lX, undefAddr=0x%lX\n", initialValue, undefinedSymbol->n_value+fSlide); + addend = initialValue - (undefinedSymbol->n_value + fSlide); + #if __arm__ + // if weak and thumb subtract off extra thumb bit + if ( (undefinedSymbol->n_desc & N_ARM_THUMB_DEF) != 0 ) + addend &= -2; + #endif + } + else { + // nothing fixed up yet, addend is just initial value + //dyld::log("addend=0x%lX\n", initialValue); + addend = initialValue; + } + } + + uint8_t type = BIND_TYPE_POINTER; + #if __i386__ + if ( reloc->r_pcrel ) + type = BIND_TYPE_TEXT_PCREL32; + #endif + this->bindLocation(context, (uintptr_t)location, value, type, symbolName, addend, this->getPath(), targetImage ? targetImage->getPath() : NULL, "weak "); + boundSomething = true; + } + } + + // scan lazy and non-lazy pointers for uses of symbol_index + const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds; + const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; + const struct load_command* cmd = cmds; + const uint32_t* const indirectTable = (uint32_t*)&fLinkEditBase[fDynamicInfo->indirectsymoff]; + for (uint32_t i = 0; i < cmd_count; ++i) { + if ( cmd->cmd == LC_SEGMENT_COMMAND ) { + const struct macho_segment_command* seg = (struct macho_segment_command*)cmd; + const struct macho_section* const sectionsStart = (struct macho_section*)((char*)seg + sizeof(struct macho_segment_command)); + const struct macho_section* const sectionsEnd = §ionsStart[seg->nsects]; + for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) { + uint32_t elementSize = sizeof(uintptr_t); + switch ( sect->flags & SECTION_TYPE ) { + #if __i386__ + case S_SYMBOL_STUBS: + if ( ((sect->flags & S_ATTR_SELF_MODIFYING_CODE) ==0) || (sect->reserved2 != 5) ) + continue; + elementSize = 5; + #endif + case S_NON_LAZY_SYMBOL_POINTERS: + case S_LAZY_SYMBOL_POINTERS: + { + size_t elementCount = sect->size / elementSize; + const uint32_t indirectTableOffset = sect->reserved1; + uint8_t* ptrToBind = (uint8_t*)(sect->addr + fSlide); + //dyld::log(" scanning section %s of %s starting at %p\n", sect->sectname, this->getShortName(), ptrToBind); + for (size_t j=0; j < elementCount; ++j, ptrToBind += elementSize) { + if ( indirectTable[indirectTableOffset + j] == symbol_index ) { + //dyld::log(" found symbol index match at %d/%d, ptrToBind=%p\n", j, elementCount, ptrToBind); + // update pointer + this->bindIndirectSymbol((uintptr_t*)ptrToBind, sect, it.symbolName, value, targetImage, context); + boundSomething = true; + } + } + } + break; + } + } + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + if ( boundSomething && (targetImage != this) ) { + context.addDynamicReference(this, targetImage); + } + + // mark that this symbol has already been bound, so we don't try to bind again + it.type = 1; +} + + +void ImageLoaderMachOClassic::bindIndirectSymbolPointers(const LinkContext& context, bool bindNonLazys, bool bindLazys) +{ + // scan for all non-lazy-pointer sections + const bool twoLevel = this->usesTwoLevelNameSpace(); + const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds; + const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; + const struct load_command* cmd = cmds; + const uint32_t* const indirectTable = (uint32_t*)&fLinkEditBase[fDynamicInfo->indirectsymoff]; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd) { + case LC_SEGMENT_COMMAND: + { + const struct macho_segment_command* seg = (struct macho_segment_command*)cmd; + const struct macho_section* const sectionsStart = (struct macho_section*)((char*)seg + sizeof(struct macho_segment_command)); + const struct macho_section* const sectionsEnd = §ionsStart[seg->nsects]; + for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) { + bool isLazySymbol = false; + const uint8_t type = sect->flags & SECTION_TYPE; + uint32_t elementSize = sizeof(uintptr_t); + size_t elementCount = sect->size / elementSize; + if ( type == S_NON_LAZY_SYMBOL_POINTERS ) { + if ( ! bindNonLazys ) + continue; + } + else if ( type == S_LAZY_SYMBOL_POINTERS ) { + // process each symbol pointer in this section + fgTotalPossibleLazyBindFixups += elementCount; + isLazySymbol = true; + if ( ! bindLazys ) + continue; + } + #if __i386__ + else if ( (type == S_SYMBOL_STUBS) && (sect->flags & S_ATTR_SELF_MODIFYING_CODE) && (sect->reserved2 == 5) ) { + // process each jmp entry in this section + elementCount = sect->size / 5; + elementSize = 5; + fgTotalPossibleLazyBindFixups += elementCount; + isLazySymbol = true; + if ( ! bindLazys ) + continue; + } + #endif + else { + continue; + } + const uint32_t indirectTableOffset = sect->reserved1; + uint8_t* ptrToBind = (uint8_t*)(sect->addr + fSlide); + for (size_t j=0; j < elementCount; ++j, ptrToBind += elementSize) { + #if LINKEDIT_USAGE_DEBUG + noteAccessedLinkEditAddress(&indirectTable[indirectTableOffset + j]); + #endif + uint32_t symbolIndex = indirectTable[indirectTableOffset + j]; + if ( symbolIndex == INDIRECT_SYMBOL_LOCAL) { + *((uintptr_t*)ptrToBind) += this->fSlide; + } + else if ( symbolIndex == INDIRECT_SYMBOL_ABS) { + // do nothing since already has absolute address + } + else { + const struct macho_nlist* sym = &fSymbolTable[symbolIndex]; + if ( symbolIndex == 0 ) { + // This could be rdar://problem/3534709 + if ( ((const macho_header*)fMachOData)->filetype == MH_EXECUTE ) { + static bool alreadyWarned = false; + if ( (sym->n_type & N_TYPE) != N_UNDF ) { + // The indirect table parallels the (non)lazy pointer sections. For + // instance, to find info about the fifth lazy pointer you look at the + // fifth entry in the indirect table. (try otool -Iv on a file). + // The entry in the indirect table contains an index into the symbol table. + + // The bug in ld caused the entry in the indirect table to be zero + // (instead of a magic value that means a local symbol). So, if the + // symbolIndex == 0, we may be encountering the bug, or 0 may be a valid + // symbol table index. The check I put in place is to see if the zero'th + // symbol table entry is an import entry (usually it is a local symbol + // definition). + if ( context.verboseWarnings && !alreadyWarned ) { + dyld::log("dyld: malformed executable '%s', skipping indirect symbol to %s\n", + this->getPath(), &fStrings[sym->n_un.n_strx]); + alreadyWarned = true; + } + continue; + } + } + } + const ImageLoader* image = NULL; + // let weak definitions resolve to themselves, later coalescing may overwrite them + bool dontCoalesce = true; + if ( bindLazys && isLazySymbol ) { + // if this is something normally lazy bound, but we are forcing + // it to be bound now, do coalescing + dontCoalesce = false; + } + if ( symbolIsWeakReference(sym) ) { + // when weakbind() is run on a classic mach-o encoding, it won't try + // to coalesce N_REF_TO_WEAK symbols because they are not in the sorted + // range of global symbols. To handle that case we do the coalesing now. + dontCoalesce = false; + } + uintptr_t symbolAddr = resolveUndefined(context, sym, twoLevel, dontCoalesce, false, &image); + // update pointer + symbolAddr = this->bindIndirectSymbol((uintptr_t*)ptrToBind, sect, &fStrings[sym->n_un.n_strx], symbolAddr, image, context); + // update stats + ++fgTotalBindFixups; + } + } + } + } + break; + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } +} + + + +#if __i386__ +void ImageLoaderMachOClassic::initializeLazyStubs(const LinkContext& context) +{ + if ( ! this->usablePrebinding(context) ) { + // reset all "fast" stubs + const macho_header* mh = (macho_header*)fMachOData; + const uint32_t cmd_count = mh->ncmds; + const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; + const struct load_command* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd) { + case LC_SEGMENT_COMMAND: + { + const struct macho_segment_command* seg = (struct macho_segment_command*)cmd; + const struct macho_section* const sectionsStart = (struct macho_section*)((char*)seg + sizeof(struct macho_segment_command)); + const struct macho_section* const sectionsEnd = §ionsStart[seg->nsects]; + for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) { + const uint8_t type = sect->flags & SECTION_TYPE; + if ( (type == S_SYMBOL_STUBS) && (sect->flags & S_ATTR_SELF_MODIFYING_CODE) && (sect->reserved2 == 5) ) { + // reset each jmp entry in this section + const uint32_t indirectTableOffset = sect->reserved1; + const uint32_t* const indirectTable = (uint32_t*)&fLinkEditBase[fDynamicInfo->indirectsymoff]; + uint8_t* start = (uint8_t*)(sect->addr + this->fSlide); + uint8_t* end = start + sect->size; + uintptr_t dyldHandler = (uintptr_t)&stub_binding_helper_i386_old; + uint32_t entryIndex = 0; + for (uint8_t* entry = start; entry < end; entry += 5, ++entryIndex) { + bool installLazyHandler = true; + // jump table entries that cross a (64-byte) cache line boundary have the potential to cause crashes + // if the instruction is updated by one thread while being executed by another + if ( ((uint32_t)entry & 0xFFFFFFC0) != ((uint32_t)entry+4 & 0xFFFFFFC0) ) { + // need to bind this now to avoid a potential problem if bound lazily + uint32_t symbolIndex = indirectTable[indirectTableOffset + entryIndex]; + // the latest linker marks 64-byte crossing stubs with INDIRECT_SYMBOL_ABS so they are not used + if ( symbolIndex != INDIRECT_SYMBOL_ABS ) { + const char* symbolName = &fStrings[fSymbolTable[symbolIndex].n_un.n_strx]; + const ImageLoader* image = NULL; + try { + uintptr_t symbolAddr = this->resolveUndefined(context, &fSymbolTable[symbolIndex], this->usesTwoLevelNameSpace(), false, false, &image); + symbolAddr = this->bindIndirectSymbol((uintptr_t*)entry, sect, symbolName, symbolAddr, image, context); + ++fgTotalBindFixups; + uint32_t rel32 = symbolAddr - (((uint32_t)entry)+5); + entry[0] = 0xE9; // JMP rel32 + entry[1] = rel32 & 0xFF; + entry[2] = (rel32 >> 8) & 0xFF; + entry[3] = (rel32 >> 16) & 0xFF; + entry[4] = (rel32 >> 24) & 0xFF; + installLazyHandler = false; + } + catch (const char* msg) { + // ignore errors when binding symbols early + // maybe the function is never called, and therefore erroring out now would be a regression + } + } + } + if ( installLazyHandler ) { + uint32_t rel32 = dyldHandler - (((uint32_t)entry)+5); + entry[0] = 0xE8; // CALL rel32 + entry[1] = rel32 & 0xFF; + entry[2] = (rel32 >> 8) & 0xFF; + entry[3] = (rel32 >> 16) & 0xFF; + entry[4] = (rel32 >> 24) & 0xFF; + } + } + } + } + } + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + } +} +#endif // __i386__ + + +void ImageLoaderMachOClassic::doBind(const LinkContext& context, bool forceLazysBound) +{ + CRSetCrashLogMessage2(this->getPath()); +#if __i386__ + this->initializeLazyStubs(context); +#endif + + // if prebound and loaded at prebound address, and all libraries are same as when this was prebound, then no need to bind + // note: flat-namespace binaries need to have imports rebound (even if correctly prebound) + if ( this->usablePrebinding(context) ) { + // binding already up to date + } + else { + // no valid prebinding, so bind symbols. + // values bound by name are stored two different ways in classic mach-o: + + #if TEXT_RELOC_SUPPORT + // if there are __TEXT fixups, temporarily make __TEXT writable + if ( fTextSegmentBinds ) + this->makeTextSegmentWritable(context, true); + #endif + + // 1) external relocations are used for data initialized to external symbols + this->doBindExternalRelocations(context); + + // 2) "indirect symbols" are used for code references to external symbols + // if this image is in the shared cache, there is no way to reset the lazy pointers, so bind them now + this->bindIndirectSymbolPointers(context, true, forceLazysBound || fInSharedCache); + + #if TEXT_RELOC_SUPPORT + // if there were __TEXT fixups, restore write protection + if ( fTextSegmentBinds ) + this->makeTextSegmentWritable(context, false); + #endif + } + + // set up dyld entry points in image + this->setupLazyPointerHandler(context); + + CRSetCrashLogMessage2(NULL); +} + +void ImageLoaderMachOClassic::doBindJustLazies(const LinkContext& context) +{ + // some API called requested that all lazy pointers in this image be force bound + this->bindIndirectSymbolPointers(context, false, true); +} + +void ImageLoaderMachOClassic::doInterpose(const LinkContext& context) +{ + if ( context.verboseInterposing ) + dyld::log("dyld: interposing %lu tuples onto: %s\n", fgInterposingTuples.size(), this->getPath()); + + // scan indirect symbols + const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds; + const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; + const struct load_command* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd) { + case LC_SEGMENT_COMMAND: + { + const struct macho_segment_command* seg = (struct macho_segment_command*)cmd; + const struct macho_section* const sectionsStart = (struct macho_section*)((char*)seg + sizeof(struct macho_segment_command)); + const struct macho_section* const sectionsEnd = §ionsStart[seg->nsects]; + for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) { + const uint8_t type = sect->flags & SECTION_TYPE; + if ( (type == S_NON_LAZY_SYMBOL_POINTERS) || (type == S_LAZY_SYMBOL_POINTERS) ) { + const size_t pointerCount = sect->size / sizeof(uintptr_t); + uintptr_t* const symbolPointers = (uintptr_t*)(sect->addr + fSlide); + for (size_t pointerIndex=0; pointerIndex < pointerCount; ++pointerIndex) { + uintptr_t newValue = interposedAddress(context, symbolPointers[pointerIndex], this); + if ( newValue != symbolPointers[pointerIndex] ) + symbolPointers[pointerIndex] = newValue; + } + } + #if __i386__ + // i386 has special self-modifying stubs that might be prebound to "JMP rel32" that need checking + else if ( (type == S_SYMBOL_STUBS) && ((sect->flags & S_ATTR_SELF_MODIFYING_CODE) != 0) && (sect->reserved2 == 5) ) { + // check each jmp entry in this section + uint8_t* start = (uint8_t*)(sect->addr + this->fSlide); + uint8_t* end = start + sect->size; + for (uint8_t* entry = start; entry < end; entry += 5) { + if ( entry[0] == 0xE9 ) { // 0xE9 == JMP + uint32_t rel32 = *((uint32_t*)&entry[1]); // assume unaligned load of uint32_t is ok + uint32_t target = (uint32_t)&entry[5] + rel32; + uint32_t newTarget = interposedAddress(context, target, this); + if ( newTarget != target ) { + uint32_t newRel32 = newTarget - (uint32_t)&entry[5]; + *((uint32_t*)&entry[1]) = newRel32; // assume unaligned store of uint32_t is ok + } + } + } + } + #endif + } + } + break; + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + + // scan external relocations + const uintptr_t relocBase = this->getRelocBase(); + const relocation_info* const relocsStart = (struct relocation_info*)(&fLinkEditBase[fDynamicInfo->extreloff]); + const relocation_info* const relocsEnd = &relocsStart[fDynamicInfo->nextrel]; + for (const relocation_info* reloc=relocsStart; reloc < relocsEnd; ++reloc) { + if (reloc->r_length == RELOC_SIZE) { + switch(reloc->r_type) { + case POINTER_RELOC: + { + uintptr_t* location = ((uintptr_t*)(reloc->r_address + relocBase)); + uintptr_t value = *location; + uintptr_t newValue = interposedAddress(context, value, this); + if ( newValue != value ) + *location = newValue; + } + break; + } + } + } +} + +void ImageLoaderMachOClassic::dynamicInterpose(const LinkContext& context) +{ + if ( context.verboseInterposing ) + dyld::log("dyld: dynamic interposing %lu tuples onto image: %s\n", context.dynamicInterposeCount, this->getPath()); + + // scan indirect symbols + const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds; + const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; + const struct load_command* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd) { + case LC_SEGMENT_COMMAND: + { + const struct macho_segment_command* seg = (struct macho_segment_command*)cmd; + const struct macho_section* const sectionsStart = (struct macho_section*)((char*)seg + sizeof(struct macho_segment_command)); + const struct macho_section* const sectionsEnd = §ionsStart[seg->nsects]; + for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) { + const uint8_t type = sect->flags & SECTION_TYPE; + if ( (type == S_NON_LAZY_SYMBOL_POINTERS) || (type == S_LAZY_SYMBOL_POINTERS) ) { + const size_t pointerCount = sect->size / sizeof(uintptr_t); + uintptr_t* const symbolPointers = (uintptr_t*)(sect->addr + fSlide); + for (size_t pointerIndex=0; pointerIndex < pointerCount; ++pointerIndex) { + for(size_t j=0; j < context.dynamicInterposeCount; ++j) { + // replace all references to 'replacee' with 'replacement' + if ( symbolPointers[pointerIndex] == (uintptr_t)context.dynamicInterposeArray[j].replacee ) { + if ( context.verboseInterposing ) { + dyld::log("dyld: dynamic interposing: at %p replace %p with %p in %s\n", + &symbolPointers[pointerIndex], context.dynamicInterposeArray[j].replacee, context.dynamicInterposeArray[j].replacement, this->getPath()); + } + symbolPointers[pointerIndex] = (uintptr_t)context.dynamicInterposeArray[j].replacement; + } + } + } + } + } + } + break; + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + + // scan external relocations + const uintptr_t relocBase = this->getRelocBase(); + const relocation_info* const relocsStart = (struct relocation_info*)(&fLinkEditBase[fDynamicInfo->extreloff]); + const relocation_info* const relocsEnd = &relocsStart[fDynamicInfo->nextrel]; + for (const relocation_info* reloc=relocsStart; reloc < relocsEnd; ++reloc) { + if (reloc->r_length == RELOC_SIZE) { + switch(reloc->r_type) { + case POINTER_RELOC: + { + uintptr_t* location = ((uintptr_t*)(reloc->r_address + relocBase)); + for(size_t i=0; i < context.dynamicInterposeCount; ++i) { + // replace all references to 'replacee' with 'replacement' + if ( *location == (uintptr_t)context.dynamicInterposeArray[i].replacee ) { + if ( context.verboseInterposing ) { + dyld::log("dyld: dynamic interposing: at %p replace %p with %p in %s\n", + location, context.dynamicInterposeArray[i].replacee, context.dynamicInterposeArray[i].replacement, this->getPath()); + } + *location = (uintptr_t)context.dynamicInterposeArray[i].replacement; + } + } + } + break; + } + } + } +} + + +const char* ImageLoaderMachOClassic::findClosestSymbol(const void* addr, const void** closestAddr) const +{ + uintptr_t targetAddress = (uintptr_t)addr - fSlide; + const struct macho_nlist* bestSymbol = NULL; + // first walk all global symbols + const struct macho_nlist* const globalsStart = &fSymbolTable[fDynamicInfo->iextdefsym]; + const struct macho_nlist* const globalsEnd= &globalsStart[fDynamicInfo->nextdefsym]; + for (const struct macho_nlist* s = globalsStart; s < globalsEnd; ++s) { + if ( (s->n_type & N_TYPE) == N_SECT ) { + if ( bestSymbol == NULL ) { + if ( s->n_value <= targetAddress ) + bestSymbol = s; + } + else if ( (s->n_value <= targetAddress) && (bestSymbol->n_value < s->n_value) ) { + bestSymbol = s; + } + } + } + // next walk all local symbols + const struct macho_nlist* const localsStart = &fSymbolTable[fDynamicInfo->ilocalsym]; + const struct macho_nlist* const localsEnd= &localsStart[fDynamicInfo->nlocalsym]; + for (const struct macho_nlist* s = localsStart; s < localsEnd; ++s) { + if ( ((s->n_type & N_TYPE) == N_SECT) && ((s->n_type & N_STAB) == 0) ) { + if ( bestSymbol == NULL ) { + if ( s->n_value <= targetAddress ) + bestSymbol = s; + } + else if ( (s->n_value <= targetAddress) && (bestSymbol->n_value < s->n_value) ) { + bestSymbol = s; + } + } + } + if ( bestSymbol != NULL ) { +#if __arm__ + if (bestSymbol->n_desc & N_ARM_THUMB_DEF) + *closestAddr = (void*)((bestSymbol->n_value | 1) + fSlide); + else + *closestAddr = (void*)(bestSymbol->n_value + fSlide); +#else + *closestAddr = (void*)(bestSymbol->n_value + fSlide); +#endif + return &fStrings[bestSymbol->n_un.n_strx]; + } + return NULL; +} + + diff --git a/src/dyld-apple/src/ImageLoaderMachOClassic.h b/src/dyld-apple/src/ImageLoaderMachOClassic.h new file mode 100644 index 000000000..1f63ae09d --- /dev/null +++ b/src/dyld-apple/src/ImageLoaderMachOClassic.h @@ -0,0 +1,138 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2008 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@ + */ + + +#ifndef __IMAGELOADER_MACHO_CLASSIC__ +#define __IMAGELOADER_MACHO_CLASSIC__ + +#include + +#include "ImageLoaderMachO.h" + + +// +// ImageLoaderMachOClassic is the concrete subclass of ImageLoader which loads mach-o files +// that use the traditional LINKEDIT format. +// +class ImageLoaderMachOClassic : public ImageLoaderMachO { +public: + static ImageLoaderMachOClassic* instantiateMainExecutable(const macho_header* mh, uintptr_t slide, const char* path, + unsigned int segCount, unsigned int libCount, const LinkContext& context); + static ImageLoaderMachOClassic* instantiateFromFile(const char* path, int fd, const uint8_t* fileData, size_t lenFileData, + uint64_t offsetInFat, uint64_t lenInFat, const struct stat& info, + unsigned int segCount, unsigned int libCount, + const struct linkedit_data_command* codeSigCmd, const LinkContext& context); + static ImageLoaderMachOClassic* instantiateFromCache(const macho_header* mh, const char* path, long slide, const struct stat& info, + unsigned int segCount, unsigned int libCount, const LinkContext& context); + static ImageLoaderMachOClassic* instantiateFromMemory(const char* moduleName, const macho_header* mh, uint64_t len, + unsigned int segCount, unsigned int libCount, const LinkContext& context); + + virtual ~ImageLoaderMachOClassic(); + + virtual ImageLoader* libImage(unsigned int) const; + virtual bool libReExported(unsigned int) const; + virtual bool libIsUpward(unsigned int) const; + virtual void setLibImage(unsigned int, ImageLoader*, bool, bool); + virtual void doBind(const LinkContext& context, bool forceLazysBound); + virtual void doBindJustLazies(const LinkContext& context); + virtual uintptr_t doBindLazySymbol(uintptr_t* lazyPointer, const LinkContext& context); + virtual uintptr_t doBindFastLazySymbol(uint32_t lazyBindingInfoOffset, const LinkContext& context, void (*lock)(), void (*unlock)()); + virtual const char* findClosestSymbol(const void* addr, const void** closestAddr) const; + virtual void initializeCoalIterator(CoalIterator&, unsigned int loadOrder, unsigned); + virtual bool incrementCoalIterator(CoalIterator&); + virtual uintptr_t getAddressCoalIterator(CoalIterator&, const LinkContext& contex); + virtual void updateUsesCoalIterator(CoalIterator&, uintptr_t newAddr, ImageLoader* target, unsigned targetIndex, const LinkContext& context); + +protected: + virtual void doInterpose(const LinkContext& context); + virtual void dynamicInterpose(const LinkContext& context); + virtual void setDyldInfo(const dyld_info_command*) {} + virtual void setSymbolTableInfo(const macho_nlist*, const char*, const dysymtab_command*); + virtual bool isSubframeworkOf(const LinkContext& context, const ImageLoader* image) const; + virtual bool hasSubLibrary(const LinkContext& context, const ImageLoader* child) const; + virtual uint32_t* segmentCommandOffsets() const; + virtual void rebase(const LinkContext& context, uintptr_t slide); + virtual const ImageLoader::Symbol* findShallowExportedSymbol(const char* name, const ImageLoader** foundIn) const; + virtual bool containsSymbol(const void* addr) const; + virtual uintptr_t exportedSymbolAddress(const LinkContext& context, const Symbol* symbol, const ImageLoader* requestor, bool runResolver) const; + virtual bool exportedSymbolIsWeakDefintion(const Symbol* symbol) const; + virtual const char* exportedSymbolName(const Symbol* symbol) const; + virtual unsigned int exportedSymbolCount() const; + virtual const ImageLoader::Symbol* exportedSymbolIndexed(unsigned int) const; + virtual unsigned int importedSymbolCount() const; + virtual const ImageLoader::Symbol* importedSymbolIndexed(unsigned int) const; + virtual const char* importedSymbolName(const Symbol* symbol) const; +#if PREBOUND_IMAGE_SUPPORT + virtual void resetPreboundLazyPointers(const LinkContext& context); +#endif + + +private: + ImageLoaderMachOClassic(const macho_header* mh, const char* path, unsigned int segCount, + uint32_t segOffsets[], unsigned int libCount); + static ImageLoaderMachOClassic* instantiateStart(const macho_header* mh, const char* path, unsigned int segCount, unsigned int libCount); + void instantiateFinish(const LinkContext& context); + uintptr_t getRelocBase(); + void mapSegmentsClassic(int fd, uint64_t offsetInFat, uint64_t lenInFat, uint64_t fileLen, const LinkContext& context); + uintptr_t getFirstWritableSegmentAddress(); + const struct macho_nlist* binarySearch(const char* key, const char stringPool[], const struct macho_nlist symbols[], uint32_t symbolCount) const; + const struct macho_nlist* binarySearchWithToc(const char* key, const char stringPool[], const struct macho_nlist symbols[], + const struct dylib_table_of_contents toc[], uint32_t symbolCount, uint32_t hintIndex) const; + static bool symbolIsWeakReference(const struct macho_nlist* symbol); + static bool symbolIsWeakDefinition(const struct macho_nlist* symbol); + uintptr_t resolveUndefined(const LinkContext& context, const struct macho_nlist* symbol, bool twoLevel, + bool dontCoalesce, bool runResolver, const ImageLoader **foundIn); + uintptr_t getSymbolAddress(const macho_nlist*, const LinkContext& context, bool runResolver) const; + bool isAddrInSection(uintptr_t addr, uint8_t sectionIndex); + void doBindExternalRelocations(const LinkContext& context); + uintptr_t bindIndirectSymbol(uintptr_t* ptrToBind, const struct macho_section* sect, + const char* symbolName, uintptr_t targetAddr, + const ImageLoader* targetImage, const LinkContext& context); + void bindIndirectSymbolPointers(const LinkContext& context, bool bindNonLazys, bool bindLazys); + void initializeLazyStubs(const LinkContext& context); + void prefetchLINKEDIT(const LinkContext& context); +#if SPLIT_SEG_DYLIB_SUPPORT + unsigned int getExtraZeroFillEntriesCount(); + void initMappingTable(uint64_t offsetInFat, shared_file_mapping_np *mappingTable); +#endif + int mapSplitSegDylibOutsideSharedRegion(int fd, uint64_t offsetInFat, uint64_t lenInFat, uint64_t fileLen, const LinkContext& context); +#if SPLIT_SEG_SHARED_REGION_SUPPORT + int mapSplitSegDylibInfoSharedRegion(int fd, uint64_t offsetInFat, uint64_t lenInFat, uint64_t fileLen, const LinkContext& context); +#endif + + + const char* fStrings; + const struct macho_nlist* fSymbolTable; + const struct dysymtab_command* fDynamicInfo; + +}; + + + + +#endif // __IMAGELOADER_MACHO_CLASSIC__ + + + + diff --git a/src/dyld-apple/src/ImageLoaderMachOCompressed.cpp b/src/dyld-apple/src/ImageLoaderMachOCompressed.cpp new file mode 100644 index 000000000..fb3c04e25 --- /dev/null +++ b/src/dyld-apple/src/ImageLoaderMachOCompressed.cpp @@ -0,0 +1,1625 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2008 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@ + */ + + +#if __arm__ || __arm64__ + #include +#else + #include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ImageLoaderMachOCompressed.h" +#include "mach-o/dyld_images.h" + +#ifndef EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE + #define EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE 0x02 +#endif + +// relocation_info.r_length field has value 3 for 64-bit executables and value 2 for 32-bit executables +#if __LP64__ + #define RELOC_SIZE 3 + #define LC_SEGMENT_COMMAND LC_SEGMENT_64 + #define LC_ROUTINES_COMMAND LC_ROUTINES_64 + struct macho_segment_command : public segment_command_64 {}; + struct macho_section : public section_64 {}; + struct macho_routines_command : public routines_command_64 {}; +#else + #define RELOC_SIZE 2 + #define LC_SEGMENT_COMMAND LC_SEGMENT + #define LC_ROUTINES_COMMAND LC_ROUTINES + struct macho_segment_command : public segment_command {}; + struct macho_section : public section {}; + struct macho_routines_command : public routines_command {}; +#endif + + +// create image for main executable +ImageLoaderMachOCompressed* ImageLoaderMachOCompressed::instantiateMainExecutable(const macho_header* mh, uintptr_t slide, const char* path, + unsigned int segCount, unsigned int libCount, const LinkContext& context) +{ + ImageLoaderMachOCompressed* image = ImageLoaderMachOCompressed::instantiateStart(mh, path, segCount, libCount); + + // set slide for PIE programs + image->setSlide(slide); + + // for PIE record end of program, to know where to start loading dylibs + if ( slide != 0 ) + fgNextPIEDylibAddress = (uintptr_t)image->getEnd(); + + image->disableCoverageCheck(); + image->instantiateFinish(context); + image->setMapped(context); + + if ( context.verboseMapping ) { + dyld::log("dyld: Main executable mapped %s\n", path); + for(unsigned int i=0, e=image->segmentCount(); i < e; ++i) { + const char* name = image->segName(i); + if ( (strcmp(name, "__PAGEZERO") == 0) || (strcmp(name, "__UNIXSTACK") == 0) ) + dyld::log("%18s at 0x%08lX->0x%08lX\n", name, image->segPreferredLoadAddress(i), image->segPreferredLoadAddress(i)+image->segSize(i)); + else + dyld::log("%18s at 0x%08lX->0x%08lX\n", name, image->segActualLoadAddress(i), image->segActualEndAddress(i)); + } + } + + return image; +} + +// create image by mapping in a mach-o file +ImageLoaderMachOCompressed* ImageLoaderMachOCompressed::instantiateFromFile(const char* path, int fd, const uint8_t* fileData, size_t lenFileData, + uint64_t offsetInFat, uint64_t lenInFat, const struct stat& info, + unsigned int segCount, unsigned int libCount, + const struct linkedit_data_command* codeSigCmd, + const struct encryption_info_command* encryptCmd, + const LinkContext& context) +{ + ImageLoaderMachOCompressed* image = ImageLoaderMachOCompressed::instantiateStart((macho_header*)fileData, path, segCount, libCount); + + try { + // record info about file + image->setFileInfo(info.st_dev, info.st_ino, info.st_mtime); + + // if this image is code signed, let kernel validate signature before mapping any pages from image + image->loadCodeSignature(codeSigCmd, fd, offsetInFat, context); + + // Validate that first data we read with pread actually matches with code signature + image->validateFirstPages(codeSigCmd, fd, fileData, lenFileData, offsetInFat, context); + + // mmap segments + image->mapSegments(fd, offsetInFat, lenInFat, info.st_size, context); + + // if framework is FairPlay encrypted, register with kernel + image->registerEncryption(encryptCmd, context); + + // probe to see if code signed correctly + image->crashIfInvalidCodeSignature(); + + // finish construction + image->instantiateFinish(context); + + // if path happens to be same as in LC_DYLIB_ID load command use that, otherwise malloc a copy of the path + const char* installName = image->getInstallPath(); + if ( (installName != NULL) && (strcmp(installName, path) == 0) && (path[0] == '/') ) + image->setPathUnowned(installName); +#if __MAC_OS_X_VERSION_MIN_REQUIRED + // app crashes when libSystem cannot be found + else if ( (installName != NULL) && (strcmp(path, "/usr/lib/libgcc_s.1.dylib") == 0) && (strcmp(installName, "/usr/lib/libSystem.B.dylib") == 0) ) + image->setPathUnowned("/usr/lib/libSystem.B.dylib"); +#endif + else if ( (path[0] != '/') || (strstr(path, "../") != NULL) ) { + // rdar://problem/10733082 Fix up @rpath based paths during introspection + // rdar://problem/5135363 turn relative paths into absolute paths so gdb, Symbolication can later find them + char realPath[MAXPATHLEN]; + if ( fcntl(fd, F_GETPATH, realPath) == 0 ) + image->setPaths(path, realPath); + else + image->setPath(path); + } + else + image->setPath(path); + + // make sure path is stable before recording in dyld_all_image_infos + image->setMapped(context); + + // pre-fetch content of __DATA and __LINKEDIT segment for faster launches + // don't do this on prebound images or if prefetching is disabled + if ( !context.preFetchDisabled && !image->isPrebindable()) { + image->preFetchDATA(fd, offsetInFat, context); + image->markSequentialLINKEDIT(context); + } + } + catch (...) { + // ImageLoader::setMapped() can throw an exception to block loading of image + // Leaked fSegmentsArray and image segments during failed dlopen_preflight + delete image; + throw; + } + + return image; +} + +// create image by using cached mach-o file +ImageLoaderMachOCompressed* ImageLoaderMachOCompressed::instantiateFromCache(const macho_header* mh, const char* path, long slide, + const struct stat& info, unsigned int segCount, + unsigned int libCount, const LinkContext& context) +{ + ImageLoaderMachOCompressed* image = ImageLoaderMachOCompressed::instantiateStart(mh, path, segCount, libCount); + try { + // record info about file + image->setFileInfo(info.st_dev, info.st_ino, info.st_mtime); + + // remember this is from shared cache and cannot be unloaded + image->fInSharedCache = true; + image->setNeverUnload(); + image->setSlide(slide); + image->disableCoverageCheck(); + + // segments already mapped in cache + if ( context.verboseMapping ) { + dyld::log("dyld: Using shared cached for %s\n", path); + for(unsigned int i=0; i < image->fSegmentsCount; ++i) { + dyld::log("%18s at 0x%08lX->0x%08lX\n", image->segName(i), image->segActualLoadAddress(i), image->segActualEndAddress(i)); + } + } + + image->instantiateFinish(context); + image->setMapped(context); + } + catch (...) { + // ImageLoader::setMapped() can throw an exception to block loading of image + // Leaked fSegmentsArray and image segments during failed dlopen_preflight + delete image; + throw; + } + + return image; +} + +// create image by copying an in-memory mach-o file +ImageLoaderMachOCompressed* ImageLoaderMachOCompressed::instantiateFromMemory(const char* moduleName, const macho_header* mh, uint64_t len, + unsigned int segCount, unsigned int libCount, const LinkContext& context) +{ + ImageLoaderMachOCompressed* image = ImageLoaderMachOCompressed::instantiateStart(mh, moduleName, segCount, libCount); + try { + // map segments + if ( mh->filetype == MH_EXECUTE ) + throw "can't load another MH_EXECUTE"; + + // vmcopy segments + image->mapSegments((const void*)mh, len, context); + + // for compatibility, never unload dylibs loaded from memory + image->setNeverUnload(); + + image->disableCoverageCheck(); + + // bundle loads need path copied + if ( moduleName != NULL ) + image->setPath(moduleName); + + image->instantiateFinish(context); + image->setMapped(context); + } + catch (...) { + // ImageLoader::setMapped() can throw an exception to block loading of image + // Leaked fSegmentsArray and image segments during failed dlopen_preflight + delete image; + throw; + } + + return image; +} + + +ImageLoaderMachOCompressed::ImageLoaderMachOCompressed(const macho_header* mh, const char* path, unsigned int segCount, + uint32_t segOffsets[], unsigned int libCount) + : ImageLoaderMachO(mh, path, segCount, segOffsets, libCount), fDyldInfo(NULL) +{ +} + +ImageLoaderMachOCompressed::~ImageLoaderMachOCompressed() +{ + // don't do clean up in ~ImageLoaderMachO() because virtual call to segmentCommandOffsets() won't work + destroy(); +} + + + +// construct ImageLoaderMachOCompressed using "placement new" with SegmentMachO objects array at end +ImageLoaderMachOCompressed* ImageLoaderMachOCompressed::instantiateStart(const macho_header* mh, const char* path, + unsigned int segCount, unsigned int libCount) +{ + size_t size = sizeof(ImageLoaderMachOCompressed) + segCount * sizeof(uint32_t) + libCount * sizeof(ImageLoader*); + ImageLoaderMachOCompressed* allocatedSpace = static_cast(malloc(size)); + if ( allocatedSpace == NULL ) + throw "malloc failed"; + uint32_t* segOffsets = ((uint32_t*)(((uint8_t*)allocatedSpace) + sizeof(ImageLoaderMachOCompressed))); + bzero(&segOffsets[segCount], libCount*sizeof(void*)); // zero out lib array + return new (allocatedSpace) ImageLoaderMachOCompressed(mh, path, segCount, segOffsets, libCount); +} + + +// common code to finish initializing object +void ImageLoaderMachOCompressed::instantiateFinish(const LinkContext& context) +{ + // now that segments are mapped in, get real fMachOData, fLinkEditBase, and fSlide + this->parseLoadCmds(context); +} + +uint32_t* ImageLoaderMachOCompressed::segmentCommandOffsets() const +{ + return ((uint32_t*)(((uint8_t*)this) + sizeof(ImageLoaderMachOCompressed))); +} + + +ImageLoader* ImageLoaderMachOCompressed::libImage(unsigned int libIndex) const +{ + const uintptr_t* images = ((uintptr_t*)(((uint8_t*)this) + sizeof(ImageLoaderMachOCompressed) + fSegmentsCount*sizeof(uint32_t))); + // mask off low bits + return (ImageLoader*)(images[libIndex] & (-4)); +} + +bool ImageLoaderMachOCompressed::libReExported(unsigned int libIndex) const +{ + const uintptr_t* images = ((uintptr_t*)(((uint8_t*)this) + sizeof(ImageLoaderMachOCompressed) + fSegmentsCount*sizeof(uint32_t))); + // re-export flag is low bit + return ((images[libIndex] & 1) != 0); +} + +bool ImageLoaderMachOCompressed::libIsUpward(unsigned int libIndex) const +{ + const uintptr_t* images = ((uintptr_t*)(((uint8_t*)this) + sizeof(ImageLoaderMachOCompressed) + fSegmentsCount*sizeof(uint32_t))); + // re-export flag is second bit + return ((images[libIndex] & 2) != 0); +} + + +void ImageLoaderMachOCompressed::setLibImage(unsigned int libIndex, ImageLoader* image, bool reExported, bool upward) +{ + uintptr_t* images = ((uintptr_t*)(((uint8_t*)this) + sizeof(ImageLoaderMachOCompressed) + fSegmentsCount*sizeof(uint32_t))); + uintptr_t value = (uintptr_t)image; + if ( reExported ) + value |= 1; + if ( upward ) + value |= 2; + images[libIndex] = value; +} + + +void ImageLoaderMachOCompressed::markFreeLINKEDIT(const LinkContext& context) +{ + // mark that we are done with rebase and bind info + markLINKEDIT(context, MADV_FREE); +} + +void ImageLoaderMachOCompressed::markSequentialLINKEDIT(const LinkContext& context) +{ + // mark the rebase and bind info and using sequential access + markLINKEDIT(context, MADV_SEQUENTIAL); +} + +void ImageLoaderMachOCompressed::markLINKEDIT(const LinkContext& context, int advise) +{ + // if not loaded at preferred address, mark rebase info + uintptr_t start = 0; + if ( (fSlide != 0) && (fDyldInfo->rebase_size != 0) ) + start = (uintptr_t)fLinkEditBase + fDyldInfo->rebase_off; + else if ( fDyldInfo->bind_off != 0 ) + start = (uintptr_t)fLinkEditBase + fDyldInfo->bind_off; + else + return; // no binding info to prefetch + + // end is at end of bind info + uintptr_t end = 0; + if ( fDyldInfo->bind_off != 0 ) + end = (uintptr_t)fLinkEditBase + fDyldInfo->bind_off + fDyldInfo->bind_size; + else if ( fDyldInfo->rebase_off != 0 ) + end = (uintptr_t)fLinkEditBase + fDyldInfo->rebase_off + fDyldInfo->rebase_size; + else + return; + + + // round to whole pages + start = dyld_page_trunc(start); + end = dyld_page_round(end); + + // do nothing if only one page of rebase/bind info + if ( (end-start) <= dyld_page_size ) + return; + + // tell kernel about our access to these pages + madvise((void*)start, end-start, advise); + if ( context.verboseMapping ) { + const char* adstr = "sequential"; + if ( advise == MADV_FREE ) + adstr = "free"; + dyld::log("%18s %s 0x%0lX -> 0x%0lX for %s\n", "__LINKEDIT", adstr, start, end-1, this->getPath()); + } +} + + + +void ImageLoaderMachOCompressed::rebaseAt(const LinkContext& context, uintptr_t addr, uintptr_t slide, uint8_t type) +{ + if ( context.verboseRebase ) { + dyld::log("dyld: rebase: %s:*0x%08lX += 0x%08lX\n", this->getShortName(), (uintptr_t)addr, slide); + } + //dyld::log("0x%08lX type=%d\n", addr, type); + uintptr_t* locationToFix = (uintptr_t*)addr; + switch (type) { + case REBASE_TYPE_POINTER: + *locationToFix += slide; + break; + case REBASE_TYPE_TEXT_ABSOLUTE32: + *locationToFix += slide; + break; + default: + dyld::throwf("bad rebase type %d", type); + } +} + +void ImageLoaderMachOCompressed::throwBadRebaseAddress(uintptr_t address, uintptr_t segmentEndAddress, int segmentIndex, + const uint8_t* startOpcodes, const uint8_t* endOpcodes, const uint8_t* pos) +{ + dyld::throwf("malformed rebase opcodes (%ld/%ld): address 0x%08lX is outside of segment %s (0x%08lX -> 0x%08lX)", + (intptr_t)(pos-startOpcodes), (intptr_t)(endOpcodes-startOpcodes), address, segName(segmentIndex), + segActualLoadAddress(segmentIndex), segmentEndAddress); +} + +void ImageLoaderMachOCompressed::rebase(const LinkContext& context, uintptr_t slide) +{ + CRSetCrashLogMessage2(this->getPath()); + const uint8_t* const start = fLinkEditBase + fDyldInfo->rebase_off; + const uint8_t* const end = &start[fDyldInfo->rebase_size]; + const uint8_t* p = start; + + try { + uint8_t type = 0; + int segmentIndex = 0; + uintptr_t address = segActualLoadAddress(0); + uintptr_t segmentStartAddress = segActualLoadAddress(0); + uintptr_t segmentEndAddress = segActualEndAddress(0); + uintptr_t count; + uintptr_t skip; + bool done = false; + while ( !done && (p < end) ) { + uint8_t immediate = *p & REBASE_IMMEDIATE_MASK; + uint8_t opcode = *p & REBASE_OPCODE_MASK; + ++p; + switch (opcode) { + case REBASE_OPCODE_DONE: + done = true; + break; + case REBASE_OPCODE_SET_TYPE_IMM: + type = immediate; + break; + case REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: + segmentIndex = immediate; + if ( segmentIndex >= fSegmentsCount ) + dyld::throwf("REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has segment %d which is too large (0..%d)", + segmentIndex, fSegmentsCount-1); + #if TEXT_RELOC_SUPPORT + if ( !segWriteable(segmentIndex) && !segHasRebaseFixUps(segmentIndex) && !segHasBindFixUps(segmentIndex) ) + #else + if ( !segWriteable(segmentIndex) ) + #endif + dyld::throwf("REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has segment %d which is not a writable segment (%s)", + segmentIndex, segName(segmentIndex)); + segmentStartAddress = segActualLoadAddress(segmentIndex); + segmentEndAddress = segActualEndAddress(segmentIndex); + address = segmentStartAddress + read_uleb128(p, end); + break; + case REBASE_OPCODE_ADD_ADDR_ULEB: + address += read_uleb128(p, end); + break; + case REBASE_OPCODE_ADD_ADDR_IMM_SCALED: + address += immediate*sizeof(uintptr_t); + break; + case REBASE_OPCODE_DO_REBASE_IMM_TIMES: + for (int i=0; i < immediate; ++i) { + if ( (address < segmentStartAddress) || (address >= segmentEndAddress) ) + throwBadRebaseAddress(address, segmentEndAddress, segmentIndex, start, end, p); + rebaseAt(context, address, slide, type); + address += sizeof(uintptr_t); + } + fgTotalRebaseFixups += immediate; + break; + case REBASE_OPCODE_DO_REBASE_ULEB_TIMES: + count = read_uleb128(p, end); + for (uint32_t i=0; i < count; ++i) { + if ( (address < segmentStartAddress) || (address >= segmentEndAddress) ) + throwBadRebaseAddress(address, segmentEndAddress, segmentIndex, start, end, p); + rebaseAt(context, address, slide, type); + address += sizeof(uintptr_t); + } + fgTotalRebaseFixups += count; + break; + case REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB: + if ( (address < segmentStartAddress) || (address >= segmentEndAddress) ) + throwBadRebaseAddress(address, segmentEndAddress, segmentIndex, start, end, p); + rebaseAt(context, address, slide, type); + address += read_uleb128(p, end) + sizeof(uintptr_t); + ++fgTotalRebaseFixups; + break; + case REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB: + count = read_uleb128(p, end); + skip = read_uleb128(p, end); + for (uint32_t i=0; i < count; ++i) { + if ( (address < segmentStartAddress) || (address >= segmentEndAddress) ) + throwBadRebaseAddress(address, segmentEndAddress, segmentIndex, start, end, p); + rebaseAt(context, address, slide, type); + address += skip + sizeof(uintptr_t); + } + fgTotalRebaseFixups += count; + break; + default: + dyld::throwf("bad rebase opcode %d", *p); + } + } + } + catch (const char* msg) { + const char* newMsg = dyld::mkstringf("%s in %s", msg, this->getPath()); + free((void*)msg); + throw newMsg; + } + CRSetCrashLogMessage2(NULL); +} + +const ImageLoader::Symbol* ImageLoaderMachOCompressed::findShallowExportedSymbol(const char* symbol, const ImageLoader** foundIn) const +{ + //dyld::log("Compressed::findExportedSymbol(%s) in %s\n", symbol, this->getShortName()); + if ( fDyldInfo->export_size == 0 ) + return NULL; +#if LOG_BINDINGS + dyld::logBindings("%s: %s\n", this->getShortName(), symbol); +#endif + ++ImageLoaderMachO::fgSymbolTrieSearchs; + const uint8_t* start = &fLinkEditBase[fDyldInfo->export_off]; + const uint8_t* end = &start[fDyldInfo->export_size]; + const uint8_t* foundNodeStart = this->trieWalk(start, end, symbol); + if ( foundNodeStart != NULL ) { + const uint8_t* p = foundNodeStart; + const uintptr_t flags = read_uleb128(p, end); + // found match, return pointer to terminal part of node + if ( flags & EXPORT_SYMBOL_FLAGS_REEXPORT ) { + // re-export from another dylib, lookup there + const uintptr_t ordinal = read_uleb128(p, end); + const char* importedName = (char*)p; + if ( importedName[0] == '\0' ) + importedName = symbol; + if ( (ordinal > 0) && (ordinal <= libraryCount()) ) { + const ImageLoader* reexportedFrom = libImage((unsigned int)ordinal-1); + //dyld::log("Compressed::findExportedSymbol(), %s -> %s/%s\n", symbol, reexportedFrom->getShortName(), importedName); + const char* reExportLibPath = libPath((unsigned int)ordinal-1); + return reexportedFrom->findExportedSymbol(importedName, true, reExportLibPath, foundIn); + } + else { + //dyld::throwf("bad mach-o binary, library ordinal (%u) invalid (max %u) for re-exported symbol %s in %s", + // ordinal, libraryCount(), symbol, this->getPath()); + } + } + else { + //dyld::log("findExportedSymbol(%s) in %s found match, returning %p\n", symbol, this->getShortName(), p); + if ( foundIn != NULL ) + *foundIn = (ImageLoader*)this; + // return pointer to terminal part of node + return (Symbol*)foundNodeStart; + } + } + return NULL; +} + + +bool ImageLoaderMachOCompressed::containsSymbol(const void* addr) const +{ + const uint8_t* start = &fLinkEditBase[fDyldInfo->export_off]; + const uint8_t* end = &start[fDyldInfo->export_size]; + return ( (start <= addr) && (addr < end) ); +} + + +uintptr_t ImageLoaderMachOCompressed::exportedSymbolAddress(const LinkContext& context, const Symbol* symbol, const ImageLoader* requestor, bool runResolver) const +{ + const uint8_t* exportNode = (uint8_t*)symbol; + const uint8_t* exportTrieStart = fLinkEditBase + fDyldInfo->export_off; + const uint8_t* exportTrieEnd = exportTrieStart + fDyldInfo->export_size; + if ( (exportNode < exportTrieStart) || (exportNode > exportTrieEnd) ) + throw "symbol is not in trie"; + //dyld::log("exportedSymbolAddress(): node=%p, nodeOffset=0x%04X in %s\n", symbol, (int)((uint8_t*)symbol - exportTrieStart), this->getShortName()); + uintptr_t flags = read_uleb128(exportNode, exportTrieEnd); + switch ( flags & EXPORT_SYMBOL_FLAGS_KIND_MASK ) { + case EXPORT_SYMBOL_FLAGS_KIND_REGULAR: + if ( runResolver && (flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER) ) { + // this node has a stub and resolver, run the resolver to get target address + uintptr_t stub = read_uleb128(exportNode, exportTrieEnd) + (uintptr_t)fMachOData; // skip over stub + // interposing dylibs have the stub address as their replacee + uintptr_t interposedStub = interposedAddress(context, stub, requestor); + if ( interposedStub != stub ) + return interposedStub; + // stub was not interposed, so run resolver + typedef uintptr_t (*ResolverProc)(void); + ResolverProc resolver = (ResolverProc)(read_uleb128(exportNode, exportTrieEnd) + (uintptr_t)fMachOData); + uintptr_t result = (*resolver)(); + if ( context.verboseBind ) + dyld::log("dyld: resolver at %p returned 0x%08lX\n", resolver, result); + return result; + } + return read_uleb128(exportNode, exportTrieEnd) + (uintptr_t)fMachOData; + case EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL: + if ( flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER ) + dyld::throwf("unsupported exported symbol kind. flags=%lu at node=%p", flags, symbol); + return read_uleb128(exportNode, exportTrieEnd) + (uintptr_t)fMachOData; + case EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE: + if ( flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER ) + dyld::throwf("unsupported exported symbol kind. flags=%lu at node=%p", flags, symbol); + return read_uleb128(exportNode, exportTrieEnd); + default: + dyld::throwf("unsupported exported symbol kind. flags=%lu at node=%p", flags, symbol); + } +} + +bool ImageLoaderMachOCompressed::exportedSymbolIsWeakDefintion(const Symbol* symbol) const +{ + const uint8_t* exportNode = (uint8_t*)symbol; + const uint8_t* exportTrieStart = fLinkEditBase + fDyldInfo->export_off; + const uint8_t* exportTrieEnd = exportTrieStart + fDyldInfo->export_size; + if ( (exportNode < exportTrieStart) || (exportNode > exportTrieEnd) ) + throw "symbol is not in trie"; + uintptr_t flags = read_uleb128(exportNode, exportTrieEnd); + return ( flags & EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION ); +} + + +const char* ImageLoaderMachOCompressed::exportedSymbolName(const Symbol* symbol) const +{ + throw "NSNameOfSymbol() not supported with compressed LINKEDIT"; +} + +unsigned int ImageLoaderMachOCompressed::exportedSymbolCount() const +{ + throw "NSSymbolDefinitionCountInObjectFileImage() not supported with compressed LINKEDIT"; +} + +const ImageLoader::Symbol* ImageLoaderMachOCompressed::exportedSymbolIndexed(unsigned int index) const +{ + throw "NSSymbolDefinitionNameInObjectFileImage() not supported with compressed LINKEDIT"; +} + +unsigned int ImageLoaderMachOCompressed::importedSymbolCount() const +{ + throw "NSSymbolReferenceCountInObjectFileImage() not supported with compressed LINKEDIT"; +} + +const ImageLoader::Symbol* ImageLoaderMachOCompressed::importedSymbolIndexed(unsigned int index) const +{ + throw "NSSymbolReferenceCountInObjectFileImage() not supported with compressed LINKEDIT"; +} + +const char* ImageLoaderMachOCompressed::importedSymbolName(const Symbol* symbol) const +{ + throw "NSSymbolReferenceNameInObjectFileImage() not supported with compressed LINKEDIT"; +} + + + +uintptr_t ImageLoaderMachOCompressed::resolveFlat(const LinkContext& context, const char* symbolName, bool weak_import, + bool runResolver, const ImageLoader** foundIn) +{ + const Symbol* sym; + if ( context.flatExportFinder(symbolName, &sym, foundIn) ) { + if ( *foundIn != this ) + context.addDynamicReference(this, const_cast(*foundIn)); + return (*foundIn)->getExportedSymbolAddress(sym, context, this, runResolver); + } + // if a bundle is loaded privately the above will not find its exports + if ( this->isBundle() && this->hasHiddenExports() ) { + // look in self for needed symbol + sym = this->findShallowExportedSymbol(symbolName, foundIn); + if ( sym != NULL ) + return (*foundIn)->getExportedSymbolAddress(sym, context, this, runResolver); + } + if ( weak_import ) { + // definition can't be found anywhere, ok because it is weak, just return 0 + return 0; + } + throwSymbolNotFound(context, symbolName, this->getPath(), "", "flat namespace"); +} + + +uintptr_t ImageLoaderMachOCompressed::resolveTwolevel(const LinkContext& context, const char* symbolName, const ImageLoader* definedInImage, + const ImageLoader* requestorImage, unsigned requestorOrdinalOfDef, bool weak_import, bool runResolver, + const ImageLoader** foundIn) +{ + // two level lookup + uintptr_t address; + if ( definedInImage->findExportedSymbolAddress(context, symbolName, requestorImage, requestorOrdinalOfDef, runResolver, foundIn, &address) ) + return address; + + if ( weak_import ) { + // definition can't be found anywhere, ok because it is weak, just return 0 + return 0; + } + + // nowhere to be found, check if maybe this image is too new for this OS + char versMismatch[256]; + versMismatch[0] = '\0'; + uint32_t imageMinOS = this->minOSVersion(); + // dyld is always built for the current OS, so we can get the current OS version + // from the load command in dyld itself. + extern const mach_header __dso_handle; + uint32_t dyldMinOS = ImageLoaderMachO::minOSVersion(&__dso_handle); + if ( imageMinOS > dyldMinOS ) { +#if __MAC_OS_X_VERSION_MIN_REQUIRED + const char* msg = dyld::mkstringf(" (which was built for Mac OS X %d.%d)", imageMinOS >> 16, (imageMinOS >> 8) & 0xFF); +#else + const char* msg = dyld::mkstringf(" (which was built for iOS %d.%d)", imageMinOS >> 16, (imageMinOS >> 8) & 0xFF); +#endif + strcpy(versMismatch, msg); + ::free((void*)msg); + } + throwSymbolNotFound(context, symbolName, this->getPath(), versMismatch, definedInImage->getPath()); +} + + +uintptr_t ImageLoaderMachOCompressed::resolve(const LinkContext& context, const char* symbolName, + uint8_t symboFlags, long libraryOrdinal, const ImageLoader** targetImage, + LastLookup* last, bool runResolver) +{ + *targetImage = NULL; + + // only clients that benefit from caching last lookup pass in a LastLookup struct + if ( last != NULL ) { + if ( (last->ordinal == libraryOrdinal) + && (last->flags == symboFlags) + && (last->name == symbolName) ) { + *targetImage = last->foundIn; + return last->result; + } + } + + bool weak_import = (symboFlags & BIND_SYMBOL_FLAGS_WEAK_IMPORT); + uintptr_t symbolAddress; + if ( context.bindFlat || (libraryOrdinal == BIND_SPECIAL_DYLIB_FLAT_LOOKUP) ) { + symbolAddress = this->resolveFlat(context, symbolName, weak_import, runResolver, targetImage); + } + else { + if ( libraryOrdinal == BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE ) { + *targetImage = context.mainExecutable; + } + else if ( libraryOrdinal == BIND_SPECIAL_DYLIB_SELF ) { + *targetImage = this; + } + else if ( libraryOrdinal <= 0 ) { + dyld::throwf("bad mach-o binary, unknown special library ordinal (%ld) too big for symbol %s in %s", + libraryOrdinal, symbolName, this->getPath()); + } + else if ( (unsigned)libraryOrdinal <= libraryCount() ) { + *targetImage = libImage((unsigned int)libraryOrdinal-1); + } + else { + dyld::throwf("bad mach-o binary, library ordinal (%ld) too big (max %u) for symbol %s in %s", + libraryOrdinal, libraryCount(), symbolName, this->getPath()); + } + if ( *targetImage == NULL ) { + if ( weak_import ) { + // if target library not loaded and reference is weak or library is weak return 0 + symbolAddress = 0; + } + else { + dyld::throwf("can't resolve symbol %s in %s because dependent dylib #%ld could not be loaded", + symbolName, this->getPath(), libraryOrdinal); + } + } + else { + symbolAddress = resolveTwolevel(context, symbolName, *targetImage, this, (unsigned)libraryOrdinal, weak_import, runResolver, targetImage); + } + } + + // save off lookup results if client wants + if ( last != NULL ) { + last->ordinal = libraryOrdinal; + last->flags = symboFlags; + last->name = symbolName; + last->foundIn = *targetImage; + last->result = symbolAddress; + } + + return symbolAddress; +} + +uintptr_t ImageLoaderMachOCompressed::bindAt(const LinkContext& context, uintptr_t addr, uint8_t type, const char* symbolName, + uint8_t symbolFlags, intptr_t addend, long libraryOrdinal, const char* msg, + LastLookup* last, bool runResolver) +{ + const ImageLoader* targetImage; + uintptr_t symbolAddress; + + // resolve symbol + symbolAddress = this->resolve(context, symbolName, symbolFlags, libraryOrdinal, &targetImage, last, runResolver); + + // do actual update + return this->bindLocation(context, addr, symbolAddress, type, symbolName, addend, this->getPath(), targetImage ? targetImage->getPath() : NULL, msg); +} + + +void ImageLoaderMachOCompressed::throwBadBindingAddress(uintptr_t address, uintptr_t segmentEndAddress, int segmentIndex, + const uint8_t* startOpcodes, const uint8_t* endOpcodes, const uint8_t* pos) +{ + dyld::throwf("malformed binding opcodes (%ld/%ld): address 0x%08lX is outside segment %s (0x%08lX -> 0x%08lX)", + (intptr_t)(pos-startOpcodes), (intptr_t)(endOpcodes-startOpcodes), address, segName(segmentIndex), + segActualLoadAddress(segmentIndex), segmentEndAddress); +} + + +void ImageLoaderMachOCompressed::doBind(const LinkContext& context, bool forceLazysBound) +{ + CRSetCrashLogMessage2(this->getPath()); + + // if prebound and loaded at prebound address, and all libraries are same as when this was prebound, then no need to bind + // note: flat-namespace binaries need to have imports rebound (even if correctly prebound) + if ( this->usablePrebinding(context) ) { + // don't need to bind + } + else { + uint64_t t0 = mach_absolute_time(); + + #if TEXT_RELOC_SUPPORT + // if there are __TEXT fixups, temporarily make __TEXT writable + if ( fTextSegmentBinds ) + this->makeTextSegmentWritable(context, true); + #endif + + // run through all binding opcodes + eachBind(context, &ImageLoaderMachOCompressed::bindAt); + + #if TEXT_RELOC_SUPPORT + // if there were __TEXT fixups, restore write protection + if ( fTextSegmentBinds ) + this->makeTextSegmentWritable(context, false); + #endif + + // if this image is in the shared cache, but depends on something no longer in the shared cache, + // there is no way to reset the lazy pointers, so force bind them now + if ( forceLazysBound || fInSharedCache ) + this->doBindJustLazies(context); + + // this image is in cache, but something below it is not. If + // this image has lazy pointer to a resolver function, then + // the stub may have been altered to point to a shared lazy pointer. + if ( fInSharedCache ) + this->updateOptimizedLazyPointers(context); + + // tell kernel we are done with chunks of LINKEDIT + if ( !context.preFetchDisabled ) + this->markFreeLINKEDIT(context); + + uint64_t t1 = mach_absolute_time(); + ImageLoader::fgTotalRebindCacheTime += (t1-t0); + } + + // set up dyld entry points in image + // do last so flat main executables will have __dyld or __program_vars set up + this->setupLazyPointerHandler(context); + CRSetCrashLogMessage2(NULL); +} + + +void ImageLoaderMachOCompressed::doBindJustLazies(const LinkContext& context) +{ + eachLazyBind(context, &ImageLoaderMachOCompressed::bindAt); +} + +void ImageLoaderMachOCompressed::eachBind(const LinkContext& context, bind_handler handler) +{ + try { + uint8_t type = 0; + int segmentIndex = -1; + uintptr_t address = segActualLoadAddress(0); + uintptr_t segmentStartAddress = segActualLoadAddress(0); + uintptr_t segmentEndAddress = segActualEndAddress(0); + const char* symbolName = NULL; + uint8_t symboFlags = 0; + bool libraryOrdinalSet = false; + long libraryOrdinal = 0; + intptr_t addend = 0; + uintptr_t count; + uintptr_t skip; + uintptr_t segOffset; + LastLookup last = { 0, 0, NULL, 0, NULL }; + const uint8_t* const start = fLinkEditBase + fDyldInfo->bind_off; + const uint8_t* const end = &start[fDyldInfo->bind_size]; + const uint8_t* p = start; + bool done = false; + while ( !done && (p < end) ) { + uint8_t immediate = *p & BIND_IMMEDIATE_MASK; + uint8_t opcode = *p & BIND_OPCODE_MASK; + ++p; + switch (opcode) { + case BIND_OPCODE_DONE: + done = true; + break; + case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM: + libraryOrdinal = immediate; + libraryOrdinalSet = true; + break; + case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB: + libraryOrdinal = read_uleb128(p, end); + libraryOrdinalSet = true; + break; + case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM: + // the special ordinals are negative numbers + if ( immediate == 0 ) + libraryOrdinal = 0; + else { + int8_t signExtended = BIND_OPCODE_MASK | immediate; + libraryOrdinal = signExtended; + } + libraryOrdinalSet = true; + break; + case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: + symbolName = (char*)p; + symboFlags = immediate; + while (*p != '\0') + ++p; + ++p; + break; + case BIND_OPCODE_SET_TYPE_IMM: + type = immediate; + break; + case BIND_OPCODE_SET_ADDEND_SLEB: + addend = read_sleb128(p, end); + break; + case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: + segmentIndex = immediate; + if ( (segmentIndex >= fSegmentsCount) || (segmentIndex < 0) ) + dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has segment %d which is out of range (0..%d)", + segmentIndex, fSegmentsCount-1); + #if TEXT_RELOC_SUPPORT + if ( !segWriteable(segmentIndex) && !segHasRebaseFixUps(segmentIndex) && !segHasBindFixUps(segmentIndex) ) + #else + if ( !segWriteable(segmentIndex) ) + #endif + dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has segment %d which is not writable", segmentIndex); + segOffset = read_uleb128(p, end); + if ( segOffset > segSize(segmentIndex) ) + dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has offset 0x%08lX beyond segment size (0x%08lX)", segOffset, segSize(segmentIndex)); + segmentStartAddress = segActualLoadAddress(segmentIndex); + address = segmentStartAddress + segOffset; + segmentEndAddress = segActualEndAddress(segmentIndex); + break; + case BIND_OPCODE_ADD_ADDR_ULEB: + address += read_uleb128(p, end); + break; + case BIND_OPCODE_DO_BIND: + if ( (address < segmentStartAddress) || (address >= segmentEndAddress) ) + throwBadBindingAddress(address, segmentEndAddress, segmentIndex, start, end, p); + if ( symbolName == NULL ) + dyld::throwf("BIND_OPCODE_DO_BIND missing preceding BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM"); + if ( segmentIndex == -1 ) + dyld::throwf("BIND_OPCODE_DO_BIND missing preceding BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB"); + if ( !libraryOrdinalSet ) + dyld::throwf("BIND_OPCODE_DO_BIND missing preceding BIND_OPCODE_SET_DYLIB_ORDINAL*"); + (this->*handler)(context, address, type, symbolName, symboFlags, addend, libraryOrdinal, "", &last, false); + address += sizeof(intptr_t); + break; + case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: + if ( (address < segmentStartAddress) || (address >= segmentEndAddress) ) + throwBadBindingAddress(address, segmentEndAddress, segmentIndex, start, end, p); + if ( symbolName == NULL ) + dyld::throwf("BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB missing preceding BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM"); + if ( segmentIndex == -1 ) + dyld::throwf("BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB missing preceding BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB"); + if ( !libraryOrdinalSet ) + dyld::throwf("BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB missing preceding BIND_OPCODE_SET_DYLIB_ORDINAL*"); + (this->*handler)(context, address, type, symbolName, symboFlags, addend, libraryOrdinal, "", &last, false); + address += read_uleb128(p, end) + sizeof(intptr_t); + break; + case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: + if ( (address < segmentStartAddress) || (address >= segmentEndAddress) ) + throwBadBindingAddress(address, segmentEndAddress, segmentIndex, start, end, p); + if ( symbolName == NULL ) + dyld::throwf("BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED missing preceding BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM"); + if ( segmentIndex == -1 ) + dyld::throwf("BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED missing preceding BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB"); + if ( !libraryOrdinalSet ) + dyld::throwf("BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED missing preceding BIND_OPCODE_SET_DYLIB_ORDINAL*"); + (this->*handler)(context, address, type, symbolName, symboFlags, addend, libraryOrdinal, "", &last, false); + address += immediate*sizeof(intptr_t) + sizeof(intptr_t); + break; + case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: + if ( symbolName == NULL ) + dyld::throwf("BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB missing preceding BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM"); + if ( segmentIndex == -1 ) + dyld::throwf("BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB missing preceding BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB"); + count = read_uleb128(p, end); + if ( !libraryOrdinalSet ) + dyld::throwf("BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB missing preceding BIND_OPCODE_SET_DYLIB_ORDINAL*"); + skip = read_uleb128(p, end); + for (uint32_t i=0; i < count; ++i) { + if ( (address < segmentStartAddress) || (address >= segmentEndAddress) ) + throwBadBindingAddress(address, segmentEndAddress, segmentIndex, start, end, p); + (this->*handler)(context, address, type, symbolName, symboFlags, addend, libraryOrdinal, "", &last, false); + address += skip + sizeof(intptr_t); + } + break; + default: + dyld::throwf("bad bind opcode %d in bind info", *p); + } + } + } + catch (const char* msg) { + const char* newMsg = dyld::mkstringf("%s in %s", msg, this->getPath()); + free((void*)msg); + throw newMsg; + } +} + +void ImageLoaderMachOCompressed::eachLazyBind(const LinkContext& context, bind_handler handler) +{ + try { + uint8_t type = BIND_TYPE_POINTER; + int segmentIndex = -1; + uintptr_t address = segActualLoadAddress(0); + uintptr_t segmentStartAddress = segActualLoadAddress(0); + uintptr_t segmentEndAddress = segActualEndAddress(0); + uintptr_t segOffset; + const char* symbolName = NULL; + uint8_t symboFlags = 0; + long libraryOrdinal = 0; + intptr_t addend = 0; + const uint8_t* const start = fLinkEditBase + fDyldInfo->lazy_bind_off; + const uint8_t* const end = &start[fDyldInfo->lazy_bind_size]; + const uint8_t* p = start; + bool done = false; + while ( !done && (p < end) ) { + uint8_t immediate = *p & BIND_IMMEDIATE_MASK; + uint8_t opcode = *p & BIND_OPCODE_MASK; + ++p; + switch (opcode) { + case BIND_OPCODE_DONE: + // there is BIND_OPCODE_DONE at end of each lazy bind, don't stop until end of whole sequence + break; + case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM: + libraryOrdinal = immediate; + break; + case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB: + libraryOrdinal = read_uleb128(p, end); + break; + case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM: + // the special ordinals are negative numbers + if ( immediate == 0 ) + libraryOrdinal = 0; + else { + int8_t signExtended = BIND_OPCODE_MASK | immediate; + libraryOrdinal = signExtended; + } + break; + case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: + symbolName = (char*)p; + symboFlags = immediate; + while (*p != '\0') + ++p; + ++p; + break; + case BIND_OPCODE_SET_TYPE_IMM: + type = immediate; + break; + case BIND_OPCODE_SET_ADDEND_SLEB: + addend = read_sleb128(p, end); + break; + case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: + segmentIndex = immediate; + if ( (segmentIndex >= fSegmentsCount) || (segmentIndex < 0) ) + dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has segment %d which is out of range (0..%d)", + segmentIndex, fSegmentsCount-1); + if ( !segWriteable(segmentIndex) ) + dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has segment %d which is not writable", segmentIndex); + segOffset = read_uleb128(p, end); + if ( segOffset > segSize(segmentIndex) ) + dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has offset 0x%08lX beyond segment size (0x%08lX)", segOffset, segSize(segmentIndex)); + segmentStartAddress = segActualLoadAddress(segmentIndex); + segmentEndAddress = segActualEndAddress(segmentIndex); + address = segmentStartAddress + segOffset; + break; + case BIND_OPCODE_ADD_ADDR_ULEB: + address += read_uleb128(p, end); + break; + case BIND_OPCODE_DO_BIND: + if ( segmentIndex == -1 ) + dyld::throwf("BIND_OPCODE_DO_BIND missing preceding BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB"); + if ( (address < segmentStartAddress) || (address >= segmentEndAddress) ) + throwBadBindingAddress(address, segmentEndAddress, segmentIndex, start, end, p); + if ( symbolName == NULL ) + dyld::throwf("BIND_OPCODE_DO_BIND missing preceding BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM"); + (this->*handler)(context, address, type, symbolName, symboFlags, addend, libraryOrdinal, "forced lazy ", NULL, false); + address += sizeof(intptr_t); + break; + case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: + case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: + case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: + default: + dyld::throwf("bad lazy bind opcode %d", *p); + } + } + } + + catch (const char* msg) { + const char* newMsg = dyld::mkstringf("%s in %s", msg, this->getPath()); + free((void*)msg); + throw newMsg; + } +} + +// A program built targeting 10.5 will have hybrid stubs. When used with weak symbols +// the classic lazy loader is used even when running on 10.6 +uintptr_t ImageLoaderMachOCompressed::doBindLazySymbol(uintptr_t* lazyPointer, const LinkContext& context) +{ + // only works with compressed LINKEDIT if classic symbol table is also present + const macho_nlist* symbolTable = NULL; + const char* symbolTableStrings = NULL; + const dysymtab_command* dynSymbolTable = NULL; + const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds; + const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; + const struct load_command* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd) { + case LC_SYMTAB: + { + const struct symtab_command* symtab = (struct symtab_command*)cmd; + symbolTableStrings = (const char*)&fLinkEditBase[symtab->stroff]; + symbolTable = (macho_nlist*)(&fLinkEditBase[symtab->symoff]); + } + break; + case LC_DYSYMTAB: + dynSymbolTable = (struct dysymtab_command*)cmd; + break; + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + // no symbol table => no lookup by address + if ( (symbolTable == NULL) || (dynSymbolTable == NULL) ) + dyld::throwf("classic lazy binding used with compressed LINKEDIT at %p in image %s", lazyPointer, this->getPath()); + + // scan for all lazy-pointer sections + const bool twoLevel = this->usesTwoLevelNameSpace(); + const uint32_t* const indirectTable = (uint32_t*)&fLinkEditBase[dynSymbolTable->indirectsymoff]; + cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd) { + case LC_SEGMENT_COMMAND: + { + const struct macho_segment_command* seg = (struct macho_segment_command*)cmd; + const struct macho_section* const sectionsStart = (struct macho_section*)((char*)seg + sizeof(struct macho_segment_command)); + const struct macho_section* const sectionsEnd = §ionsStart[seg->nsects]; + for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) { + const uint8_t type = sect->flags & SECTION_TYPE; + uint32_t symbolIndex = INDIRECT_SYMBOL_LOCAL; + if ( type == S_LAZY_SYMBOL_POINTERS ) { + const size_t pointerCount = sect->size / sizeof(uintptr_t); + uintptr_t* const symbolPointers = (uintptr_t*)(sect->addr + fSlide); + if ( (lazyPointer >= symbolPointers) && (lazyPointer < &symbolPointers[pointerCount]) ) { + const uint32_t indirectTableOffset = sect->reserved1; + const size_t lazyIndex = lazyPointer - symbolPointers; + symbolIndex = indirectTable[indirectTableOffset + lazyIndex]; + } + } + if ( (symbolIndex != INDIRECT_SYMBOL_ABS) && (symbolIndex != INDIRECT_SYMBOL_LOCAL) ) { + const macho_nlist* symbol = &symbolTable[symbolIndex]; + const char* symbolName = &symbolTableStrings[symbol->n_un.n_strx]; + int libraryOrdinal = GET_LIBRARY_ORDINAL(symbol->n_desc); + if ( !twoLevel || context.bindFlat ) + libraryOrdinal = BIND_SPECIAL_DYLIB_FLAT_LOOKUP; + uintptr_t ptrToBind = (uintptr_t)lazyPointer; + uintptr_t symbolAddr = bindAt(context, ptrToBind, BIND_TYPE_POINTER, symbolName, 0, 0, libraryOrdinal, "lazy ", NULL); + ++fgTotalLazyBindFixups; + return symbolAddr; + } + } + } + break; + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + dyld::throwf("lazy pointer not found at address %p in image %s", lazyPointer, this->getPath()); +} + + + +uintptr_t ImageLoaderMachOCompressed::doBindFastLazySymbol(uint32_t lazyBindingInfoOffset, const LinkContext& context, + void (*lock)(), void (*unlock)()) +{ + // race condition with flat-namespace lazy binding + if ( this->usesTwoLevelNameSpace() ) { + // two-level namespace lookup does not require lock because dependents can't be unloaded before this image + } + else { + // acquire dyld global lock + if ( lock != NULL ) + lock(); + } + + const uint8_t* const start = fLinkEditBase + fDyldInfo->lazy_bind_off; + const uint8_t* const end = &start[fDyldInfo->lazy_bind_size]; + uint8_t segIndex; + uintptr_t segOffset; + int libraryOrdinal; + const char* symbolName; + bool doneAfterBind; + uintptr_t result; + do { + if ( ! getLazyBindingInfo(lazyBindingInfoOffset, start, end, &segIndex, &segOffset, &libraryOrdinal, &symbolName, &doneAfterBind) ) + dyld::throwf("bad lazy bind info"); + + if ( segIndex >= fSegmentsCount ) + dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has segment %d which is too large (0..%d)", + segIndex, fSegmentsCount-1); + if ( segOffset > segSize(segIndex) ) + dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has offset 0x%08lX beyond segment size (0x%08lX)", segOffset, segSize(segIndex)); + uintptr_t address = segActualLoadAddress(segIndex) + segOffset; + result = this->bindAt(context, address, BIND_TYPE_POINTER, symbolName, 0, 0, libraryOrdinal, "lazy ", NULL, true); + // Some old apps had multiple lazy symbols bound at once + } while (!doneAfterBind && !context.strictMachORequired); + + if ( !this->usesTwoLevelNameSpace() ) { + // release dyld global lock + if ( unlock != NULL ) + unlock(); + } + return result; +} + +void ImageLoaderMachOCompressed::initializeCoalIterator(CoalIterator& it, unsigned int loadOrder, unsigned) +{ + it.image = this; + it.symbolName = " "; + it.loadOrder = loadOrder; + it.weakSymbol = false; + it.symbolMatches = false; + it.done = false; + it.curIndex = 0; + it.endIndex = this->fDyldInfo->weak_bind_size; + it.address = 0; + it.type = 0; + it.addend = 0; +} + + +bool ImageLoaderMachOCompressed::incrementCoalIterator(CoalIterator& it) +{ + if ( it.done ) + return false; + + if ( this->fDyldInfo->weak_bind_size == 0 ) { + /// hmmm, ld set MH_WEAK_DEFINES or MH_BINDS_TO_WEAK, but there is no weak binding info + it.done = true; + it.symbolName = "~~~"; + return true; + } + const uint8_t* start = fLinkEditBase + fDyldInfo->weak_bind_off; + const uint8_t* p = start + it.curIndex; + const uint8_t* end = fLinkEditBase + fDyldInfo->weak_bind_off + this->fDyldInfo->weak_bind_size; + uintptr_t count; + uintptr_t skip; + uintptr_t segOffset; + while ( p < end ) { + uint8_t immediate = *p & BIND_IMMEDIATE_MASK; + uint8_t opcode = *p & BIND_OPCODE_MASK; + ++p; + switch (opcode) { + case BIND_OPCODE_DONE: + it.done = true; + it.curIndex = p - start; + it.symbolName = "~~~"; // sorts to end + return true; + case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: + it.symbolName = (char*)p; + it.weakSymbol = ((immediate & BIND_SYMBOL_FLAGS_NON_WEAK_DEFINITION) == 0); + it.symbolMatches = false; + while (*p != '\0') + ++p; + ++p; + it.curIndex = p - start; + return false; + case BIND_OPCODE_SET_TYPE_IMM: + it.type = immediate; + break; + case BIND_OPCODE_SET_ADDEND_SLEB: + it.addend = read_sleb128(p, end); + break; + case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: + if ( immediate >= fSegmentsCount ) + dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has segment %d which is too large (0..%d)", + immediate, fSegmentsCount-1); + #if __arm__ + // iOS app compatibility + if ( !segWriteable(immediate) && it.image->isPositionIndependentExecutable() ) + #elif TEXT_RELOC_SUPPORT + // i386 OS X app compatibility + if ( !segWriteable(immediate) && !segHasRebaseFixUps(immediate) && !segHasBindFixUps(immediate) + && (!it.image->isExecutable() || it.image->isPositionIndependentExecutable()) ) + #else + if ( !segWriteable(immediate) ) + #endif + dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB targets segment %s which is not writable", segName(immediate)); + segOffset = read_uleb128(p, end); + if ( segOffset > segSize(immediate) ) + dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has offset 0x%08lX beyond segment size (0x%08lX)", segOffset, segSize(immediate)); + it.address = segActualLoadAddress(immediate) + segOffset; + break; + case BIND_OPCODE_ADD_ADDR_ULEB: + it.address += read_uleb128(p, end); + break; + case BIND_OPCODE_DO_BIND: + it.address += sizeof(intptr_t); + break; + case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: + it.address += read_uleb128(p, end) + sizeof(intptr_t); + break; + case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: + it.address += immediate*sizeof(intptr_t) + sizeof(intptr_t); + break; + case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: + count = read_uleb128(p, end); + skip = read_uleb128(p, end); + for (uint32_t i=0; i < count; ++i) { + it.address += skip + sizeof(intptr_t); + } + break; + default: + dyld::throwf("bad weak bind opcode '%d' found after processing %d bytes in '%s'", *p, (int)(p-start), this->getPath()); + } + } + /// hmmm, BIND_OPCODE_DONE is missing... + it.done = true; + it.symbolName = "~~~"; + //dyld::log("missing BIND_OPCODE_DONE for image %s\n", this->getPath()); + return true; +} + +uintptr_t ImageLoaderMachOCompressed::getAddressCoalIterator(CoalIterator& it, const LinkContext& context) +{ + //dyld::log("looking for %s in %s\n", it.symbolName, this->getPath()); + const ImageLoader* foundIn = NULL; + const ImageLoader::Symbol* sym = this->findShallowExportedSymbol(it.symbolName, &foundIn); + if ( sym != NULL ) { + //dyld::log("sym=%p, foundIn=%p\n", sym, foundIn); + return foundIn->getExportedSymbolAddress(sym, context, this); + } + return 0; +} + + +void ImageLoaderMachOCompressed::updateUsesCoalIterator(CoalIterator& it, uintptr_t value, ImageLoader* targetImage, unsigned targetIndex, const LinkContext& context) +{ + // weak binding done too early with inserted libraries + if ( this->getState() < dyld_image_state_bound ) + return; + + const uint8_t* start = fLinkEditBase + fDyldInfo->weak_bind_off; + const uint8_t* p = start + it.curIndex; + const uint8_t* end = fLinkEditBase + fDyldInfo->weak_bind_off + this->fDyldInfo->weak_bind_size; + + uint8_t type = it.type; + uintptr_t address = it.address; + const char* symbolName = it.symbolName; + intptr_t addend = it.addend; + uintptr_t count; + uintptr_t skip; + uintptr_t segOffset; + bool done = false; + bool boundSomething = false; + while ( !done && (p < end) ) { + uint8_t immediate = *p & BIND_IMMEDIATE_MASK; + uint8_t opcode = *p & BIND_OPCODE_MASK; + ++p; + switch (opcode) { + case BIND_OPCODE_DONE: + done = true; + break; + case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: + done = true; + break; + case BIND_OPCODE_SET_TYPE_IMM: + type = immediate; + break; + case BIND_OPCODE_SET_ADDEND_SLEB: + addend = read_sleb128(p, end); + break; + case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: + if ( immediate >= fSegmentsCount ) + dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has segment %d which is too large (0..%d)", + immediate, fSegmentsCount-1); + #if __arm__ + // iOS app compatibility + if ( !segWriteable(immediate) && it.image->isPositionIndependentExecutable() ) + #elif TEXT_RELOC_SUPPORT + // i386 OS X app compatibility + if ( !segWriteable(immediate) && !segHasRebaseFixUps(immediate) && !segHasBindFixUps(immediate) + && (!it.image->isExecutable() || it.image->isPositionIndependentExecutable()) ) + #else + if ( !segWriteable(immediate) ) + #endif + dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB targets segment %s which is not writable", segName(immediate)); + segOffset = read_uleb128(p, end); + if ( segOffset > segSize(immediate) ) + dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has offset 0x%08lX beyond segment size (0x%08lX)", segOffset, segSize(immediate)); + address = segActualLoadAddress(immediate) + segOffset; + break; + case BIND_OPCODE_ADD_ADDR_ULEB: + address += read_uleb128(p, end); + break; + case BIND_OPCODE_DO_BIND: + bindLocation(context, address, value, type, symbolName, addend, this->getPath(), targetImage ? targetImage->getPath() : NULL, "weak "); + boundSomething = true; + address += sizeof(intptr_t); + break; + case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: + bindLocation(context, address, value, type, symbolName, addend, this->getPath(), targetImage ? targetImage->getPath() : NULL, "weak "); + boundSomething = true; + address += read_uleb128(p, end) + sizeof(intptr_t); + break; + case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: + bindLocation(context, address, value, type, symbolName, addend, this->getPath(), targetImage ? targetImage->getPath() : NULL, "weak "); + boundSomething = true; + address += immediate*sizeof(intptr_t) + sizeof(intptr_t); + break; + case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: + count = read_uleb128(p, end); + skip = read_uleb128(p, end); + for (uint32_t i=0; i < count; ++i) { + bindLocation(context, address, value, type, symbolName, addend, this->getPath(), targetImage ? targetImage->getPath() : NULL, "weak "); + boundSomething = true; + address += skip + sizeof(intptr_t); + } + break; + default: + dyld::throwf("bad bind opcode %d in weak binding info", *p); + } + } + // C++ weak coalescing cannot be tracked by reference counting. Error on side of never unloading. + if ( boundSomething && (targetImage != this) ) + context.addDynamicReference(this, targetImage); +} + +uintptr_t ImageLoaderMachOCompressed::interposeAt(const LinkContext& context, uintptr_t addr, uint8_t type, const char*, + uint8_t, intptr_t, long, const char*, LastLookup*, bool runResolver) +{ + if ( type == BIND_TYPE_POINTER ) { + uintptr_t* fixupLocation = (uintptr_t*)addr; + uintptr_t curValue = *fixupLocation; + uintptr_t newValue = interposedAddress(context, curValue, this); + if ( newValue != curValue) { + *fixupLocation = newValue; + } + } + return 0; +} + +void ImageLoaderMachOCompressed::doInterpose(const LinkContext& context) +{ + if ( context.verboseInterposing ) + dyld::log("dyld: interposing %lu tuples onto image: %s\n", fgInterposingTuples.size(), this->getPath()); + + // update prebound symbols + eachBind(context, &ImageLoaderMachOCompressed::interposeAt); + eachLazyBind(context, &ImageLoaderMachOCompressed::interposeAt); +} + + +uintptr_t ImageLoaderMachOCompressed::dynamicInterposeAt(const LinkContext& context, uintptr_t addr, uint8_t type, const char* symbolName, + uint8_t, intptr_t, long, const char*, LastLookup*, bool runResolver) +{ + if ( type == BIND_TYPE_POINTER ) { + uintptr_t* fixupLocation = (uintptr_t*)addr; + uintptr_t value = *fixupLocation; + // don't apply interposing to table entries. + if ( (context.dynamicInterposeArray <= (void*)addr) && ((void*)addr < &context.dynamicInterposeArray[context.dynamicInterposeCount]) ) + return 0; + for(size_t i=0; i < context.dynamicInterposeCount; ++i) { + if ( value == (uintptr_t)context.dynamicInterposeArray[i].replacee ) { + if ( context.verboseInterposing ) { + dyld::log("dyld: dynamic interposing: at %p replace %p with %p in %s\n", + fixupLocation, context.dynamicInterposeArray[i].replacee, context.dynamicInterposeArray[i].replacement, this->getPath()); + } + *fixupLocation = (uintptr_t)context.dynamicInterposeArray[i].replacement; + } + } + } + return 0; +} + +void ImageLoaderMachOCompressed::dynamicInterpose(const LinkContext& context) +{ + if ( context.verboseInterposing ) + dyld::log("dyld: dynamic interposing %lu tuples onto image: %s\n", context.dynamicInterposeCount, this->getPath()); + + // update already bound references to symbols + eachBind(context, &ImageLoaderMachOCompressed::dynamicInterposeAt); + eachLazyBind(context, &ImageLoaderMachOCompressed::dynamicInterposeAt); +} + +const char* ImageLoaderMachOCompressed::findClosestSymbol(const void* addr, const void** closestAddr) const +{ + return ImageLoaderMachO::findClosestSymbol((mach_header*)fMachOData, addr, closestAddr); +} + + +#if PREBOUND_IMAGE_SUPPORT +void ImageLoaderMachOCompressed::resetPreboundLazyPointers(const LinkContext& context) +{ + // no way to back off a prebound compress image +} +#endif + + +#if __arm__ || __x86_64__ +void ImageLoaderMachOCompressed::updateAlternateLazyPointer(uint8_t* stub, void** originalLazyPointerAddr, const LinkContext& context) +{ +#if __arm__ + uint32_t* instructions = (uint32_t*)stub; + // sanity check this is a stub we understand + if ( (instructions[0] != 0xe59fc004) || (instructions[1] != 0xe08fc00c) || (instructions[2] != 0xe59cf000) ) + return; + + void** lazyPointerAddr = (void**)(instructions[3] + (stub + 12)); +#endif +#if __x86_64__ + // sanity check this is a stub we understand + if ( (stub[0] != 0xFF) || (stub[1] != 0x25) ) + return; + int32_t ripOffset = *((int32_t*)(&stub[2])); + void** lazyPointerAddr = (void**)(ripOffset + stub + 6); +#endif + + // if stub does not use original lazy pointer (meaning it was optimized by update_dyld_shared_cache) + if ( lazyPointerAddr != originalLazyPointerAddr ) { + // only de-optimization lazy pointers if they are part of shared cache not loaded (because overridden) + const ImageLoader* lazyPointerImage = context.findImageContainingAddress(lazyPointerAddr); + if ( lazyPointerImage != NULL ) + return; + + // copy newly re-bound lazy pointer value to shared lazy pointer + *lazyPointerAddr = *originalLazyPointerAddr; + + if ( context.verboseBind ) + dyld::log("dyld: alter bind: %s: *0x%08lX = 0x%08lX \n", + this->getShortName(), (long)lazyPointerAddr, (long)*originalLazyPointerAddr); + } +} +#endif + + +// overriding shared cache dylibs with resolvers fails +void ImageLoaderMachOCompressed::updateOptimizedLazyPointers(const LinkContext& context) +{ +#if __arm__ || __x86_64__ + // find stubs and indirect symbol table + const struct macho_section* stubsSection = NULL; + const dysymtab_command* dynSymbolTable = NULL; + const macho_header* mh = (macho_header*)fMachOData; + const uint32_t cmd_count = mh->ncmds; + const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; + const struct load_command* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + if (cmd->cmd == LC_SEGMENT_COMMAND) { + const struct macho_segment_command* seg = (struct macho_segment_command*)cmd; + const struct macho_section* const sectionsStart = (struct macho_section*)((char*)seg + sizeof(struct macho_segment_command)); + const struct macho_section* const sectionsEnd = §ionsStart[seg->nsects]; + for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) { + const uint8_t type = sect->flags & SECTION_TYPE; + if ( type == S_SYMBOL_STUBS ) + stubsSection = sect; + } + } + else if ( cmd->cmd == LC_DYSYMTAB ) { + dynSymbolTable = (struct dysymtab_command*)cmd; + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + if ( dynSymbolTable == NULL ) + return; + const uint32_t* const indirectTable = (uint32_t*)&fLinkEditBase[dynSymbolTable->indirectsymoff]; + if ( stubsSection == NULL ) + return; + const uint32_t stubsSize = stubsSection->reserved2; + const uint32_t stubsCount = (uint32_t)(stubsSection->size / stubsSize); + const uint32_t stubsIndirectTableOffset = stubsSection->reserved1; + if ( (stubsIndirectTableOffset+stubsCount) > dynSymbolTable->nindirectsyms ) + return; + uint8_t* const stubsAddr = (uint8_t*)(stubsSection->addr + this->fSlide); + + // for each lazy pointer section + cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + if (cmd->cmd == LC_SEGMENT_COMMAND) { + const struct macho_segment_command* seg = (struct macho_segment_command*)cmd; + const struct macho_section* const sectionsStart = (struct macho_section*)((char*)seg + sizeof(struct macho_segment_command)); + const struct macho_section* const sectionsEnd = §ionsStart[seg->nsects]; + for (const struct macho_section* lazyPointerSection=sectionsStart; lazyPointerSection < sectionsEnd; ++lazyPointerSection) { + const uint8_t type = lazyPointerSection->flags & SECTION_TYPE; + if ( type != S_LAZY_SYMBOL_POINTERS ) + continue; + const uint32_t lazyPointersCount = (uint32_t)(lazyPointerSection->size / sizeof(void*)); + const uint32_t lazyPointersIndirectTableOffset = lazyPointerSection->reserved1; + if ( (lazyPointersIndirectTableOffset+lazyPointersCount) > dynSymbolTable->nindirectsyms ) + continue; + void** const lazyPointersAddr = (void**)(lazyPointerSection->addr + this->fSlide); + // for each lazy pointer + for(uint32_t lpIndex=0; lpIndex < lazyPointersCount; ++lpIndex) { + const uint32_t lpSymbolIndex = indirectTable[lazyPointersIndirectTableOffset+lpIndex]; + // find matching stub and validate it uses this lazy pointer + for(uint32_t stubIndex=0; stubIndex < stubsCount; ++stubIndex) { + if ( indirectTable[stubsIndirectTableOffset+stubIndex] == lpSymbolIndex ) { + this->updateAlternateLazyPointer(stubsAddr+stubIndex*stubsSize, &lazyPointersAddr[lpIndex], context); + break; + } + } + } + + } + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + +#endif +} + + +void ImageLoaderMachOCompressed::registerEncryption(const encryption_info_command* encryptCmd, const LinkContext& context) +{ +#if __arm__ || __arm64__ + if ( encryptCmd == NULL ) + return; + const mach_header* mh = NULL; + for(unsigned int i=0; i < fSegmentsCount; ++i) { + if ( (segFileOffset(i) == 0) && (segFileSize(i) != 0) ) { + mh = (mach_header*)segActualLoadAddress(i); + break; + } + } + void* start = ((uint8_t*)mh) + encryptCmd->cryptoff; + size_t len = encryptCmd->cryptsize; + uint32_t cputype = mh->cputype; + uint32_t cpusubtype = mh->cpusubtype; + uint32_t cryptid = encryptCmd->cryptid; + if (context.verboseMapping) { + dyld::log(" 0x%08lX->0x%08lX configured for FairPlay decryption\n", (long)start, (long)start+len); + } + int result = mremap_encrypted(start, len, cryptid, cputype, cpusubtype); + if ( result != 0 ) { + dyld::throwf("mremap_encrypted() => %d, errno=%d for %s\n", result, errno, this->getPath()); + } +#endif +} + + + diff --git a/src/dyld-apple/src/ImageLoaderMachOCompressed.h b/src/dyld-apple/src/ImageLoaderMachOCompressed.h new file mode 100644 index 000000000..013bb907b --- /dev/null +++ b/src/dyld-apple/src/ImageLoaderMachOCompressed.h @@ -0,0 +1,149 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2008 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@ + */ + + +#ifndef __IMAGELOADER_MACHO_COMPRESSED__ +#define __IMAGELOADER_MACHO_COMPRESSED__ + +#include + +#include "ImageLoaderMachO.h" + + +// +// ImageLoaderMachOCompressed is the concrete subclass of ImageLoader which loads mach-o files +// that use the compressed LINKEDIT format. +// +class ImageLoaderMachOCompressed : public ImageLoaderMachO { +public: + static ImageLoaderMachOCompressed* instantiateMainExecutable(const macho_header* mh, uintptr_t slide, const char* path, + unsigned int segCount, unsigned int libCount, const LinkContext& context); + static ImageLoaderMachOCompressed* instantiateFromFile(const char* path, int fd, const uint8_t *fileData, size_t lenFileData, + uint64_t offsetInFat, uint64_t lenInFat, const struct stat& info, + unsigned int segCount, unsigned int libCount, + const struct linkedit_data_command* codeSigCmd, + const struct encryption_info_command* encryptCmd, + const LinkContext& context); + static ImageLoaderMachOCompressed* instantiateFromCache(const macho_header* mh, const char* path, long slide, const struct stat& info, + unsigned int segCount, unsigned int libCount, const LinkContext& context); + static ImageLoaderMachOCompressed* instantiateFromMemory(const char* moduleName, const macho_header* mh, uint64_t len, + unsigned int segCount, unsigned int libCount, const LinkContext& context); + + + virtual ~ImageLoaderMachOCompressed(); + + virtual ImageLoader* libImage(unsigned int) const; + virtual bool libReExported(unsigned int) const; + virtual bool libIsUpward(unsigned int) const; + virtual void setLibImage(unsigned int, ImageLoader*, bool, bool); + virtual void doBind(const LinkContext& context, bool forceLazysBound); + virtual void doBindJustLazies(const LinkContext& context); + virtual uintptr_t doBindLazySymbol(uintptr_t* lazyPointer, const LinkContext& context); + virtual uintptr_t doBindFastLazySymbol(uint32_t lazyBindingInfoOffset, const LinkContext& context, void (*lock)(), void (*unlock)()); + virtual const char* findClosestSymbol(const void* addr, const void** closestAddr) const; + virtual void initializeCoalIterator(CoalIterator&, unsigned int loadOrder, unsigned); + virtual bool incrementCoalIterator(CoalIterator&); + virtual uintptr_t getAddressCoalIterator(CoalIterator&, const LinkContext& contex); + virtual void updateUsesCoalIterator(CoalIterator&, uintptr_t newAddr, ImageLoader* target, unsigned targetIndex, const LinkContext& context); + +protected: + virtual void doInterpose(const LinkContext& context); + virtual void dynamicInterpose(const LinkContext& context); + virtual void setDyldInfo(const dyld_info_command* dyldInfo) { fDyldInfo = dyldInfo; } + virtual void setSymbolTableInfo(const macho_nlist*, const char*, const dysymtab_command*) {} + virtual bool isSubframeworkOf(const LinkContext& context, const ImageLoader* image) const { return false; } + virtual bool hasSubLibrary(const LinkContext& context, const ImageLoader* child) const { return false; } + virtual uint32_t* segmentCommandOffsets() const; + virtual void rebase(const LinkContext& context, uintptr_t slide); + virtual const ImageLoader::Symbol* findShallowExportedSymbol(const char* name, const ImageLoader** foundIn) const; + virtual bool containsSymbol(const void* addr) const; + virtual uintptr_t exportedSymbolAddress(const LinkContext& context, const Symbol* symbol, const ImageLoader* requestor, bool runResolver) const; + virtual bool exportedSymbolIsWeakDefintion(const Symbol* symbol) const; + virtual const char* exportedSymbolName(const Symbol* symbol) const; + virtual unsigned int exportedSymbolCount() const; + virtual const ImageLoader::Symbol* exportedSymbolIndexed(unsigned int) const; + virtual unsigned int importedSymbolCount() const; + virtual const ImageLoader::Symbol* importedSymbolIndexed(unsigned int) const; + virtual const char* importedSymbolName(const Symbol* symbol) const; +#if PREBOUND_IMAGE_SUPPORT + virtual void resetPreboundLazyPointers(const LinkContext& context); +#endif + + +private: + struct LastLookup { long ordinal; uint8_t flags; const char* name; uintptr_t result; const ImageLoader* foundIn; }; + + + typedef uintptr_t (ImageLoaderMachOCompressed::*bind_handler)(const LinkContext& context, uintptr_t addr, uint8_t type, + const char* symbolName, uint8_t symboFlags, intptr_t addend, long libraryOrdinal, + const char* msg, LastLookup* last, bool runResolver); + + void eachLazyBind(const LinkContext& context, bind_handler); + void eachBind(const LinkContext& context, bind_handler); + + + ImageLoaderMachOCompressed(const macho_header* mh, const char* path, unsigned int segCount, + uint32_t segOffsets[], unsigned int libCount); + static ImageLoaderMachOCompressed* instantiateStart(const macho_header* mh, const char* path, unsigned int segCount, unsigned int libCount); + void instantiateFinish(const LinkContext& context); + void markSequentialLINKEDIT(const LinkContext& context); + void markFreeLINKEDIT(const LinkContext& context); + void markLINKEDIT(const LinkContext& context, int advise); + + void rebaseAt(const LinkContext& context, uintptr_t addr, uintptr_t slide, uint8_t type); + void throwBadRebaseAddress(uintptr_t address, uintptr_t segmentEndAddress, int segmentIndex, + const uint8_t* startOpcodes, const uint8_t* endOpcodes, const uint8_t* pos); + uintptr_t bindAt(const LinkContext& context, uintptr_t addr, uint8_t type, const char* symbolName, + uint8_t symboFlags, intptr_t addend, long libraryOrdinal, const char* msg, + LastLookup* last, bool runResolver=false); + void bindCompressed(const LinkContext& context); + void throwBadBindingAddress(uintptr_t address, uintptr_t segmentEndAddress, int segmentIndex, + const uint8_t* startOpcodes, const uint8_t* endOpcodes, const uint8_t* pos); + uintptr_t resolve(const LinkContext& context, const char* symbolName, + uint8_t symboFlags, long libraryOrdinal, const ImageLoader** targetImage, + LastLookup* last = NULL, bool runResolver=false); + uintptr_t resolveFlat(const LinkContext& context, const char* symbolName, bool weak_import, bool runResolver, + const ImageLoader** foundIn); + uintptr_t resolveCoalesced(const LinkContext& context, const char* symbolName, const ImageLoader** foundIn); + uintptr_t resolveTwolevel(const LinkContext& context, const char* symbolName, const ImageLoader* definedInImage, + const ImageLoader* requestorImage, unsigned requestorOrdinalOfDef, bool weak_import, bool runResolver, + const ImageLoader** foundInn); + uintptr_t interposeAt(const LinkContext& context, uintptr_t addr, uint8_t type, const char*, + uint8_t, intptr_t, long, const char*, LastLookup*, bool runResolver); + uintptr_t dynamicInterposeAt(const LinkContext& context, uintptr_t addr, uint8_t type, const char*, + uint8_t, intptr_t, long, const char*, LastLookup*, bool runResolver); + void updateOptimizedLazyPointers(const LinkContext& context); + void updateAlternateLazyPointer(uint8_t* stub, void** originalLazyPointerAddr, const LinkContext& context); + void registerEncryption(const struct encryption_info_command* encryptCmd, const LinkContext& context); + + const struct dyld_info_command* fDyldInfo; +}; + + + +#endif // __IMAGELOADER_MACHO_COMPRESSED__ + + + + diff --git a/src/dyld-apple/src/ImageLoaderMegaDylib.cpp b/src/dyld-apple/src/ImageLoaderMegaDylib.cpp new file mode 100644 index 000000000..f3612d64c --- /dev/null +++ b/src/dyld-apple/src/ImageLoaderMegaDylib.cpp @@ -0,0 +1,1144 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2015 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@ + */ + + +#if __arm__ || __arm64__ + #include +#else + #include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ImageLoaderMegaDylib.h" +#include "ImageLoaderMachO.h" +#include "mach-o/dyld_images.h" +#include "dyldLibSystemInterface.h" +#include "dyld.h" + +// from dyld_gdb.cpp +extern void addImagesToAllImages(uint32_t infoCount, const dyld_image_info info[]); + + +#ifndef EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE + #define EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE 0x02 +#endif + +// relocation_info.r_length field has value 3 for 64-bit executables and value 2 for 32-bit executables +#if __LP64__ + #define RELOC_SIZE 3 + #define LC_SEGMENT_COMMAND LC_SEGMENT_64 + #define LC_ROUTINES_COMMAND LC_ROUTINES_64 + struct macho_segment_command : public segment_command_64 {}; + struct macho_section : public section_64 {}; + struct macho_routines_command : public routines_command_64 {}; +#else + #define RELOC_SIZE 2 + #define LC_SEGMENT_COMMAND LC_SEGMENT + #define LC_ROUTINES_COMMAND LC_ROUTINES + struct macho_segment_command : public segment_command {}; + struct macho_section : public section {}; + struct macho_routines_command : public routines_command {}; +#endif + + + +#if SUPPORT_ACCELERATE_TABLES + + +ImageLoaderMegaDylib* ImageLoaderMegaDylib::makeImageLoaderMegaDylib(const dyld_cache_header* header, long slide, const LinkContext& context) +{ + return new ImageLoaderMegaDylib(header, slide, context); +} + +struct DATAdyld { + void* dyldLazyBinder; // filled in at launch by dyld to point into dyld to &stub_binding_helper + void* dyldFuncLookup; // filled in at launch by dyld to point into dyld to &_dyld_func_lookup + ProgramVars vars; +}; + + + + +ImageLoaderMegaDylib::ImageLoaderMegaDylib(const dyld_cache_header* header, long slide, const LinkContext& context) + : ImageLoader("dyld shared cache", 0), _header(header), _linkEditBias(NULL), _slide(slide), + _lockArray(NULL), _lockArrayInUseCount(0) +{ + pthread_mutex_init(&_lockArrayGuard, NULL); + const dyld_cache_mapping_info* mappings = (const dyld_cache_mapping_info*)((uint8_t*)_header + _header->mappingOffset); + const dyld_cache_mapping_info* lastMapping = &mappings[_header->mappingCount-1]; + const dyld_cache_accelerator_info* accHeader = (dyld_cache_accelerator_info*)(_header->accelerateInfoAddr + slide); + for (const dyld_cache_mapping_info* m=mappings; m <= lastMapping; ++m) { + if ( m->initProt == VM_PROT_READ ) { + _linkEditBias = (uint8_t*)(m->address - m->fileOffset) + _slide; + } + } + + _endOfCacheInMemory = (void*)(lastMapping->address + lastMapping->size + slide); + _images = (const dyld_cache_image_info*)((uint8_t*)_header + _header->imagesOffset); + _imageExtras = (dyld_cache_image_info_extra*)((char*)accHeader + accHeader->imagesExtrasOffset); + _initializers = (dyld_cache_accelerator_initializer*)((char*)accHeader + accHeader->initializersOffset); + _reExportsArray = (uint16_t*)((char*)accHeader + accHeader->reExportListOffset); + _dependenciesArray = (uint16_t*)((char*)accHeader + accHeader->depListOffset); + _bottomUpArray = (uint16_t*)((char*)accHeader + accHeader->bottomUpListOffset); + _rangeTable = (dyld_cache_range_entry*)((char*)accHeader + accHeader->rangeTableOffset); + _rangeTableCount = accHeader->rangeTableCount; + _imageCount = accHeader->imageExtrasCount; + _stateFlags = (uint8_t*)calloc(_imageCount, 1); + _initializerCount = accHeader->initializersCount; + _dylibsTrieStart = (uint8_t*)accHeader + accHeader->dylibTrieOffset; + _dylibsTrieEnd = _dylibsTrieStart + accHeader->dylibTrieSize; + _imageTextInfo = (dyld_cache_image_text_info*)((uint8_t*)_header + _header->imagesTextOffset); + DATAdyld* dyldSection = (DATAdyld*)(accHeader->dyldSectionAddr + slide); + dyldSection->dyldLazyBinder = NULL; // not used by libdyld.dylib + dyldSection->dyldFuncLookup = (void*)&_dyld_func_lookup; + dyldSection->vars.mh = context.mainExecutable->machHeader(); + context.setNewProgramVars(dyldSection->vars); +} + + +void ImageLoaderMegaDylib::unreachable() const +{ + abort(); +} + +ImageLoaderMegaDylib::~ImageLoaderMegaDylib() +{ +} + +const char* ImageLoaderMegaDylib::getInstallPath() const { + unreachable(); +} + +const macho_header* ImageLoaderMegaDylib::getIndexedMachHeader(unsigned index) const +{ + if ( index > _header->imagesCount ) + dyld::throwf("cache image index out of range (%u), max=%u", index, _header->imagesCount - 1); + return (const macho_header*)(_images[index].address + _slide); +} + +const char* ImageLoaderMegaDylib::getIndexedPath(unsigned index) const +{ + if ( index > _header->imagesCount ) + dyld::throwf("cache image index out of range (%u), max=%u", index, _header->imagesCount - 1); + return (char*)_header + _images[index].pathFileOffset; +} + +const char* ImageLoaderMegaDylib::getIndexedShortName(unsigned index) const +{ + const char* path = getIndexedPath(index); + const char* lastSlash = strrchr(path, '/'); + if ( lastSlash == NULL ) + return path; + else + return lastSlash+1; +} + +void ImageLoaderMegaDylib::getDylibUUID(unsigned int index, uuid_t uuid) const +{ + if ( index > _header->imagesCount ) + dyld::throwf("cache image index out of range (%u), max=%u", index, _header->imagesCount - 1); + memcpy(uuid, _imageTextInfo[index].uuid, 16); +} + +void ImageLoaderMegaDylib::printSegments(const macho_header* mh) const +{ + const uint32_t cmd_count = mh->ncmds; + const struct load_command* const cmds = (struct load_command*)((uint8_t*)mh + sizeof(macho_header)); + const struct load_command* cmd = cmds; + const macho_segment_command* seg; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd) { + case LC_SEGMENT_COMMAND: + seg = (macho_segment_command*)cmd; + dyld::log("%18s at 0x%08lX->0x%08lX\n", seg->segname, (long)(seg->vmaddr + _slide), (long)(seg->vmaddr + seg->vmsize + _slide)); + break; + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } +} + +bool ImageLoaderMegaDylib::hasDylib(const char* path, unsigned* index) const +{ + const uint8_t* imageNode = ImageLoader::trieWalk(_dylibsTrieStart, _dylibsTrieEnd, path); + if ( imageNode == NULL ) { + #if __MAC_OS_X_VERSION_MIN_REQUIRED + // not all symlinks are recorded as aliases in accelerator tables + if ( (strncmp(path, "/usr/lib/", 9) == 0) || (strncmp(path, "/System/Library/", 16) == 0) ) { + char resolvedPath[PATH_MAX]; + if ( realpath(path, resolvedPath) != NULL ) { + imageNode = ImageLoader::trieWalk(_dylibsTrieStart, _dylibsTrieEnd, resolvedPath); + } + } + if ( imageNode == NULL ) + return false; + #else + return false; + #endif + } + *index = (unsigned)read_uleb128(imageNode, _dylibsTrieEnd); + return true; +} + +bool ImageLoaderMegaDylib::addressInCache(const void* address, const mach_header** mh, const char** path, unsigned* index) +{ + // quick out of bounds check +#if __x86_64__ + if ( (uintptr_t)address < 0x7FFF70000000LL ) + return false; +#else + if ( address < (void*)_header ) + return false; +#endif + if ( address > _endOfCacheInMemory ) + return false; + + uint64_t unslidAddress = (uint64_t)address - _slide; + // linear search for now + const dyld_cache_range_entry* rangeTableEnd = &_rangeTable[_rangeTableCount]; + for (const dyld_cache_range_entry* r = _rangeTable; r < rangeTableEnd; ++r) { + if ( (r->startAddress <= unslidAddress) && (unslidAddress < r->startAddress+r->size) ) { + *index = r->imageIndex; + *mh = (mach_header*)getIndexedMachHeader(r->imageIndex); + *path = getIndexedPath(r->imageIndex); + return true; + } + } + + return false; +} + + +bool ImageLoaderMegaDylib::findUnwindSections(const void* address, dyld_unwind_sections* info) +{ + const char* path; + unsigned index; + if ( addressInCache(address, &info->mh, &path, &index) ) { + info->dwarf_section = NULL; + info->dwarf_section_length = 0; + ImageLoaderMachO::findSection(info->mh, "__TEXT", "__eh_frame", (void**)&info->dwarf_section, &info->dwarf_section_length); + + info->compact_unwind_section = NULL; + info->compact_unwind_section_length = 0; + ImageLoaderMachO::findSection(info->mh, "__TEXT", "__unwind_info", (void**)&info->compact_unwind_section, &info->compact_unwind_section_length); + + return true; + } + return false; +} + + +unsigned ImageLoaderMegaDylib::findImageIndex(const LinkContext& context, const char* path) const +{ + unsigned index; + if ( hasDylib(path, &index) ) + return index; + + // Somehow we found the dylib in the cache, but it is not this literal string, try simple expansions of @rpath + if ( strncmp(path, "@rpath/", 7) == 0 ) { + std::vector rpathsFromMainExecutable; + context.mainExecutable->getRPaths(context, rpathsFromMainExecutable); + const char* trailingPath = &path[7]; + for (const char* anRPath : rpathsFromMainExecutable) { + if ( anRPath[0] != '/' ) + continue; + char possiblePath[strlen(anRPath) + strlen(trailingPath)+2]; + strcpy(possiblePath, anRPath); + if ( possiblePath[strlen(possiblePath)-1] != '/' ) + strcat(possiblePath, "/"); + strcat(possiblePath, trailingPath); + if ( hasDylib(possiblePath, &index) ) { + return index; + } + } + } + dyld::throwf("no cache image with name (%s)", path); +} + +void ImageLoaderMegaDylib::initializeCoalIterator(CoalIterator& it, unsigned int loadOrder, unsigned imageIndex) +{ + it.image = this; + it.symbolName = " "; + it.loadOrder = loadOrder; + it.weakSymbol = false; + it.symbolMatches = false; + it.done = false; + it.curIndex = 0; + it.endIndex = _imageExtras[imageIndex].weakBindingsSize; + it.address = 0; + it.type = 0; + it.addend = 0; + it.imageIndex = imageIndex; +} + +bool ImageLoaderMegaDylib::incrementCoalIterator(CoalIterator& it) +{ + if ( it.done ) + return false; + + if ( _imageExtras[it.imageIndex].weakBindingsSize == 0 ) { + /// hmmm, ld set MH_WEAK_DEFINES or MH_BINDS_TO_WEAK, but there is no weak binding info + it.done = true; + it.symbolName = "~~~"; + return true; + } + const uint8_t* start = (uint8_t*)(_imageExtras[it.imageIndex].weakBindingsAddr + _slide); + const uint8_t* end = (uint8_t*)(_imageExtras[it.imageIndex].weakBindingsAddr + _slide + _imageExtras[it.imageIndex].weakBindingsSize); + const uint8_t* p = start + it.curIndex; + uintptr_t count; + uintptr_t skip; + uint64_t segOffset; + unsigned segIndex; + const mach_header* mh; + while ( p < end ) { + uint8_t immediate = *p & BIND_IMMEDIATE_MASK; + uint8_t opcode = *p & BIND_OPCODE_MASK; + ++p; + switch (opcode) { + case BIND_OPCODE_DONE: + it.done = true; + it.curIndex = p - start; + it.symbolName = "~~~"; // sorts to end + return true; + case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: + it.symbolName = (char*)p; + it.weakSymbol = ((immediate & BIND_SYMBOL_FLAGS_NON_WEAK_DEFINITION) == 0); + it.symbolMatches = false; + while (*p != '\0') + ++p; + ++p; + it.curIndex = p - start; + return false; + case BIND_OPCODE_SET_TYPE_IMM: + it.type = immediate; + break; + case BIND_OPCODE_SET_ADDEND_SLEB: + it.addend = read_sleb128(p, end); + break; + case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: + segIndex = immediate; + segOffset = read_uleb128(p, end); + mh = (mach_header*)getIndexedMachHeader((unsigned)it.imageIndex); + if ( uintptr_t segPrefAddress = ImageLoaderMachO::segPreferredAddress(mh, segIndex) ) + it.address = segPrefAddress + segOffset + _slide; + else + dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has segment %d which is too large", segIndex); + break; + case BIND_OPCODE_ADD_ADDR_ULEB: + it.address += read_uleb128(p, end); + break; + case BIND_OPCODE_DO_BIND: + it.address += sizeof(intptr_t); + break; + case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: + it.address += read_uleb128(p, end) + sizeof(intptr_t); + break; + case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: + it.address += immediate*sizeof(intptr_t) + sizeof(intptr_t); + break; + case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: + count = read_uleb128(p, end); + skip = read_uleb128(p, end); + for (uint32_t i=0; i < count; ++i) { + it.address += skip + sizeof(intptr_t); + } + break; + default: + dyld::throwf("bad weak bind opcode '%d' found after processing %d bytes in '%s'", *p, (int)(p-start), this->getPath()); + } + } + /// hmmm, BIND_OPCODE_DONE is missing... + it.done = true; + it.symbolName = "~~~"; + //dyld::log("missing BIND_OPCODE_DONE for image %s\n", this->getPath()); + return true; +} + +uintptr_t ImageLoaderMegaDylib::getAddressCoalIterator(CoalIterator& it, const LinkContext& context) +{ + //dyld::log("looking for %s in %s\n", it.symbolName, this->getPath()); + uintptr_t address; + if ( findInChainedTries(context, it.symbolName, (unsigned)it.imageIndex, NULL, false, &address) ) { + return address; + } + return 0; +} + +void ImageLoaderMegaDylib::updateUsesCoalIterator(CoalIterator& it, uintptr_t value, ImageLoader* targetImage, unsigned targetIndex, const LinkContext& context) +{ + + const uint8_t* start = (uint8_t*)(_imageExtras[it.imageIndex].weakBindingsAddr + _slide); + const uint8_t* end = (uint8_t*)(_imageExtras[it.imageIndex].weakBindingsAddr + _slide + _imageExtras[it.imageIndex].weakBindingsSize); + const uint8_t* p = start + it.curIndex; + + uint8_t type = it.type; + uintptr_t address = it.address; + const char* symbolName = it.symbolName; + intptr_t addend = it.addend; + uint64_t segOffset; + unsigned segIndex; + const mach_header* mh; + uintptr_t count; + uintptr_t skip; + bool done = false; + bool boundSomething = false; + const char* targetImagePath = targetImage ? targetImage->getIndexedPath(targetIndex) : NULL; + while ( !done && (p < end) ) { + uint8_t immediate = *p & BIND_IMMEDIATE_MASK; + uint8_t opcode = *p & BIND_OPCODE_MASK; + ++p; + switch (opcode) { + case BIND_OPCODE_DONE: + done = true; + break; + case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: + done = true; + break; + case BIND_OPCODE_SET_TYPE_IMM: + type = immediate; + break; + case BIND_OPCODE_SET_ADDEND_SLEB: + addend = read_sleb128(p, end); + break; + case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: + segIndex = immediate; + segOffset = read_uleb128(p, end); + mh = (mach_header*)getIndexedMachHeader((unsigned)it.imageIndex); + if ( uintptr_t segPrefAddress = ImageLoaderMachO::segPreferredAddress(mh, segIndex) ) + address = segPrefAddress + segOffset + _slide; + else + dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has segment %d which is too large", segIndex); + break; + case BIND_OPCODE_ADD_ADDR_ULEB: + address += read_uleb128(p, end); + break; + case BIND_OPCODE_DO_BIND: + ImageLoaderMachO::bindLocation(context, address, value, type, symbolName, addend, getIndexedPath((unsigned)it.imageIndex), targetImagePath, "weak "); + boundSomething = true; + address += sizeof(intptr_t); + break; + case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: + ImageLoaderMachO::bindLocation(context, address, value, type, symbolName, addend, getIndexedPath((unsigned)it.imageIndex), targetImagePath, "weak "); + boundSomething = true; + address += read_uleb128(p, end) + sizeof(intptr_t); + break; + case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: + ImageLoaderMachO::bindLocation(context, address, value, type, symbolName, addend, getIndexedPath((unsigned)it.imageIndex), targetImagePath, "weak "); + boundSomething = true; + address += immediate*sizeof(intptr_t) + sizeof(intptr_t); + break; + case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: + count = read_uleb128(p, end); + skip = read_uleb128(p, end); + for (uint32_t i=0; i < count; ++i) { + ImageLoaderMachO::bindLocation(context, address, value, type, symbolName, addend, getIndexedPath((unsigned)it.imageIndex), targetImagePath, "weak "); + boundSomething = true; + address += skip + sizeof(intptr_t); + } + break; + default: + dyld::throwf("bad bind opcode %d in weak binding info", *p); + } + } + // C++ weak coalescing cannot be tracked by reference counting. Error on side of never unloading. + if ( boundSomething && (targetImage != this) ) + context.addDynamicReference(this, targetImage); +} + + +void ImageLoaderMegaDylib::appendImagesNeedingCoalescing(ImageLoader* images[], unsigned imageIndexes[], unsigned& count) +{ + for (unsigned i=0; i < _imageCount; ++i) { + uint16_t index = _bottomUpArray[i]; + if ( _stateFlags[index] == kStateUnused ) + continue; + if ( _imageExtras[index].weakBindingsSize == 0 ) + continue; + images[count] = this; + imageIndexes[count] = index; + ++count; + } +} + + +bool ImageLoaderMegaDylib::weakSymbolsBound(unsigned index) +{ + return ( _stateFlags[index] >= kStateFlagWeakBound ); +} + +void ImageLoaderMegaDylib::setWeakSymbolsBound(unsigned index) +{ + if ( _stateFlags[index] == kStateFlagBound ) + _stateFlags[index] = kStateFlagWeakBound; +} + + +void ImageLoaderMegaDylib::recursiveMarkLoaded(const LinkContext& context, unsigned imageIndex) +{ + if ( _stateFlags[imageIndex] != kStateUnused ) + return; + + const macho_header* mh = getIndexedMachHeader(imageIndex); + const char* path = getIndexedPath(imageIndex); + + if ( context.verboseLoading ) + dyld::log("dyld: loaded: %s\n", path); + if ( context.verboseMapping ) { + dyld::log("dyld: Using shared cached for %s\n", path); + printSegments(mh); + } + + // change state to "loaded" before recursing to break cycles + _stateFlags[imageIndex] = kStateLoaded; + ++fgImagesUsedFromSharedCache; + + dyld_image_info debuggerInfo; + debuggerInfo.imageLoadAddress = (mach_header*)mh; + debuggerInfo.imageFilePath = path; + debuggerInfo.imageFileModDate = 0; + addImagesToAllImages(1, &debuggerInfo); + + if ( _imageExtras[imageIndex].weakBindingsSize != 0 ) { + ++fgImagesRequiringCoalescing; + ++fgImagesHasWeakDefinitions; + } + + unsigned startArrayIndex = _imageExtras[imageIndex].dependentsStartArrayIndex; + for (int i=startArrayIndex; _dependenciesArray[i] != 0xFFFF; ++i) { + unsigned subDep = (_dependenciesArray[i] & 0x7FFF); // mask off upward bit + recursiveMarkLoaded(context, subDep); + } +} + +void ImageLoaderMegaDylib::recursiveLoadLibraries(const LinkContext& context, bool preflightOnly, const RPathChain& loaderRPaths, const char* loadPath) +{ + unsigned index = findImageIndex(context, loadPath); + recursiveMarkLoaded(context, index); +} + +unsigned int ImageLoaderMegaDylib::recursiveUpdateDepth(unsigned int maxDepth) +{ + setDepth(maxDepth); + return maxDepth; +} + + +const ImageLoader::Symbol* ImageLoaderMegaDylib::findExportedSymbol(const char* name, bool searchReExports, const char* thisPath, const ImageLoader** foundIn) const +{ + unsigned index; + if ( !hasDylib(thisPath, &index) ) + return NULL; + const uint8_t* exportNode; + const uint8_t* exportTrieEnd; + unsigned foundinIndex; + // always search re-exports + // the point of searchReExports was to break cycles in dylibs, we don't have cycles in cache, so ok to search deep + searchReExports = true; + if ( searchReExports ) { + if ( !exportTrieHasNodeRecursive(name, index, &exportNode, &exportTrieEnd, &foundinIndex) ) + return NULL; + } + else { + if ( !exportTrieHasNode(name, index, &exportNode, &exportTrieEnd) ) + return NULL; + } + *foundIn = this; + return (ImageLoader::Symbol*)exportNode; +} + +bool ImageLoaderMegaDylib::exportTrieHasNode(const char* symbolName, unsigned index, + const uint8_t** exportNode, const uint8_t** exportTrieEnd) const +{ + const uint8_t* start = (uint8_t*)(_imageExtras[index].exportsTrieAddr + _slide); + const uint32_t size = _imageExtras[index].exportsTrieSize; + if ( size == 0 ) + return false; + const uint8_t* end = start + size; + const uint8_t* node = ImageLoader::trieWalk(start, end, symbolName); + if ( node == NULL ) + return false; + *exportNode = node; + *exportTrieEnd = end; + return true; +} + +bool ImageLoaderMegaDylib::exportTrieHasNodeRecursive(const char* symbolName, unsigned index, + const uint8_t** exportNode, const uint8_t** exportTrieEnd, + unsigned* foundinIndex) const +{ + // look in trie for image index + if ( exportTrieHasNode(symbolName, index, exportNode, exportTrieEnd) ) { + *foundinIndex = index; + return true; + } + // recursively look in all re-exported tries + unsigned startArrayIndex = _imageExtras[index].reExportsStartArrayIndex; + for (int i=startArrayIndex; _reExportsArray[i] != 0xFFFF; ++i) { + unsigned reExIndex = _reExportsArray[i]; + if ( exportTrieHasNodeRecursive(symbolName, reExIndex, exportNode, exportTrieEnd, foundinIndex) ) + return true; + } + return false; +} + +bool ImageLoaderMegaDylib::findExportedSymbolAddress(const LinkContext& context, const char* symbolName, + const ImageLoader* requestorImage, int requestorOrdinalOfDef, + bool runResolver, const ImageLoader** foundIn, uintptr_t* address) const +{ + const char* definedImagePath = requestorImage->libPath(requestorOrdinalOfDef-1); + unsigned index = findImageIndex(context, definedImagePath); + *foundIn = this; + return findInChainedTries(context, symbolName, index, requestorImage, runResolver, address); +} + +uintptr_t ImageLoaderMegaDylib::getExportedSymbolAddress(const Symbol* sym, const LinkContext& context, + const ImageLoader* requestor, bool runResolver, const char* symbolName) const +{ + // scan for with trie contains this node + const uint8_t* exportTrieEnd = NULL; + unsigned imageIndex = 0xFFFF; + const macho_header* mh = NULL; + uint64_t unslidTrieNode = ((uintptr_t)sym) - _slide; + for (unsigned i=0; i < _imageCount; ++i) { + uint64_t start = _imageExtras[i].exportsTrieAddr; + uint64_t end = _imageExtras[i].exportsTrieAddr + _imageExtras[i].exportsTrieSize; + if ( (start < unslidTrieNode) && (unslidTrieNode < end) ) { + exportTrieEnd = (uint8_t*)(end + _slide); + imageIndex = i; + mh = (macho_header*)(_images[imageIndex].address + _slide); + break; + } + } + + if ( mh == NULL ) + dyld::throwf("getExportedSymbolAddress(Symbol=%p) not in a cache trie", sym); + + const uint8_t* exportNode = (const uint8_t*)sym; + uintptr_t address; + processExportNode(context, symbolName ? symbolName : "unknown", imageIndex, exportNode, exportTrieEnd, requestor, runResolver, &address); + return address; +} + +void ImageLoaderMegaDylib::processExportNode(const LinkContext& context, const char* symbolName, unsigned definedImageIndex, + const uint8_t* exportNode, const uint8_t* exportTrieEnd, + const ImageLoader* requestorImage, bool runResolver, uintptr_t* address) const +{ + const macho_header* mh = getIndexedMachHeader(definedImageIndex); + uintptr_t flags = read_uleb128(exportNode, exportTrieEnd); + uintptr_t rawAddress; + switch ( flags & EXPORT_SYMBOL_FLAGS_KIND_MASK ) { + case EXPORT_SYMBOL_FLAGS_KIND_REGULAR: + if ( runResolver && (flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER) ) { + // this node has a stub and resolver, run the resolver to get target address + uintptr_t stub = read_uleb128(exportNode, exportTrieEnd) + (uintptr_t)mh; // skip over stub + // interposing dylibs have the stub address as their replacee + uintptr_t interposedStub = interposedAddress(context, stub, requestorImage); + if ( interposedStub != stub ) { + *address = interposedStub; + return; + } + // stub was not interposed, so run resolver + typedef uintptr_t (*ResolverProc)(void); + ResolverProc resolver = (ResolverProc)(read_uleb128(exportNode, exportTrieEnd) + (uintptr_t)mh); + *address = (*resolver)(); + if ( context.verboseBind ) + dyld::log("dyld: resolver at %p returned 0x%08lX\n", resolver, *address); + return; + } + if ( flags & EXPORT_SYMBOL_FLAGS_REEXPORT ) { + // re-export from another dylib, lookup there + const uintptr_t ordinal = read_uleb128(exportNode, exportTrieEnd); + const char* importedName = (char*)exportNode; + if ( importedName[0] == '\0' ) + importedName = symbolName; + unsigned startArrayIndex = _imageExtras[definedImageIndex].dependentsStartArrayIndex; + unsigned reExImageIndex = _dependenciesArray[startArrayIndex + ordinal-1] & 0x7FFF; + if ( findInChainedTries(context, importedName, reExImageIndex, requestorImage, runResolver, address) ) + return; + dyld::throwf("re-exported symbol '%s' not found for image %s expected re-exported in %s, node=%p", + symbolName, getIndexedShortName(definedImageIndex), getIndexedShortName(reExImageIndex), exportNode); + } + rawAddress = read_uleb128(exportNode, exportTrieEnd) + (uintptr_t)mh; + *address = interposedAddress(context, rawAddress, requestorImage); + return; + case EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL: + if ( flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER ) + dyld::throwf("unsupported exported symbol kind. flags=%lu at node=%p", flags, exportNode); + *address = read_uleb128(exportNode, exportTrieEnd) + (uintptr_t)mh; + return; + case EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE: + if ( flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER ) + dyld::throwf("unsupported exported symbol kind. flags=%lu at node=%p", flags, exportNode); + *address = read_uleb128(exportNode, exportTrieEnd); + return; + default: + dyld::throwf("unsupported exported symbol kind. flags=%lu at node=%p", flags, exportNode); + } + dyld::throwf("unsupported exported symbol node=%p", exportNode); +} + +bool ImageLoaderMegaDylib::findInChainedTries(const LinkContext& context, const char* symbolName, unsigned definedImageIndex, + const ImageLoader* requestorImage, bool runResolver, uintptr_t* address) const +{ + //dyld::log("findInChainedTries(sym=%s, index=%u, path=%s)\n", symbolName, definedImageIndex, getIndexedPath(definedImageIndex)); + const uint8_t* exportNode; + const uint8_t* exportTrieEnd; + unsigned foundinIndex; + if ( !exportTrieHasNodeRecursive(symbolName, definedImageIndex, &exportNode, &exportTrieEnd, &foundinIndex) ) + return false; + + processExportNode(context, symbolName, foundinIndex, exportNode, exportTrieEnd, requestorImage, runResolver, address); + return true; +} + + +bool ImageLoaderMegaDylib::findInChainedTriesAndDependentsExcept(const LinkContext& context, const char* symbolName, unsigned imageIndex, + const ImageLoader* requestorImage, bool runResolver, bool alreadyVisited[], uintptr_t* address) const +{ + //dyld::log("findInChainedTriesAndDependentsExcept(sym=%s, index=%u, path=%s)\n", symbolName, imageIndex, getIndexedPath(imageIndex)); + if ( alreadyVisited[imageIndex] ) + return false; + alreadyVisited[imageIndex] = true; + + if ( findInChainedTries(context, symbolName, imageIndex, requestorImage, runResolver, address) ) + return true; + + unsigned startArrayIndex = _imageExtras[imageIndex].dependentsStartArrayIndex; + for (int i=startArrayIndex; _dependenciesArray[i] != 0xFFFF; ++i) { + // ignore upward links + if ( (_dependenciesArray[i] & 0x8000) == 0 ) { + unsigned depIndex = _dependenciesArray[i] & 0x7FFF; + if ( _stateFlags[depIndex] != kStateFlagInitialized ) + continue; + if ( findInChainedTriesAndDependentsExcept(context, symbolName, depIndex, requestorImage, runResolver, alreadyVisited, address) ) + return true; + } + } + return false; +} + +bool ImageLoaderMegaDylib::findInChainedTriesAndDependents(const LinkContext& context, const char* symbolName, unsigned definedImageIndex, + const ImageLoader* requestorImage, bool runResolver, uintptr_t* address) const +{ + //dyld::log("findInChainedTriesAndDependents(sym=%s, index=%u, path=%s)\n", symbolName, definedImageIndex, getIndexedPath(definedImageIndex)); + if ( findInChainedTries(context, symbolName, definedImageIndex, requestorImage, runResolver, address) ) + return true; + + bool alreadyVisited[_header->imagesCount]; + bzero(alreadyVisited, sizeof(alreadyVisited)); + return findInChainedTriesAndDependentsExcept(context, symbolName, definedImageIndex, requestorImage, runResolver, alreadyVisited, address); +} + + +bool ImageLoaderMegaDylib::flatFindSymbol(const char* name, bool onlyInCoalesced, const ImageLoader::Symbol** sym, const ImageLoader** image) +{ + // check export trie of all in-use images + for (unsigned i=0; i < _imageCount ; ++i) { + uint16_t imageIndex = _bottomUpArray[i]; + if ( _stateFlags[imageIndex] == kStateUnused ) + continue; + if ( onlyInCoalesced && (_imageExtras[imageIndex].weakBindingsSize == 0) ) + continue; + const uint8_t* exportNode; + const uint8_t* exportTrieEnd; + if ( exportTrieHasNode(name, imageIndex, &exportNode, &exportTrieEnd) ) { + *sym = (Symbol*)exportNode; + *image = this; + return true; + } + } + return false; +} + + +void ImageLoaderMegaDylib::markAllbound(const LinkContext& context) +{ + for (unsigned i=0; i < _imageCount; ++i) { + uint16_t imageIndex = _bottomUpArray[i]; + if ( _stateFlags[imageIndex] == kStateLoaded ) { + _stateFlags[imageIndex] = kStateFlagBound; + context.notifySingleFromCache(dyld_image_state_bound, (mach_header*)getIndexedMachHeader(imageIndex), getIndexedPath(imageIndex)); + } + } +} + + +void ImageLoaderMegaDylib::recursiveSpinLockAcquire(unsigned int imageIndex, mach_port_t thisThread) +{ + pthread_mutex_lock(&_lockArrayGuard); + if ( _lockArray == NULL ) + _lockArray = (recursive_lock*)calloc(_imageCount, sizeof(recursive_lock)); + _lockArrayInUseCount++; + pthread_mutex_unlock(&_lockArrayGuard); + + recursive_lock* imagesLock = &_lockArray[imageIndex]; + while ( !OSAtomicCompareAndSwap32Barrier(0, thisThread, (int*)&imagesLock->thread) ) { + if ( imagesLock->thread == thisThread ) + break; + } + imagesLock->count++; +} + +void ImageLoaderMegaDylib::recursiveSpinLockRelease(unsigned int imageIndex, mach_port_t thisThread) +{ + recursive_lock* imagesLock = &_lockArray[imageIndex]; + if ( --imagesLock->count == 0 ) + imagesLock->thread = 0; + + pthread_mutex_lock(&_lockArrayGuard); + _lockArrayInUseCount--; + if ( _lockArrayInUseCount == 0 ) { + free((void*)_lockArray); + _lockArray = NULL; + } + pthread_mutex_unlock(&_lockArrayGuard); +} + + +void ImageLoaderMegaDylib::recursiveInitialization(const LinkContext& context, mach_port_t thisThread, unsigned int imageIndex, + InitializerTimingList& timingInfo) +{ + // Don't do any locking until libSystem.dylib is initialized, so we can malloc() the lock array + bool useLock = dyld::gProcessInfo->libSystemInitialized; + if ( useLock ) + recursiveSpinLockAcquire(imageIndex, thisThread); + + // only run initializers if currently in bound state + if ( (_stateFlags[imageIndex] == kStateFlagBound) || (_stateFlags[imageIndex] == kStateFlagWeakBound) ) { + + // Each image in cache has its own lock. We only set the state to Initialized if we hold the lock for the image. + _stateFlags[imageIndex] = kStateFlagInitialized; + + // first recursively init all dependents + unsigned startArrayIndex = _imageExtras[imageIndex].dependentsStartArrayIndex; + for (int i=startArrayIndex; _dependenciesArray[i] != 0xFFFF; ++i) { + unsigned subDepIndex = _dependenciesArray[i]; + // ignore upward links + if ( (subDepIndex & 0x8000) == 0 ) + recursiveInitialization(context, thisThread, subDepIndex, timingInfo); + } + + // notify objc about this image + context.notifySingleFromCache(dyld_image_state_dependents_initialized, (mach_header*)getIndexedMachHeader(imageIndex), getIndexedPath(imageIndex)); + + // run all initializers for imageIndex + const dyld_cache_accelerator_initializer* pInitStart = _initializers; + const dyld_cache_accelerator_initializer* pInitEnd = &pInitStart[_initializerCount]; + bool ranSomeInitializers = false; + uint64_t t1 = mach_absolute_time(); + for (const dyld_cache_accelerator_initializer* p=pInitStart; p < pInitEnd; ++p) { + if ( p->imageIndex == imageIndex ) { + Initializer func = (Initializer)(p->functionOffset + (uintptr_t)_header); + if ( context.verboseInit ) + dyld::log("dyld: calling initializer function %p in %s\n", func, getIndexedPath(imageIndex)); + bool haveLibSystemHelpersBefore = (dyld::gLibSystemHelpers != NULL); + func(context.argc, context.argv, context.envp, context.apple, &context.programVars); + bool haveLibSystemHelpersAfter = (dyld::gLibSystemHelpers != NULL); + ranSomeInitializers = true; + if ( !haveLibSystemHelpersBefore && haveLibSystemHelpersAfter ) { + // now safe to use malloc() and other calls in libSystem.dylib + dyld::gProcessInfo->libSystemInitialized = true; + } + } + } + if ( ranSomeInitializers ) { + uint64_t t2 = mach_absolute_time(); + const char* shortName = strrchr(getIndexedPath(imageIndex), '/'); + if ( shortName == NULL ) + shortName = getIndexedPath(imageIndex); + else + ++shortName; + timingInfo.images[timingInfo.count].shortName = shortName; + timingInfo.images[timingInfo.count].initTime = (t2-t1); + timingInfo.count++; + } + } + + // only unlock if this frame locked (note: libSystemInitialized changes after libSystem's initializer is run) + if ( useLock ) + recursiveSpinLockRelease(imageIndex, thisThread); +} + + +void ImageLoaderMegaDylib::recursiveInitialization(const LinkContext& context, mach_port_t thisThread, const char* pathToInitialize, + InitializerTimingList& timingInfo, UninitedUpwards& uninitUps) +{ + unsigned imageIndex; + if ( hasDylib(pathToInitialize, &imageIndex) ) { + this->recursiveInitialization(context, thisThread, imageIndex, timingInfo); + } +} + +void ImageLoaderMegaDylib::recursiveBind(const LinkContext& context, bool forceLazysBound, bool neverUnload) +{ + markAllbound(context); +} + +uint8_t ImageLoaderMegaDylib::dyldStateToCacheState(dyld_image_states state) { + switch (state) { + case dyld_image_state_mapped: + case dyld_image_state_dependents_mapped: + return kStateLoaded; + case dyld_image_state_bound: + return kStateFlagBound; + case dyld_image_state_initialized: + return kStateFlagInitialized; + case dyld_image_state_rebased: + case dyld_image_state_dependents_initialized: + case dyld_image_state_terminated: + return kStateUnused; + } + return kStateUnused; +} + +void ImageLoaderMegaDylib::recursiveApplyInterposing(const LinkContext& context) +{ + if ( context.verboseInterposing ) + dyld::log("dyld: interposing %lu tuples onto shared cache\n", fgInterposingTuples.size()); + + +} + +unsigned ImageLoaderMegaDylib::appendImagesToNotify(dyld_image_states state, bool orLater, dyld_image_info* infos) +{ + uint8_t targetCacheState = dyldStateToCacheState(state); + if ( targetCacheState == kStateUnused ) + return 0; + + unsigned usedCount = 0; + for (int i=_imageCount-1; i > 0; --i) { + uint16_t index = _bottomUpArray[i]; + uint8_t imageState = _stateFlags[index]; + if ( imageState == kStateFlagWeakBound ) + imageState = kStateFlagBound; + if ( (imageState == targetCacheState) || (orLater && (imageState > targetCacheState)) ) { + infos[usedCount].imageLoadAddress = (mach_header*)getIndexedMachHeader(index); + infos[usedCount].imageFilePath = getIndexedPath(index); + infos[usedCount].imageFileModDate = 0; + ++usedCount; + } + } + return usedCount; +} + + +bool ImageLoaderMegaDylib::dlopenFromCache(const LinkContext& context, const char* path, int mode, void** handle) +{ + unsigned imageIndex; + if ( !hasDylib(path, &imageIndex) ) { + return false; + } + + // RTLD_NOLOAD means return handle if already loaded, but don't now load it + if ( mode & RTLD_NOLOAD ) { + dyld::gLibSystemHelpers->releaseGlobalDyldLock(); + if ( _stateFlags[imageIndex] == kStateUnused ) { + *handle = NULL; + return true; + } + } + else { + this->recursiveMarkLoaded(context, imageIndex); + context.notifyBatch(dyld_image_state_dependents_mapped, false); + this->markAllbound(context); + context.notifyBatch(dyld_image_state_bound, false); + + this->weakBind(context); + + // Release dyld global lock before running initializers in dlopen() with customer cache + dyld::gLibSystemHelpers->releaseGlobalDyldLock(); + + InitializerTimingList timingInfo[_initializerCount]; + timingInfo[0].count = 0; + mach_port_t thisThread = mach_thread_self(); + this->recursiveInitialization(context, thisThread, imageIndex, timingInfo[0]); + mach_port_deallocate(mach_task_self(), thisThread); + context.notifyBatch(dyld_image_state_initialized, false); + } + + *handle = makeCacheHandle(imageIndex, mode); + return true; +} + +bool ImageLoaderMegaDylib::makeCacheHandle(const LinkContext& context, unsigned cacheIndex, int mode, void** result) +{ + if ( cacheIndex >= _imageCount ) + return false; + + *result = makeCacheHandle(cacheIndex, mode); + return true; +} + +void* ImageLoaderMegaDylib::makeCacheHandle(unsigned index, int mode) +{ + uint8_t flags = ((mode & RTLD_FIRST) ? 1 : 0); + +#if __LP64__ + return (void*)(uintptr_t)( 0xFFEEDDCC00000000LL | (index << 8) | flags); +#else + return (void*)(uintptr_t)( 0xF8000000 | (index << 8) | flags); +#endif +} + +bool ImageLoaderMegaDylib::isCacheHandle(void* handle, unsigned* index, uint8_t* flags) +{ +#if __LP64__ + if ( (((uintptr_t)handle) >> 32) == 0xFFEEDDCC ) { + if ( index ) + *index = (((uintptr_t)handle) >> 8) & 0xFFFF; + if ( flags ) + *flags = ((uintptr_t)handle) & 0xFF; + return true; + } +#else + if ( (((uintptr_t)handle) >> 24) == 0xF8 ) { + if ( index ) + *index = (((uintptr_t)handle) >> 8) & 0xFFFF; + if ( flags ) + *flags = ((uintptr_t)handle) & 0xFF; + return true; + } +#endif + return false; +} + + +void* ImageLoaderMegaDylib::dlsymFromCache(const LinkContext& context, void* handle, const char* symbolName, unsigned imageIndex) +{ + unsigned indexInHandle; + uint8_t flags; + uintptr_t symAddress; + if ( handle == RTLD_SELF ) { + if ( findInChainedTriesAndDependents(context, symbolName, imageIndex, NULL, true, &symAddress) ) + return (void*)symAddress; + } + else if ( handle == RTLD_NEXT ) { + // FIXME: really need to not look in imageIndex, but look in others. + if ( findInChainedTriesAndDependents(context, symbolName, imageIndex, NULL, true, &symAddress) ) + return (void*)symAddress; + } + else if ( isCacheHandle(handle, &indexInHandle, &flags) ) { + bool searchOnlyFirst = (flags & 1); // RTLD_FIRST + // normal dlsym(handle,) semantics is that the handle is just the first place to search. RTLD_FIRST disables that + if ( searchOnlyFirst ) { + if ( findInChainedTries(context, symbolName, indexInHandle, NULL, true, &symAddress) ) + return (void*)symAddress; + } + else { + if ( findInChainedTriesAndDependents(context, symbolName, indexInHandle, NULL, true, &symAddress) ) + return (void*)symAddress; + } + } + + return NULL; +} + +bool ImageLoaderMegaDylib::dladdrFromCache(const void* address, Dl_info* info) +{ + const mach_header* mh; + unsigned index; + if ( !addressInCache(address, &mh, &info->dli_fname, &index) ) + return false; + + info->dli_fbase = (void*)mh; + if ( address == mh ) { + // special case lookup of header + info->dli_sname = "__dso_handle"; + info->dli_saddr = info->dli_fbase; + return true; + } + + // find closest symbol in the image + info->dli_sname = ImageLoaderMachO::findClosestSymbol(mh, address, (const void**)&info->dli_saddr); + + // never return the mach_header symbol + if ( info->dli_saddr == info->dli_fbase ) { + info->dli_sname = NULL; + info->dli_saddr = NULL; + return true; + } + + // strip off leading underscore + if ( info->dli_sname != NULL ) { + if ( info->dli_sname[0] == '_' ) + info->dli_sname = info->dli_sname +1; + } + return true; +} + + +uintptr_t ImageLoaderMegaDylib::bindLazy(uintptr_t lazyBindingInfoOffset, const LinkContext& context, const mach_header* mh, unsigned imageIndex) +{ + const dyld_info_command* dyldInfoCmd = ImageLoaderMachO::findDyldInfoLoadCommand(mh); + if ( dyldInfoCmd == NULL ) + return 0; + + const uint8_t* const lazyInfoStart = &_linkEditBias[dyldInfoCmd->lazy_bind_off]; + const uint8_t* const lazyInfoEnd = &lazyInfoStart[dyldInfoCmd->lazy_bind_size]; + uint32_t lbOffset = (uint32_t)lazyBindingInfoOffset; + uint8_t segIndex; + uintptr_t segOffset; + int libraryOrdinal; + const char* symbolName; + bool doneAfterBind; + if ( ImageLoaderMachO::getLazyBindingInfo(lbOffset, lazyInfoStart, lazyInfoEnd, &segIndex, &segOffset, &libraryOrdinal, &symbolName, &doneAfterBind) ) { + //const char* thisPath = getIndexedPath(imageIndex); + //dyld::log("%s needs symbol '%s' from ordinal=%d\n", thisPath, symbolName, libraryOrdinal); + unsigned startDepArrayIndex = _imageExtras[imageIndex].dependentsStartArrayIndex; + unsigned targetIndex; + if ( libraryOrdinal == BIND_SPECIAL_DYLIB_SELF ) + targetIndex = imageIndex; + else + targetIndex = _dependenciesArray[startDepArrayIndex+libraryOrdinal-1] & 0x7FFF; + //const char* targetPath = getIndexedPath(targetIndex); + //dyld::log("%s needs symbol '%s' from %s\n", thisPath, symbolName, targetPath); + uintptr_t targetAddress; + if ( findInChainedTries(context, symbolName, targetIndex, this, true, &targetAddress) ) { + if ( uintptr_t segPrefAddress = ImageLoaderMachO::segPreferredAddress(mh, segIndex) ) { + uintptr_t* lp = (uintptr_t*)(segPrefAddress + segOffset + _slide); + //dyld::log(" storing 0x%0lX to lp %p\n", targetAddress, lp); + *lp = targetAddress; + return targetAddress; + } + } + } + + return 0; +} + + +#endif // SUPPORT_ACCELERATE_TABLES + + + diff --git a/src/dyld-apple/src/ImageLoaderMegaDylib.h b/src/dyld-apple/src/ImageLoaderMegaDylib.h new file mode 100644 index 000000000..425ec8e7b --- /dev/null +++ b/src/dyld-apple/src/ImageLoaderMegaDylib.h @@ -0,0 +1,255 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2015 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@ + */ + + +#ifndef __IMAGELOADER_MEGADYLIB__ +#define __IMAGELOADER_MEGADYLIB__ + +#include +#include + +#include "ImageLoaderMachO.h" +#include "dyld_cache_format.h" + + +// +// ImageLoaderMegaDylib is the concrete subclass of ImageLoader which represents +// all dylibs in the shared cache. +// +class ImageLoaderMegaDylib : public ImageLoader { +public: + static ImageLoaderMegaDylib* makeImageLoaderMegaDylib(const dyld_cache_header*, long slide, const LinkContext&); + + + virtual ~ImageLoaderMegaDylib(); + + void appendImagesNeedingCoalescing(ImageLoader* images[], unsigned imageIndex[], unsigned& count); + virtual void initializeCoalIterator(CoalIterator&, unsigned int loadOrder, unsigned imageIndex); + virtual bool incrementCoalIterator(CoalIterator&); + virtual uintptr_t getAddressCoalIterator(CoalIterator&, const LinkContext& contex); + virtual void updateUsesCoalIterator(CoalIterator&, uintptr_t newAddr, ImageLoader* target, unsigned imageIndex, const LinkContext& context); + + virtual const char* getIndexedPath(unsigned index) const; + virtual const char* getIndexedShortName(unsigned) const; + virtual const char* getInstallPath() const; + virtual bool inSharedCache() const { return true; } + virtual bool containsSymbol(const void* addr) const { unreachable(); } + virtual void* getThreadPC() const { unreachable(); } + virtual void* getMain() const { unreachable(); } + virtual const struct mach_header* machHeader() const { unreachable(); } + virtual uintptr_t getSlide() const { return _slide; } + virtual const void* getEnd() const { unreachable(); } + virtual bool hasCoalescedExports() const { unreachable(); } + virtual bool findExportedSymbolAddress(const LinkContext& context, const char* symbolName, + const ImageLoader* requestorImage, int requestorOrdinalOfDef, + bool runResolver, const ImageLoader** foundIn, uintptr_t* address) const; + virtual const Symbol* findExportedSymbol(const char* name, bool searchReExports, const char* thisPath, const ImageLoader** foundIn) const; + virtual uintptr_t getExportedSymbolAddress(const Symbol* sym, const LinkContext& context, + const ImageLoader* requestor, bool runResolver, const char* symbolName) const; + virtual DefinitionFlags getExportedSymbolInfo(const Symbol* sym) const { unreachable(); } + virtual const char* getExportedSymbolName(const Symbol* sym) const { unreachable(); } + virtual uint32_t getExportedSymbolCount() const { unreachable(); } + virtual const Symbol* getIndexedExportedSymbol(uint32_t index) const { unreachable(); } + + virtual uint32_t getImportedSymbolCount() const { unreachable(); } + virtual const Symbol* getIndexedImportedSymbol(uint32_t index) const { unreachable(); } + virtual ReferenceFlags getImportedSymbolInfo(const Symbol* sym) const { unreachable(); } + virtual const char* getImportedSymbolName(const Symbol* sym) const { unreachable(); } + virtual const char* findClosestSymbol(const void* addr, const void** closestAddr) const { unreachable(); } + virtual bool isBundle() const { return false; } + virtual bool isDylib() const { return true; } + virtual bool isExecutable() const { unreachable(); } + virtual bool isPositionIndependentExecutable() const { unreachable(); } + virtual bool forceFlat() const { unreachable(); } + virtual uintptr_t doBindLazySymbol(uintptr_t* lazyPointer, const LinkContext& context) { unreachable(); } + virtual uintptr_t doBindFastLazySymbol(uint32_t lazyBindingInfoOffset, const LinkContext& context, + void (*lock)(), void (*unlock)()) { unreachable(); } + virtual void doTermination(const LinkContext& context) { unreachable(); } + virtual bool needsInitialization() { unreachable(); } + virtual bool getSectionContent(const char* segmentName, const char* sectionName, void** start, size_t* length) { unreachable(); } + virtual void getUnwindInfo(dyld_unwind_sections* info) { unreachable(); } + virtual bool findSection(const void* imageInterior, const char** segmentName, const char** sectionName, size_t* sectionOffset) { unreachable(); } + virtual bool isPrebindable() const { unreachable(); } + virtual bool usablePrebinding(const LinkContext& context) const { unreachable(); } + virtual void getRPaths(const LinkContext& context, std::vector&) const { } + virtual bool participatesInCoalescing() const { unreachable(); } + virtual bool getUUID(uuid_t) const { unreachable(); } + virtual void dynamicInterpose(const LinkContext& context) { unreachable(); } + void addDynamicInterposingTuples(const struct dyld_interpose_tuple array[], size_t count) { unreachable(); } + virtual unsigned int segmentCount() const { unreachable(); } + virtual const char* segName(unsigned int) const { unreachable(); } + virtual uintptr_t segSize(unsigned int) const { unreachable(); } + virtual uintptr_t segFileSize(unsigned int) const { unreachable(); } + virtual bool segHasTrailingZeroFill(unsigned int) { unreachable(); } + virtual uintptr_t segFileOffset(unsigned int) const { unreachable(); } + virtual bool segReadable(unsigned int) const { unreachable(); } + virtual bool segWriteable(unsigned int) const { unreachable(); } + virtual bool segExecutable(unsigned int) const { unreachable(); } + virtual bool segUnaccessible(unsigned int) const { unreachable(); } + virtual bool segHasPreferredLoadAddress(unsigned int) const { unreachable(); } + virtual uintptr_t segPreferredLoadAddress(unsigned int) const { unreachable(); } + virtual uintptr_t segActualLoadAddress(unsigned int) const { unreachable(); } + virtual uintptr_t segActualEndAddress(unsigned int) const { unreachable(); } + + + // info from LC_VERSION_MIN_MACOSX or LC_VERSION_MIN_IPHONEOS + virtual uint32_t sdkVersion() const { unreachable(); } + virtual uint32_t minOSVersion() const { unreachable(); } + + // if the image contains interposing functions, register them + virtual void registerInterposing() { unreachable(); } + + virtual ImageLoader* libImage(unsigned int) const { unreachable(); } + virtual bool libReExported(unsigned int) const { unreachable(); } + virtual bool libIsUpward(unsigned int) const { unreachable(); } + virtual void setLibImage(unsigned int, ImageLoader*, bool, bool) { unreachable(); } + virtual const char* libPath(unsigned int) const { unreachable(); } + + unsigned appendImagesToNotify(dyld_image_states state, bool orLater, dyld_image_info* infos); + const char* notify(dyld_image_states state, bool orLater, dyld_image_state_change_handler); + bool dlopenFromCache(const LinkContext& context, const char* path, int mode, void** handle); + bool makeCacheHandle(const LinkContext& context, unsigned cacheIndex, int mode, void** result); + void* dlsymFromCache(const LinkContext& context, void* handle, const char* symName, unsigned index); + bool isCacheHandle(void* handle, unsigned* index, uint8_t* flags); + bool hasDylib(const char* path, unsigned* index) const; + bool addressInCache(const void* address, const mach_header** mh, const char** path, unsigned* index); + bool findUnwindSections(const void* addr, dyld_unwind_sections* info); + bool dladdrFromCache(const void* address, Dl_info* info); + uintptr_t bindLazy(uintptr_t lazyBindingInfoOffset, const LinkContext& context, const mach_header* mh, unsigned index); + bool flatFindSymbol(const char* name, bool onlyInCoalesced, const ImageLoader::Symbol** sym, const ImageLoader** image); + void getDylibUUID(unsigned int index, uuid_t) const; + +protected: + virtual void setDyldInfo(const dyld_info_command* dyldInfo) { unreachable(); } + virtual void setSymbolTableInfo(const macho_nlist*, const char*, const dysymtab_command*) { unreachable(); } + virtual uint32_t* segmentCommandOffsets() const { unreachable(); } + virtual void rebase(const LinkContext& context, uintptr_t slide) { unreachable(); } + virtual uintptr_t exportedSymbolAddress(const LinkContext& context, const Symbol* symbol, const ImageLoader* requestor, bool runResolver) const { unreachable(); } + virtual bool exportedSymbolIsWeakDefintion(const Symbol* symbol) const { unreachable(); } + virtual const char* exportedSymbolName(const Symbol* symbol) const { unreachable(); } + virtual unsigned int exportedSymbolCount() const { unreachable(); } + virtual const ImageLoader::Symbol* exportedSymbolIndexed(unsigned int) const { unreachable(); } + virtual unsigned int importedSymbolCount() const { unreachable(); } + virtual const ImageLoader::Symbol* importedSymbolIndexed(unsigned int) const { unreachable(); } + virtual const char* importedSymbolName(const Symbol* symbol) const { unreachable(); } +#if PREBOUND_IMAGE_SUPPORT + virtual void resetPreboundLazyPointers(const LinkContext& context) { unreachable(); } +#endif + + virtual void recursiveLoadLibraries(const LinkContext& context, bool preflightOnly, const RPathChain& loaderRPaths, const char* loadPath); + virtual unsigned recursiveUpdateDepth(unsigned int maxDepth); + virtual void recursiveRebase(const LinkContext& context) { } + virtual void recursiveBind(const LinkContext& context, bool forceLazysBound, bool neverUnload); + virtual void recursiveApplyInterposing(const LinkContext& context); + virtual void recursiveGetDOFSections(const LinkContext& context, std::vector& dofs) { } + virtual void recursiveInitialization(const LinkContext& context, mach_port_t this_thread, const char* pathToInitialize, + ImageLoader::InitializerTimingList&, ImageLoader::UninitedUpwards&); + + virtual void doGetDependentLibraries(DependentLibraryInfo libs[]) { unreachable(); } + virtual LibraryInfo doGetLibraryInfo(const LibraryInfo& requestorInfo) { return requestorInfo; } + virtual void doRebase(const LinkContext& context) { unreachable(); } + virtual void doBind(const LinkContext& context, bool forceLazysBound) { unreachable(); } + virtual void doBindJustLazies(const LinkContext& context) { unreachable(); } + virtual void doGetDOFSections(const LinkContext& context, std::vector& dofs) { unreachable(); } + virtual void doInterpose(const LinkContext& context) { unreachable(); } + virtual bool doInitialization(const LinkContext& context) { unreachable(); } + virtual bool needsTermination() { unreachable(); } + virtual bool segmentsMustSlideTogether() const { unreachable(); } + virtual bool segmentsCanSlide() const { unreachable(); } + virtual void setSlide(intptr_t slide) { unreachable(); } + bool allDependentLibrariesAsWhenPreBound() const { unreachable(); } + virtual bool isSubframeworkOf(const LinkContext& context, const ImageLoader* image) const { return false; } + virtual bool hasSubLibrary(const LinkContext& context, const ImageLoader* child) const { return false; } + virtual bool weakSymbolsBound(unsigned index); + virtual void setWeakSymbolsBound(unsigned index); + +private: + ImageLoaderMegaDylib(const dyld_cache_header*, long slide, const LinkContext&); + + const macho_header* getIndexedMachHeader(unsigned index) const; + const uint8_t* getIndexedTrie(unsigned index, uint32_t& trieSize) const; + unsigned findImageIndex(const LinkContext& context, const char* path) const; + void recursiveMarkLoaded(const LinkContext& context, unsigned imageIndex); + void markAllbound(const LinkContext& context); + bool findInChainedTries(const LinkContext& context, const char* symbolName, unsigned definedImageIndex, + const ImageLoader* requestorImage, bool runResolver, uintptr_t* address) const; + bool findInChainedTriesAndDependents(const LinkContext& context, const char* symbolName, unsigned definedImageIndex, + const ImageLoader* requestorImage, bool runResolver, uintptr_t* address) const; + bool findInChainedTriesAndDependentsExcept(const LinkContext& context, const char* symbolName, unsigned imageIndex, + const ImageLoader* requestorImage, bool runResolver, bool alreadyVisited[], uintptr_t* address) const; + bool exportTrieHasNodeRecursive(const char* symbolName, unsigned index, + const uint8_t** exportNode, const uint8_t** exportTrieEnd, + unsigned* foundinIndex) const; + bool exportTrieHasNode(const char* symbolName, unsigned index, + const uint8_t** exportNode, const uint8_t** exportTrieEnd) const; + + void initAllLoaded(const LinkContext& context, InitializerTimingList& timingInfo); + void printSegments(const macho_header* mh) const; + void* makeCacheHandle(unsigned index, int mode); + uint8_t flagsFromCacheHandle(void* handle); + void processExportNode(const LinkContext& context, const char* symbolName, unsigned definedImageIndex, + const uint8_t* exportNode, const uint8_t* exportTrieEnd, + const ImageLoader* requestorImage, bool runResolver, uintptr_t* address) const; + static uint8_t dyldStateToCacheState(dyld_image_states state); + void recursiveInitialization(const LinkContext& context, mach_port_t this_thread, unsigned int imageIndex, + InitializerTimingList& timingInfo); + void recursiveSpinLockAcquire(unsigned int imageIndex, mach_port_t thisThread); + void recursiveSpinLockRelease(unsigned int imageIndex, mach_port_t thisThread); + + __attribute__((noreturn)) + void unreachable() const; + + enum { kStateUnused=0, kStateLoaded=1, kStateFlagBound=2, kStateFlagWeakBound=3, kStateFlagInitialized=4 }; + + + const dyld_cache_header* _header; + const void* _endOfCacheInMemory; + const uint8_t* _linkEditBias; + const dyld_cache_image_info* _images; + const dyld_cache_image_info_extra* _imageExtras; + long _slide; + const dyld_cache_accelerator_initializer* _initializers; + const dyld_cache_range_entry* _rangeTable; + const uint16_t* _reExportsArray; + const uint16_t* _dependenciesArray; + const uint16_t* _bottomUpArray; + const uint8_t* _dylibsTrieStart; + const uint8_t* _dylibsTrieEnd; + const dyld_cache_image_text_info* _imageTextInfo; + uint8_t* _stateFlags; + uint32_t _imageCount; + uint32_t _initializerCount; + uint32_t _rangeTableCount; + pthread_mutex_t _lockArrayGuard; + ImageLoader::recursive_lock* _lockArray; + unsigned int _lockArrayInUseCount; +}; + + + +#endif // __IMAGELOADER_MEGADYLIB__ + + + + diff --git a/src/dyld-apple/src/dyld.cpp b/src/dyld-apple/src/dyld.cpp new file mode 100644 index 000000000..ba7c7cc8e --- /dev/null +++ b/src/dyld-apple/src/dyld.cpp @@ -0,0 +1,6080 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2004-2013 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@ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include // mach_absolute_time() +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include <_simple.h> +//#include +#include +//#include +//#include +#include +#include +#include "../dyld_kernel.h" + +#include + +#ifndef CPU_SUBTYPE_ARM_V5TEJ + #define CPU_SUBTYPE_ARM_V5TEJ ((cpu_subtype_t) 7) +#endif +#ifndef CPU_SUBTYPE_ARM_XSCALE + #define CPU_SUBTYPE_ARM_XSCALE ((cpu_subtype_t) 8) +#endif +#ifndef CPU_SUBTYPE_ARM_V7 + #define CPU_SUBTYPE_ARM_V7 ((cpu_subtype_t) 9) +#endif +#ifndef CPU_SUBTYPE_ARM_V7F + #define CPU_SUBTYPE_ARM_V7F ((cpu_subtype_t) 10) +#endif +#ifndef CPU_SUBTYPE_ARM_V7S + #define CPU_SUBTYPE_ARM_V7S ((cpu_subtype_t) 11) +#endif +#ifndef CPU_SUBTYPE_ARM_V7K + #define CPU_SUBTYPE_ARM_V7K ((cpu_subtype_t) 12) +#endif +#ifndef LC_DYLD_ENVIRONMENT + #define LC_DYLD_ENVIRONMENT 0x27 +#endif + +#ifndef CPU_SUBTYPE_X86_64_H + #define CPU_SUBTYPE_X86_64_H ((cpu_subtype_t) 8) +#endif + +#ifndef VM_PROT_SLIDE + #define VM_PROT_SLIDE 0x20 +#endif + +#include +#include + +#include "mach-o/dyld_gdb.h" + +#include "dyld.h" +#include "ImageLoader.h" +#include "ImageLoaderMachO.h" +#include "dyldLibSystemInterface.h" +#if DYLD_SHARED_CACHE_SUPPORT +#include "dyld_cache_format.h" +#endif +#include "dyld_process_info_internal.h" +//#include +#if TARGET_IPHONE_SIMULATOR + extern "C" void xcoresymbolication_load_notifier(void *connection, uint64_t load_timestamp, const char *image_path, const struct mach_header *mach_header); + extern "C" void xcoresymbolication_unload_notifier(void *connection, uint64_t unload_timestamp, const char *image_path, const struct mach_header *mach_header); + #define coresymbolication_load_notifier(c, t, p, h) xcoresymbolication_load_notifier(c, t, p, h) + #define coresymbolication_unload_notifier(c, t, p, h) xcoresymbolication_unload_notifier(c, t, p, h) +#endif + +#if SUPPORT_ACCELERATE_TABLES + #include "ImageLoaderMegaDylib.h" +#endif + +#if TARGET_IPHONE_SIMULATOR + extern "C" void* gSyscallHelpers; +#else + #include "dyldSyscallInterface.h" +#endif + + +// not libc header for send() syscall interface +extern "C" ssize_t __sendto(int, const void *, size_t, int, const struct sockaddr *, socklen_t); + + +// ARM and x86_64 are the only architecture that use cpu-sub-types +#define CPU_SUBTYPES_SUPPORTED ((__arm__ || __x86_64__) && !TARGET_IPHONE_SIMULATOR) + +#if __LP64__ + #define LC_SEGMENT_COMMAND LC_SEGMENT_64 + #define LC_SEGMENT_COMMAND_WRONG LC_SEGMENT + #define LC_ENCRYPT_COMMAND LC_ENCRYPTION_INFO + #define macho_segment_command segment_command_64 + #define macho_section section_64 +#else + #define LC_SEGMENT_COMMAND LC_SEGMENT + #define LC_SEGMENT_COMMAND_WRONG LC_SEGMENT_64 + #define LC_ENCRYPT_COMMAND LC_ENCRYPTION_INFO_64 + #define macho_segment_command segment_command + #define macho_section section +#endif + + + +#define CPU_TYPE_MASK 0x00FFFFFF /* complement of CPU_ARCH_MASK */ + + +/* implemented in dyld_gdb.cpp */ +extern void resetAllImages(); +extern void addImagesToAllImages(uint32_t infoCount, const dyld_image_info info[]); +extern void removeImageFromAllImages(const mach_header* mh); +extern void addNonSharedCacheImageUUID(const dyld_uuid_info& info); +extern const char* notifyGDB(enum dyld_image_states state, uint32_t infoCount, const dyld_image_info info[]); +extern size_t allImagesCount(); + +// magic so CrashReporter logs message +extern "C" { + char error_string[1024]; +} + +// magic linker symbol for start of dyld binary +extern "C" const macho_header __dso_handle; + + +// +// The file contains the core of dyld used to get a process to main(). +// The API's that dyld supports are implemented in dyldAPIs.cpp. +// +// +// +// +// +namespace dyld { + struct RegisteredDOF { const mach_header* mh; int registrationID; }; + struct DylibOverride { const char* installName; const char* override; }; +} + + +VECTOR_NEVER_DESTRUCTED(ImageLoader*); +VECTOR_NEVER_DESTRUCTED(dyld::RegisteredDOF); +VECTOR_NEVER_DESTRUCTED(dyld::ImageCallback); +VECTOR_NEVER_DESTRUCTED(dyld::DylibOverride); +VECTOR_NEVER_DESTRUCTED(ImageLoader::DynamicReference); + +VECTOR_NEVER_DESTRUCTED(dyld_image_state_change_handler); + +namespace dyld { + + +// +// state of all environment variables dyld uses +// +struct EnvironmentVariables { + const char* const * DYLD_FRAMEWORK_PATH; + const char* const * DYLD_FALLBACK_FRAMEWORK_PATH; + const char* const * DYLD_LIBRARY_PATH; + const char* const * DYLD_FALLBACK_LIBRARY_PATH; + const char* const * DYLD_INSERT_LIBRARIES; + const char* const * LD_LIBRARY_PATH; // for unix conformance + const char* const * DYLD_VERSIONED_LIBRARY_PATH; + const char* const * DYLD_VERSIONED_FRAMEWORK_PATH; + bool DYLD_PRINT_LIBRARIES_POST_LAUNCH; + bool DYLD_BIND_AT_LAUNCH; + bool DYLD_PRINT_STATISTICS; + bool DYLD_PRINT_STATISTICS_DETAILS; + bool DYLD_PRINT_OPTS; + bool DYLD_PRINT_ENV; + bool DYLD_DISABLE_DOFS; + bool DYLD_PRINT_CS_NOTIFICATIONS; + // DYLD_SHARED_CACHE_DONT_VALIDATE ==> sSharedCacheIgnoreInodeAndTimeStamp + // DYLD_SHARED_CACHE_DIR ==> sSharedCacheDir + // DYLD_ROOT_PATH ==> gLinkContext.rootPaths + // DYLD_IMAGE_SUFFIX ==> gLinkContext.imageSuffix + // DYLD_PRINT_OPTS ==> gLinkContext.verboseOpts + // DYLD_PRINT_ENV ==> gLinkContext.verboseEnv + // DYLD_FORCE_FLAT_NAMESPACE ==> gLinkContext.bindFlat + // DYLD_PRINT_INITIALIZERS ==> gLinkContext.verboseInit + // DYLD_PRINT_SEGMENTS ==> gLinkContext.verboseMapping + // DYLD_PRINT_BINDINGS ==> gLinkContext.verboseBind + // DYLD_PRINT_WEAK_BINDINGS ==> gLinkContext.verboseWeakBind + // DYLD_PRINT_REBASINGS ==> gLinkContext.verboseRebase + // DYLD_PRINT_DOFS ==> gLinkContext.verboseDOF + // DYLD_PRINT_APIS ==> gLogAPIs + // DYLD_IGNORE_PREBINDING ==> gLinkContext.prebindUsage + // DYLD_PREBIND_DEBUG ==> gLinkContext.verbosePrebinding + // DYLD_NEW_LOCAL_SHARED_REGIONS ==> gLinkContext.sharedRegionMode + // DYLD_SHARED_REGION ==> gLinkContext.sharedRegionMode + // DYLD_PRINT_WARNINGS ==> gLinkContext.verboseWarnings + // DYLD_PRINT_RPATHS ==> gLinkContext.verboseRPaths + // DYLD_PRINT_INTERPOSING ==> gLinkContext.verboseInterposing + // DYLD_PRINT_LIBRARIES ==> gLinkContext.verboseLoading +}; + + + +typedef std::vector StateHandlers; + + +enum EnvVarMode { envNone, envPrintOnly, envAll }; + +static const char* _simple_getenv(const char* envp[], const char* key) +{ + const size_t len = strlen(key); + for (int i = 0; envp[i] != NULL; i++) + { + if (strncmp(envp[i], key, len) == 0 && envp[i][len] == '=') + return envp[i] + len + 1; + } + return NULL; +} + +// all global state +static const char* sExecPath = NULL; +static const char* sExecShortName = NULL; +static const macho_header* sMainExecutableMachHeader = NULL; +#if CPU_SUBTYPES_SUPPORTED +static cpu_type_t sHostCPU; +static cpu_subtype_t sHostCPUsubtype; +#endif +static ImageLoaderMachO* sMainExecutable = NULL; +static EnvVarMode sEnvMode = envNone; +static size_t sInsertedDylibCount = 0; +static std::vector sAllImages; +static std::vector sImageRoots; +static std::vector sImageFilesNeedingTermination; +static std::vector sImageFilesNeedingDOFUnregistration; +static std::vector sAddImageCallbacks; +static std::vector sRemoveImageCallbacks; +static bool sRemoveImageCallbacksInUse = false; +static void* sSingleHandlers[7][3]; +static void* sBatchHandlers[7][3]; +static ImageLoader* sLastImageByAddressCache; +static EnvironmentVariables sEnv; +#if __MAC_OS_X_VERSION_MIN_REQUIRED +static const char* sFrameworkFallbackPaths[] = { "$HOME/Library/Frameworks", "/Library/Frameworks", "/Network/Library/Frameworks", "/System/Library/Frameworks", NULL }; +static const char* sLibraryFallbackPaths[] = { "$HOME/lib", "/usr/local/lib", "/usr/lib", NULL }; +#else +static const char* sFrameworkFallbackPaths[] = { "/System/Library/Frameworks", NULL }; +static const char* sLibraryFallbackPaths[] = { "/usr/local/lib", "/usr/lib", NULL }; +#endif +static const char* sRestrictedFrameworkFallbackPaths[] = { "/System/Library/Frameworks", NULL }; +static const char* sRestrictedLibraryFallbackPaths[] = { "/usr/lib", NULL }; +static UndefinedHandler sUndefinedHandler = NULL; +static ImageLoader* sBundleBeingLoaded = NULL; // hack until OFI is reworked +#if DYLD_SHARED_CACHE_SUPPORT +static const dyld_cache_header* sSharedCache = NULL; +static long sSharedCacheSlide = 0; +static bool sSharedCacheIgnoreInodeAndTimeStamp = false; + bool gSharedCacheOverridden = false; +#if __IPHONE_OS_VERSION_MIN_REQUIRED + static const char* sSharedCacheDir = IPHONE_DYLD_SHARED_CACHE_DIR; + #define ENABLE_DYLIBS_TO_OVERRIDE_CACHE_SIZE 1024 +#else + static const char* sSharedCacheDir = MACOSX_DYLD_SHARED_CACHE_DIR; +#endif +#endif +ImageLoader::LinkContext gLinkContext; +bool gLogAPIs = false; +#if SUPPORT_ACCELERATE_TABLES +bool gLogAppAPIs = false; +#endif +const struct LibSystemHelpers* gLibSystemHelpers = NULL; +#if SUPPORT_OLD_CRT_INITIALIZATION +bool gRunInitializersOldWay = false; +#endif +static std::vector sDylibOverrides; +#if !TARGET_IPHONE_SIMULATOR +static int sLogSocket = -1; +#endif +static bool sFrameworksFoundAsDylibs = false; +#if __x86_64__ && DYLD_SHARED_CACHE_SUPPORT +static bool sHaswell = false; +#endif +static std::vector sDynamicReferences; +static OSSpinLock sDynamicReferencesLock = 0; +#if !TARGET_IPHONE_SIMULATOR +static bool sLogToFile = false; +#endif +static char sLoadingCrashMessage[1024] = "dyld: launch, loading dependent libraries"; + +static _dyld_objc_notify_mapped sNotifyObjCMapped; +static _dyld_objc_notify_init sNotifyObjCInit; +static _dyld_objc_notify_unmapped sNotifyObjCUnmapped; + +#if __IPHONE_OS_VERSION_MIN_REQUIRED && !TARGET_IPHONE_SIMULATOR +static bool sForceStderr = false; +#endif + + + +#if SUPPORT_ACCELERATE_TABLES +static ImageLoaderMegaDylib* sAllCacheImagesProxy = NULL; +static bool sDisableAcceleratorTables = false; +#endif + +// +// The MappedRanges structure is used for fast address->image lookups. +// The table is only updated when the dyld lock is held, so we don't +// need to worry about multiple writers. But readers may look at this +// data without holding the lock. Therefore, all updates must be done +// in an order that will never cause readers to see inconsistent data. +// The general rule is that if the image field is non-NULL then +// the other fields are valid. +// +struct MappedRanges +{ + MappedRanges* next; + unsigned long count; + struct { + ImageLoader* image; + uintptr_t start; + uintptr_t end; + } array[1]; +}; + +static MappedRanges* sMappedRangesStart; + +void addMappedRange(ImageLoader* image, uintptr_t start, uintptr_t end) +{ + //dyld::log("addMappedRange(0x%lX->0x%lX) for %s\n", start, end, image->getShortName()); + for (MappedRanges* p = sMappedRangesStart; p != NULL; p = p->next) { + for (unsigned long i=0; i < p->count; ++i) { + if ( p->array[i].image == NULL ) { + p->array[i].start = start; + p->array[i].end = end; + // add image field last with a barrier so that any reader will see consistent records + OSMemoryBarrier(); + p->array[i].image = image; + return; + } + } + } + // table must be full, chain another +#if SUPPORT_ACCELERATE_TABLES + unsigned count = (sAllCacheImagesProxy != NULL) ? 16 : 400; +#else + unsigned count = 400; +#endif + size_t allocationSize = sizeof(MappedRanges) + (count-1)*3*sizeof(void*); + MappedRanges* newRanges = (MappedRanges*)malloc(allocationSize); + bzero(newRanges, allocationSize); + newRanges->count = count; + newRanges->array[0].start = start; + newRanges->array[0].end = end; + newRanges->array[0].image = image; + OSMemoryBarrier(); + if ( sMappedRangesStart == NULL ) { + sMappedRangesStart = newRanges; + } + else { + for (MappedRanges* p = sMappedRangesStart; p != NULL; p = p->next) { + if ( p->next == NULL ) { + OSMemoryBarrier(); + p->next = newRanges; + break; + } + } + } +} + +void removedMappedRanges(ImageLoader* image) +{ + for (MappedRanges* p = sMappedRangesStart; p != NULL; p = p->next) { + for (unsigned long i=0; i < p->count; ++i) { + if ( p->array[i].image == image ) { + // clear with a barrier so that any reader will see consistent records + OSMemoryBarrier(); + p->array[i].image = NULL; + } + } + } +} + +ImageLoader* findMappedRange(uintptr_t target) +{ + for (MappedRanges* p = sMappedRangesStart; p != NULL; p = p->next) { + for (unsigned long i=0; i < p->count; ++i) { + if ( p->array[i].image != NULL ) { + if ( (p->array[i].start <= target) && (target < p->array[i].end) ) + return p->array[i].image; + } + } + } + return NULL; +} + + + +const char* mkstringf(const char* format, ...) +{ + _SIMPLE_STRING buf = _simple_salloc(); + if ( buf != NULL ) { + va_list list; + va_start(list, format); + _simple_vsprintf(buf, format, list); + va_end(list); + const char* t = strdup(_simple_string(buf)); + _simple_sfree(buf); + if ( t != NULL ) + return t; + } + return "mkstringf, out of memory error"; +} + + +void throwf(const char* format, ...) +{ + _SIMPLE_STRING buf = _simple_salloc(); + if ( buf != NULL ) { + va_list list; + va_start(list, format); + _simple_vsprintf(buf, format, list); + va_end(list); + const char* t = strdup(_simple_string(buf)); + _simple_sfree(buf); + if ( t != NULL ) + throw t; + } + throw "throwf, out of memory error"; +} + + +#if !TARGET_IPHONE_SIMULATOR +static int sLogfile = STDERR_FILENO; +#endif + +#if !TARGET_IPHONE_SIMULATOR +// based on CFUtilities.c: also_do_stderr() +static bool useSyslog() +{ + // Use syslog() for processes managed by launchd + static bool launchdChecked = false; + static bool launchdOwned = false; + if ( !launchdChecked && gProcessInfo->libSystemInitialized ) { + if ( (gLibSystemHelpers != NULL) && (gLibSystemHelpers->version >= 11) ) { + // only call isLaunchdOwned() after libSystem is initialized + launchdOwned = (*gLibSystemHelpers->isLaunchdOwned)(); + launchdChecked = true; + } + } + if ( launchdChecked && launchdOwned ) + return true; + + // If stderr is not available, use syslog() + struct stat sb; + int result = fstat(STDERR_FILENO, &sb); + if ( result < 0 ) + return true; // file descriptor 2 is closed + + return false; +} + + +static void socket_syslogv(int priority, const char* format, va_list list) +{ + // lazily create socket and connection to syslogd + if ( sLogSocket == -1 ) { + sLogSocket = ::socket(AF_UNIX, SOCK_DGRAM, 0); + if (sLogSocket == -1) + return; // cannot log + ::fcntl(sLogSocket, F_SETFD, 1); + + struct sockaddr_un addr; + addr.sun_family = AF_UNIX; + strncpy(addr.sun_path, _PATH_LOG, sizeof(addr.sun_path)); + if ( ::connect(sLogSocket, (struct sockaddr *)&addr, sizeof(addr)) == -1 ) { + ::close(sLogSocket); + sLogSocket = -1; + return; + } + } + + // format message to syslogd like: "Process[pid]: message" + _SIMPLE_STRING buf = _simple_salloc(); + if ( buf == NULL ) + return; + if ( _simple_sprintf(buf, "<%d>%s[%d]: ", LOG_USER|LOG_NOTICE, sExecShortName, getpid()) == 0 ) { + if ( _simple_vsprintf(buf, format, list) == 0 ) { + const char* p = _simple_string(buf); + ::__sendto(sLogSocket, p, strlen(p), 0, NULL, 0); + } + } + _simple_sfree(buf); +} + + + +void vlog(const char* format, va_list list) +{ +#if __IPHONE_OS_VERSION_MIN_REQUIRED && !TARGET_IPHONE_SIMULATOR + // log to console when running iOS app from Xcode + if ( !sLogToFile && !sForceStderr && useSyslog() ) +#else + if ( !sLogToFile && useSyslog() ) +#endif + socket_syslogv(LOG_ERR, format, list); + else { + _simple_vdprintf(sLogfile, format, list); + } +} + +void log(const char* format, ...) +{ + va_list list; + va_start(list, format); + vlog(format, list); + va_end(list); +} + + +void vwarn(const char* format, va_list list) +{ + _simple_dprintf(sLogfile, "dyld: warning, "); + _simple_vdprintf(sLogfile, format, list); +} + +void warn(const char* format, ...) +{ + va_list list; + va_start(list, format); + vwarn(format, list); + va_end(list); +} + + +#endif // !TARGET_IPHONE_SIMULATOR + + +// control access to sAllImages through a lock +// because global dyld lock is not held during initialization phase of dlopen() +// Use OSSpinLockLock to allow yielding +static OSSpinLock sAllImagesLock = 0; + +static void allImagesLock() +{ + OSSpinLockLock(&sAllImagesLock); +} + +static void allImagesUnlock() +{ + OSSpinLockUnlock(&sAllImagesLock); +} + + +// utility class to assure files are closed when an exception is thrown +class FileOpener { +public: + FileOpener(const char* path); + ~FileOpener(); + int getFileDescriptor() { return fd; } +private: + int fd; +}; + +FileOpener::FileOpener(const char* path) + : fd(-1) +{ + fd = my_open(path, O_RDONLY, 0); +} + +FileOpener::~FileOpener() +{ + if ( fd != -1 ) + close(fd); +} + + +static void registerDOFs(const std::vector& dofs) +{ + const size_t dofSectionCount = dofs.size(); + if ( !sEnv.DYLD_DISABLE_DOFS && (dofSectionCount != 0) ) { + int fd = open("/dev/" DTRACEMNR_HELPER, O_RDWR); + if ( fd < 0 ) { + //dyld::warn("can't open /dev/" DTRACEMNR_HELPER " to register dtrace DOF sections\n"); + } + else { + // allocate a buffer on the stack for the variable length dof_ioctl_data_t type + uint8_t buffer[sizeof(dof_ioctl_data_t) + dofSectionCount*sizeof(dof_helper_t)]; + dof_ioctl_data_t* ioctlData = (dof_ioctl_data_t*)buffer; + + // fill in buffer with one dof_helper_t per DOF section + ioctlData->dofiod_count = dofSectionCount; + for (unsigned int i=0; i < dofSectionCount; ++i) { + strlcpy(ioctlData->dofiod_helpers[i].dofhp_mod, dofs[i].imageShortName, DTRACE_MODNAMELEN); + ioctlData->dofiod_helpers[i].dofhp_dof = (uintptr_t)(dofs[i].dof); + ioctlData->dofiod_helpers[i].dofhp_addr = (uintptr_t)(dofs[i].dof); + } + + // tell kernel about all DOF sections en mas + // pass pointer to ioctlData because ioctl() only copies a fixed size amount of data into kernel + user_addr_t val = (user_addr_t)(unsigned long)ioctlData; + if ( ioctl(fd, DTRACEHIOC_ADDDOF, &val) != -1 ) { + // kernel returns a unique identifier for each section in the dofiod_helpers[].dofhp_dof field. + for (unsigned int i=0; i < dofSectionCount; ++i) { + RegisteredDOF info; + info.mh = dofs[i].imageHeader; + info.registrationID = (int)(ioctlData->dofiod_helpers[i].dofhp_dof); + sImageFilesNeedingDOFUnregistration.push_back(info); + if ( gLinkContext.verboseDOF ) { + dyld::log("dyld: registering DOF section %p in %s with dtrace, ID=0x%08X\n", + dofs[i].dof, dofs[i].imageShortName, info.registrationID); + } + } + } + else { + //dyld::log( "dyld: ioctl to register dtrace DOF section failed\n"); + } + close(fd); + } + } +} + +static void unregisterDOF(int registrationID) +{ + int fd = open("/dev/" DTRACEMNR_HELPER, O_RDWR); + if ( fd < 0 ) { + dyld::warn("can't open /dev/" DTRACEMNR_HELPER " to unregister dtrace DOF section\n"); + } + else { + ioctl(fd, DTRACEHIOC_REMOVE, registrationID); + close(fd); + if ( gLinkContext.verboseInit ) + dyld::warn("unregistering DOF section ID=0x%08X with dtrace\n", registrationID); + } +} + + +// +// _dyld_register_func_for_add_image() is implemented as part of the general image state change notification +// +static void notifyAddImageCallbacks(ImageLoader* image) +{ + // use guard so that we cannot notify about the same image twice + if ( ! image->addFuncNotified() ) { + for (std::vector::iterator it=sAddImageCallbacks.begin(); it != sAddImageCallbacks.end(); it++) + (*it)(image->machHeader(), image->getSlide()); + image->setAddFuncNotified(); + } +} + + + +// notify gdb about these new images +static const char* updateAllImages(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info info[]) +{ + // don't add images without paths to all-image-info-list + if ( info[0].imageFilePath != NULL ) + addImagesToAllImages(infoCount, info); + return NULL; +} + + +static StateHandlers* stateToHandlers(dyld_image_states state, void* handlersArray[7][3]) +{ + switch ( state ) { + case dyld_image_state_mapped: + return reinterpret_cast(&handlersArray[0]); + + case dyld_image_state_dependents_mapped: + return reinterpret_cast(&handlersArray[1]); + + case dyld_image_state_rebased: + return reinterpret_cast(&handlersArray[2]); + + case dyld_image_state_bound: + return reinterpret_cast(&handlersArray[3]); + + case dyld_image_state_dependents_initialized: + return reinterpret_cast(&handlersArray[4]); + + case dyld_image_state_initialized: + return reinterpret_cast(&handlersArray[5]); + + case dyld_image_state_terminated: + return reinterpret_cast(&handlersArray[6]); + } + return NULL; +} + +#if SUPPORT_ACCELERATE_TABLES +static dyld_image_state_change_handler getPreInitNotifyHandler(unsigned index) +{ + std::vector* handlers = stateToHandlers(dyld_image_state_dependents_initialized, sSingleHandlers); + if ( index >= handlers->size() ) + return NULL; + return (*handlers)[index]; +} + +static dyld_image_state_change_handler getBoundBatchHandler(unsigned index) +{ + std::vector* handlers = stateToHandlers(dyld_image_state_bound, sBatchHandlers); + if ( index >= handlers->size() ) + return NULL; + return (*handlers)[index]; +} + +static void notifySingleFromCache(dyld_image_states state, const mach_header* mh, const char* path) +{ + //dyld::log("notifySingle(state=%d, image=%s)\n", state, image->getPath()); + std::vector* handlers = stateToHandlers(state, sSingleHandlers); + if ( handlers != NULL ) { + dyld_image_info info; + info.imageLoadAddress = mh; + info.imageFilePath = path; + info.imageFileModDate = 0; + for (dyld_image_state_change_handler handler : *handlers) { + const char* result = (*handler)(state, 1, &info); + if ( (result != NULL) && (state == dyld_image_state_mapped) ) { + //fprintf(stderr, " image rejected by handler=%p\n", *it); + // make copy of thrown string so that later catch clauses can free it + const char* str = strdup(result); + throw str; + } + } + } + if ( (state == dyld_image_state_dependents_initialized) && (sNotifyObjCInit != NULL) && (mh->flags & MH_HAS_OBJC) ) { + (*sNotifyObjCInit)(path, mh); + } +} +#endif + +static mach_port_t sNotifyReplyPorts[DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT]; + + +static void notifyMonitoringDyld(bool unloading, unsigned portSlot, unsigned imageCount, const dyld_image_info infos[]) +{ + unsigned entriesSize = imageCount*sizeof(dyld_process_info_image_entry); + unsigned pathsSize = 0; + for (unsigned j=0; j < imageCount; ++j) { + pathsSize += (strlen(infos[j].imageFilePath) + 1); + } + unsigned totalSize = (sizeof(dyld_process_info_notify_header) + entriesSize + pathsSize + 127) & -128; // align + if ( totalSize > DYLD_PROCESS_INFO_NOTIFY_MAX_BUFFER_SIZE ) { + // Putting all image paths into one message would make buffer too big. + // Instead split into two messages. Recurse as needed until paths fit in buffer. + unsigned imageHalfCount = imageCount/2; + notifyMonitoringDyld(unloading, portSlot, imageHalfCount, infos); + notifyMonitoringDyld(unloading, portSlot, imageCount - imageHalfCount, &infos[imageHalfCount]); + return; + } + uint8_t buffer[totalSize]; + dyld_process_info_notify_header* header = (dyld_process_info_notify_header*)buffer; + header->version = 1; + header->imageCount = imageCount; + header->imagesOffset = sizeof(dyld_process_info_notify_header); + header->stringsOffset = sizeof(dyld_process_info_notify_header) + entriesSize; + header->timestamp = mach_absolute_time(); + dyld_process_info_image_entry* entries = (dyld_process_info_image_entry*)&buffer[header->imagesOffset]; + char* const pathPoolStart = (char*)&buffer[header->stringsOffset]; + char* pathPool = pathPoolStart; + for (unsigned j=0; j < imageCount; ++j) { + strcpy(pathPool, infos[j].imageFilePath); + uint32_t len = (uint32_t)strlen(pathPool); + bzero(entries->uuid, 16); + const ImageLoader* image = findImageByMachHeader(infos[j].imageLoadAddress); + if ( image != NULL ) { + image->getUUID(entries->uuid); + } +#if SUPPORT_ACCELERATE_TABLES + else if ( sAllCacheImagesProxy != NULL ) { + const mach_header* mh; + const char* path; + unsigned index; + if ( sAllCacheImagesProxy->addressInCache(infos[j].imageLoadAddress, &mh, &path, &index) ) { + sAllCacheImagesProxy->getDylibUUID(index, entries->uuid); + } + } +#endif + entries->loadAddress = (uint64_t)infos[j].imageLoadAddress; + entries->pathStringOffset = (uint32_t)(pathPool - pathPoolStart); + entries->pathLength = len; + pathPool += (len +1); + ++entries; + } + + if ( sNotifyReplyPorts[portSlot] == 0 ) { + if ( !mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &sNotifyReplyPorts[portSlot]) ) + mach_port_insert_right(mach_task_self(), sNotifyReplyPorts[portSlot], sNotifyReplyPorts[portSlot], MACH_MSG_TYPE_MAKE_SEND); + //dyld::log("allocated reply port %d\n", sNotifyReplyPorts[portSlot]); + } + //dyld::log("found port to send to\n"); + mach_msg_header_t* h = (mach_msg_header_t*)buffer; + h->msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND,MACH_MSG_TYPE_MAKE_SEND); // MACH_MSG_TYPE_MAKE_SEND_ONCE + h->msgh_id = unloading ? DYLD_PROCESS_INFO_NOTIFY_UNLOAD_ID : DYLD_PROCESS_INFO_NOTIFY_LOAD_ID; + h->msgh_local_port = sNotifyReplyPorts[portSlot]; + h->msgh_remote_port = dyld::gProcessInfo->notifyPorts[portSlot]; + h->msgh_reserved = 0; + h->msgh_size = (mach_msg_size_t)sizeof(buffer); + //dyld::log("sending to port[%d]=%d, size=%d, reply port=%d, id=0x%X\n", portSlot, dyld::gProcessInfo->notifyPorts[portSlot], h->msgh_size, sNotifyReplyPorts[portSlot], h->msgh_id); + kern_return_t sendResult = mach_msg(h, MACH_SEND_MSG | MACH_RCV_MSG | MACH_SEND_TIMEOUT, h->msgh_size, h->msgh_size, sNotifyReplyPorts[portSlot], 100, MACH_PORT_NULL); + //dyld::log("send result = 0x%X, msg_id=%d, msg_size=%d\n", sendResult, h->msgh_id, h->msgh_size); + if ( sendResult == MACH_SEND_INVALID_DEST ) { + // sender is not responding, detatch + //dyld::log("process requesting notification gone. deallocation send port %d and receive port %d\n", dyld::gProcessInfo->notifyPorts[portSlot], sNotifyReplyPorts[portSlot]); + mach_port_deallocate(mach_task_self(), dyld::gProcessInfo->notifyPorts[portSlot]); + mach_port_deallocate(mach_task_self(), sNotifyReplyPorts[portSlot]); + dyld::gProcessInfo->notifyPorts[portSlot] = 0; + sNotifyReplyPorts[portSlot] = 0; + } +} + +#define MAX_KERNEL_IMAGES_PER_CALL (100) + +static void flushKernelNotifications(bool loading, bool force, std::array& kernelInfos, uint32_t &kernelInfoCount) { +#if 0 + if ((force && kernelInfoCount != 0) || kernelInfoCount == MAX_KERNEL_IMAGES_PER_CALL) { + if (loading) { + task_register_dyld_image_infos(mach_task_self(), kernelInfos.data(), kernelInfoCount); + } else { + task_unregister_dyld_image_infos(mach_task_self(), kernelInfos.data(), kernelInfoCount); + } + kernelInfoCount = 0; + } +#endif +} + +static +void queueKernelNotification(const ImageLoader& image, bool loading, std::array& kernelInfos, uint32_t &kernelInfoCount) { + if ( !image.inSharedCache() ) { + ino_t inode = image.getInode(); + image.getUUID(kernelInfos[kernelInfoCount].uuid); + memcpy(&kernelInfos[kernelInfoCount].fsobjid, &inode, 8); + kernelInfos[kernelInfoCount].load_addr = (uint64_t)image.machHeader(); + // FIXME we should also be grabbing the device ID, but that is not necessary yet, + // and requires threading it through the ImageLoader + kernelInfos[kernelInfoCount].fsid.val[0] = 0; + kernelInfos[kernelInfoCount].fsid.val[1] = 0; + kernelInfoCount++; + } + flushKernelNotifications(loading, false, kernelInfos, kernelInfoCount); +} + +void notifyKernel(const ImageLoader& image, bool loading) { + std::array kernelInfos; + uint32_t kernelInfoCount = 0; + queueKernelNotification(image, loading, kernelInfos, kernelInfoCount); + flushKernelNotifications(loading, true, kernelInfos, kernelInfoCount); +} + +static void notifySingle(dyld_image_states state, const ImageLoader* image, ImageLoader::InitializerTimingList* timingInfo) +{ + //dyld::log("notifySingle(state=%d, image=%s)\n", state, image->getPath()); + std::vector* handlers = stateToHandlers(state, sSingleHandlers); + if ( handlers != NULL ) { + dyld_image_info info; + info.imageLoadAddress = image->machHeader(); + info.imageFilePath = image->getRealPath(); + info.imageFileModDate = image->lastModified(); + for (std::vector::iterator it = handlers->begin(); it != handlers->end(); ++it) { + const char* result = (*it)(state, 1, &info); + if ( (result != NULL) && (state == dyld_image_state_mapped) ) { + //fprintf(stderr, " image rejected by handler=%p\n", *it); + // make copy of thrown string so that later catch clauses can free it + const char* str = strdup(result); + throw str; + } + } + } + if ( state == dyld_image_state_mapped ) { + // Save load addr + UUID for images from outside the shared cache + if ( !image->inSharedCache() ) { + dyld_uuid_info info; + if ( image->getUUID(info.imageUUID) ) { + info.imageLoadAddress = image->machHeader(); + addNonSharedCacheImageUUID(info); + } + } + } + if ( (state == dyld_image_state_dependents_initialized) && (sNotifyObjCInit != NULL) && image->notifyObjC() ) { + uint64_t t0 = mach_absolute_time(); + (*sNotifyObjCInit)(image->getRealPath(), image->machHeader()); + uint64_t t1 = mach_absolute_time(); + uint64_t t2 = mach_absolute_time(); + uint64_t timeInObjC = t1-t0; + uint64_t emptyTime = (t2-t1)*100; + if ( (timeInObjC > emptyTime) && (timingInfo != NULL) ) { + timingInfo->addTime(image->getShortName(), timeInObjC); + } + } +#if 0 + // mach message csdlc about dynamically unloaded images + if ( image->addFuncNotified() && (state == dyld_image_state_terminated) ) { + notifyKernel(*image, false); + + uint64_t loadTimestamp = mach_absolute_time(); + if ( sEnv.DYLD_PRINT_CS_NOTIFICATIONS ) { + dyld::log("dyld: coresymbolication_unload_notifier(%p, 0x%016llX, %p, %s)\n", + dyld::gProcessInfo->coreSymbolicationShmPage, loadTimestamp, image->machHeader(), image->getPath()); + } + if ( dyld::gProcessInfo->coreSymbolicationShmPage != NULL) { + coresymbolication_unload_notifier(dyld::gProcessInfo->coreSymbolicationShmPage, loadTimestamp, image->getPath(), image->machHeader()); + } + for (int slot=0; slot < DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT; ++slot) { + if ( dyld::gProcessInfo->notifyPorts[slot] != 0 ) { + dyld_image_info info; + info.imageLoadAddress = image->machHeader(); + info.imageFilePath = image->getPath(); + info.imageFileModDate = 0; + notifyMonitoringDyld(true, slot, 1, &info); + } + else if ( sNotifyReplyPorts[slot] != 0 ) { + // monitoring process detached from this process, so release reply port + //dyld::log("deallocated reply port %d\n", sNotifyReplyPorts[slot]); + mach_port_deallocate(mach_task_self(), sNotifyReplyPorts[slot]); + sNotifyReplyPorts[slot] = 0; + } + } + } +#endif +} + + +// +// Normally, dyld_all_image_infos is only updated in batches after an entire +// graph is loaded. But if there is an error loading the initial set of +// dylibs needed by the main executable, dyld_all_image_infos is not yet set +// up, leading to usually brief crash logs. +// +// This function manually adds the images loaded so far to dyld::gProcessInfo. +// It should only be called before terminating. +// +void syncAllImages() +{ + for (std::vector::iterator it=sAllImages.begin(); it != sAllImages.end(); ++it) { + dyld_image_info info; + ImageLoader* image = *it; + info.imageLoadAddress = image->machHeader(); + info.imageFilePath = image->getRealPath(); + info.imageFileModDate = image->lastModified(); + // add to all_image_infos if not already there + bool found = false; + int existingCount = dyld::gProcessInfo->infoArrayCount; + const dyld_image_info* existing = dyld::gProcessInfo->infoArray; + if ( existing != NULL ) { + for (int i=0; i < existingCount; ++i) { + if ( existing[i].imageLoadAddress == info.imageLoadAddress ) { + //dyld::log("not adding %s\n", info.imageFilePath); + found = true; + break; + } + } + } + if ( ! found ) { + //dyld::log("adding %s\n", info.imageFilePath); + addImagesToAllImages(1, &info); + } + } +} + + +static int imageSorter(const void* l, const void* r) +{ + const ImageLoader* left = *((ImageLoader**)l); + const ImageLoader* right= *((ImageLoader**)r); + return left->compare(right); +} + +static void notifyBatchPartial(dyld_image_states state, bool orLater, dyld_image_state_change_handler onlyHandler, bool preflightOnly, bool onlyObjCMappedNotification) +{ + std::vector* handlers = stateToHandlers(state, sBatchHandlers); + std::array kernelInfos; + uint32_t kernelInfoCount = 0; + + if ( (handlers != NULL) || ((state == dyld_image_state_bound) && (sNotifyObjCMapped != NULL)) ) { + // don't use a vector because it will use malloc/free and we want notifcation to be low cost + allImagesLock(); + dyld_image_info infos[allImagesCount()+1]; + ImageLoader* images[allImagesCount()+1]; + ImageLoader** end = images; + for (std::vector::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) { + dyld_image_states imageState = (*it)->getState(); + if ( (imageState == state) || (orLater && (imageState > state)) ) + *end++ = *it; + } + if ( sBundleBeingLoaded != NULL ) { + dyld_image_states imageState = sBundleBeingLoaded->getState(); + if ( (imageState == state) || (orLater && (imageState > state)) ) + *end++ = sBundleBeingLoaded; + } + const char* dontLoadReason = NULL; + uint32_t imageCount = (uint32_t)(end-images); + if ( imageCount != 0 ) { + // sort bottom up + qsort(images, imageCount, sizeof(ImageLoader*), &imageSorter); + // build info array + for (unsigned int i=0; i < imageCount; ++i) { + dyld_image_info* p = &infos[i]; + ImageLoader* image = images[i]; + //dyld::log(" state=%d, name=%s\n", state, image->getPath()); + p->imageLoadAddress = image->machHeader(); + p->imageFilePath = image->getRealPath(); + p->imageFileModDate = image->lastModified(); + // get these registered with the kernel as early as possible + if ( state == dyld_image_state_dependents_mapped) + queueKernelNotification(*image, true, kernelInfos, kernelInfoCount); + // special case for add_image hook + if ( state == dyld_image_state_bound ) + notifyAddImageCallbacks(image); + } + flushKernelNotifications(true, true, kernelInfos, kernelInfoCount); + } +#if SUPPORT_ACCELERATE_TABLES + if ( sAllCacheImagesProxy != NULL ) { + unsigned cacheCount = sAllCacheImagesProxy->appendImagesToNotify(state, orLater, &infos[imageCount]); + // support _dyld_register_func_for_add_image() + if ( state == dyld_image_state_bound ) { + for (ImageCallback callback : sAddImageCallbacks) { + for (unsigned i=0; i < cacheCount; ++i) + (*callback)(infos[imageCount+i].imageLoadAddress, sSharedCacheSlide); + } + } + imageCount += cacheCount; + } +#endif + if ( imageCount != 0 ) { + if ( !onlyObjCMappedNotification ) { + if ( onlyHandler != NULL ) { + const char* result = NULL; + if ( result == NULL ) { + result = (*onlyHandler)(state, imageCount, infos); + } + if ( (result != NULL) && (state == dyld_image_state_dependents_mapped) ) { + //fprintf(stderr, " images rejected by handler=%p\n", onlyHandler); + // make copy of thrown string so that later catch clauses can free it + dontLoadReason = strdup(result); + } + } + else { + // call each handler with whole array + if ( handlers != NULL ) { + for (std::vector::iterator it = handlers->begin(); it != handlers->end(); ++it) { + const char* result = (*it)(state, imageCount, infos); + if ( (result != NULL) && (state == dyld_image_state_dependents_mapped) ) { + //fprintf(stderr, " images rejected by handler=%p\n", *it); + // make copy of thrown string so that later catch clauses can free it + dontLoadReason = strdup(result); + break; + } + } + } + } + } + // tell objc about new images + if ( (onlyHandler == NULL) && ((state == dyld_image_state_bound) || (orLater && (dyld_image_state_bound > state))) && (sNotifyObjCMapped != NULL) ) { + const char* paths[imageCount]; + const mach_header* mhs[imageCount]; + unsigned objcImageCount = 0; + for (int i=0; i < imageCount; ++i) { + const ImageLoader* image = findImageByMachHeader(infos[i].imageLoadAddress); + bool hasObjC = false; + if ( image != NULL ) { + hasObjC = image->notifyObjC(); + } +#if SUPPORT_ACCELERATE_TABLES + else if ( sAllCacheImagesProxy != NULL ) { + const mach_header* mh; + const char* path; + unsigned index; + if ( sAllCacheImagesProxy->addressInCache(infos[i].imageLoadAddress, &mh, &path, &index) ) { + hasObjC = (mh->flags & MH_HAS_OBJC); + } + } +#endif + if ( hasObjC ) { + paths[objcImageCount] = infos[i].imageFilePath; + mhs[objcImageCount] = infos[i].imageLoadAddress; + ++objcImageCount; + } + } + if ( objcImageCount != 0 ) { + uint64_t t0 = mach_absolute_time(); + (*sNotifyObjCMapped)(objcImageCount, paths, mhs); + uint64_t t1 = mach_absolute_time(); + ImageLoader::fgTotalObjCSetupTime += (t1-t0); + } + } + } + allImagesUnlock(); + if ( dontLoadReason != NULL ) + throw dontLoadReason; + if ( !preflightOnly && (state == dyld_image_state_dependents_mapped) ) { + if ( (dyld::gProcessInfo->coreSymbolicationShmPage != NULL) || sEnv.DYLD_PRINT_CS_NOTIFICATIONS ) { + // mach message csdlc about loaded images + uint64_t loadTimestamp = mach_absolute_time(); + for (unsigned j=0; j < imageCount; ++j) { + if ( sEnv.DYLD_PRINT_CS_NOTIFICATIONS ) { + dyld::log("dyld: coresymbolication_load_notifier(%p, 0x%016llX, %p, %s)\n", + dyld::gProcessInfo->coreSymbolicationShmPage, loadTimestamp, infos[j].imageLoadAddress, infos[j].imageFilePath); + } +#if 0 + coresymbolication_load_notifier(dyld::gProcessInfo->coreSymbolicationShmPage, loadTimestamp, infos[j].imageFilePath, infos[j].imageLoadAddress); +#endif + } + } + for (int slot=0; slot < DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT; ++slot) { + if ( dyld::gProcessInfo->notifyPorts[slot] ) + notifyMonitoringDyld(false, slot, imageCount, infos); + } + } + } +} + + + +static void notifyBatch(dyld_image_states state, bool preflightOnly) +{ + notifyBatchPartial(state, false, NULL, preflightOnly, false); +} + +// In order for register_func_for_add_image() callbacks to to be called bottom up, +// we need to maintain a list of root images. The main executable is usally the +// first root. Any images dynamically added are also roots (unless already loaded). +// If DYLD_INSERT_LIBRARIES is used, those libraries are first. +static void addRootImage(ImageLoader* image) +{ + //dyld::log("addRootImage(%p, %s)\n", image, image->getPath()); + // add to list of roots + sImageRoots.push_back(image); +} + + +static void clearAllDepths() +{ + for (std::vector::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) + (*it)->clearDepth(); +} + +static void printAllDepths() +{ + for (std::vector::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) + dyld::log("%03d %s\n", (*it)->getDepth(), (*it)->getShortName()); +} + + +static unsigned int imageCount() +{ + allImagesLock(); + unsigned int result = (unsigned int)sAllImages.size(); + allImagesUnlock(); + return (result); +} + + +static void setNewProgramVars(const ProgramVars& newVars) +{ + // make a copy of the pointers to program variables + gLinkContext.programVars = newVars; + + // now set each program global to their initial value + *gLinkContext.programVars.NXArgcPtr = gLinkContext.argc; + *gLinkContext.programVars.NXArgvPtr = gLinkContext.argv; + *gLinkContext.programVars.environPtr = gLinkContext.envp; + *gLinkContext.programVars.__prognamePtr = gLinkContext.progname; +} + +#if SUPPORT_OLD_CRT_INITIALIZATION +static void setRunInitialzersOldWay() +{ + gRunInitializersOldWay = true; +} +#endif + +static bool sandboxBlocked(const char* path, const char* kind) +{ +#if TARGET_IPHONE_SIMULATOR + // sandbox calls not yet supported in simulator runtime + return false; +#else + sandbox_filter_type filter = (sandbox_filter_type)(SANDBOX_FILTER_PATH | SANDBOX_CHECK_NO_REPORT); + return ( sandbox_check(getpid(), kind, filter, path) > 0 ); +#endif +} + +bool sandboxBlockedMmap(const char* path) +{ + return sandboxBlocked(path, "file-map-executable"); +} + +bool sandboxBlockedOpen(const char* path) +{ + return sandboxBlocked(path, "file-read-data"); +} + +bool sandboxBlockedStat(const char* path) +{ + return sandboxBlocked(path, "file-read-metadata"); +} + + +static void addDynamicReference(ImageLoader* from, ImageLoader* to) { + // don't add dynamic reference if target is in the shared cache (since it can't be unloaded) + if ( to->inSharedCache() ) + return; + + // don't add dynamic reference if there already is a static one + if ( from->dependsOn(to) ) + return; + + // don't add if this combination already exists + OSSpinLockLock(&sDynamicReferencesLock); + for (std::vector::iterator it=sDynamicReferences.begin(); it != sDynamicReferences.end(); ++it) { + if ( (it->from == from) && (it->to == to) ) { + OSSpinLockUnlock(&sDynamicReferencesLock); + return; + } + } + + //dyld::log("addDynamicReference(%s, %s\n", from->getShortName(), to->getShortName()); + ImageLoader::DynamicReference t; + t.from = from; + t.to = to; + sDynamicReferences.push_back(t); + OSSpinLockUnlock(&sDynamicReferencesLock); +} + +static void addImage(ImageLoader* image) +{ + // add to master list + allImagesLock(); + sAllImages.push_back(image); + allImagesUnlock(); + + // update mapped ranges + uintptr_t lastSegStart = 0; + uintptr_t lastSegEnd = 0; + for(unsigned int i=0, e=image->segmentCount(); i < e; ++i) { + if ( image->segUnaccessible(i) ) + continue; + uintptr_t start = image->segActualLoadAddress(i); + uintptr_t end = image->segActualEndAddress(i); + if ( start == lastSegEnd ) { + // two segments are contiguous, just record combined segments + lastSegEnd = end; + } + else { + // non-contiguous segments, record last (if any) + if ( lastSegEnd != 0 ) + addMappedRange(image, lastSegStart, lastSegEnd); + lastSegStart = start; + lastSegEnd = end; + } + } + if ( lastSegEnd != 0 ) + addMappedRange(image, lastSegStart, lastSegEnd); + + + if ( gLinkContext.verboseLoading || (sEnv.DYLD_PRINT_LIBRARIES_POST_LAUNCH && (sMainExecutable!=NULL) && sMainExecutable->isLinked()) ) { + dyld::log("dyld: loaded: %s\n", image->getPath()); + } + +} + +// +// Helper for std::remove_if +// +class RefUsesImage { +public: + RefUsesImage(ImageLoader* image) : _image(image) {} + bool operator()(const ImageLoader::DynamicReference& ref) const { + return ( (ref.from == _image) || (ref.to == _image) ); + } +private: + ImageLoader* _image; +}; + + + +void removeImage(ImageLoader* image) +{ + // if has dtrace DOF section, tell dtrace it is going away, then remove from sImageFilesNeedingDOFUnregistration + for (std::vector::iterator it=sImageFilesNeedingDOFUnregistration.begin(); it != sImageFilesNeedingDOFUnregistration.end(); ) { + if ( it->mh == image->machHeader() ) { + unregisterDOF(it->registrationID); + sImageFilesNeedingDOFUnregistration.erase(it); + // don't increment iterator, the erase caused next element to be copied to where this iterator points + } + else { + ++it; + } + } + + // tell all registered remove image handlers about this + // do this before removing image from internal data structures so that the callback can query dyld about the image + if ( image->getState() >= dyld_image_state_bound ) { + sRemoveImageCallbacksInUse = true; // This only runs inside dyld's global lock, so ok to use a global for the in-use flag. + for (std::vector::iterator it=sRemoveImageCallbacks.begin(); it != sRemoveImageCallbacks.end(); it++) { + (*it)(image->machHeader(), image->getSlide()); + } + sRemoveImageCallbacksInUse = false; + + if ( sNotifyObjCUnmapped != NULL && image->notifyObjC() ) + (*sNotifyObjCUnmapped)(image->getRealPath(), image->machHeader()); + } + + // notify + notifySingle(dyld_image_state_terminated, image, NULL); + + // remove from mapped images table + removedMappedRanges(image); + + // remove from master list + allImagesLock(); + for (std::vector::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) { + if ( *it == image ) { + sAllImages.erase(it); + break; + } + } + allImagesUnlock(); + + // remove from sDynamicReferences + OSSpinLockLock(&sDynamicReferencesLock); + sDynamicReferences.erase(std::remove_if(sDynamicReferences.begin(), sDynamicReferences.end(), RefUsesImage(image)), sDynamicReferences.end()); + OSSpinLockUnlock(&sDynamicReferencesLock); + + // flush find-by-address cache (do this after removed from master list, so there is no chance it can come back) + if ( sLastImageByAddressCache == image ) + sLastImageByAddressCache = NULL; + + // if in root list, pull it out + for (std::vector::iterator it=sImageRoots.begin(); it != sImageRoots.end(); it++) { + if ( *it == image ) { + sImageRoots.erase(it); + break; + } + } + + // log if requested + if ( gLinkContext.verboseLoading || (sEnv.DYLD_PRINT_LIBRARIES_POST_LAUNCH && (sMainExecutable!=NULL) && sMainExecutable->isLinked()) ) { + dyld::log("dyld: unloaded: %s\n", image->getPath()); + } + + // tell gdb, new way + removeImageFromAllImages(image->machHeader()); +} + + +void runImageStaticTerminators(ImageLoader* image) +{ + // if in termination list, pull it out and run terminator + bool mightBeMore; + do { + mightBeMore = false; + for (std::vector::iterator it=sImageFilesNeedingTermination.begin(); it != sImageFilesNeedingTermination.end(); it++) { + if ( *it == image ) { + sImageFilesNeedingTermination.erase(it); + if (gLogAPIs) dyld::log("dlclose(), running static terminators for %p %s\n", image, image->getShortName()); + image->doTermination(gLinkContext); + mightBeMore = true; + break; + } + } + } while ( mightBeMore ); +} + +static void terminationRecorder(ImageLoader* image) +{ + sImageFilesNeedingTermination.push_back(image); +} + +const char* getExecutablePath() +{ + return sExecPath; +} + +static void runAllStaticTerminators(void* extra) +{ + try { + const size_t imageCount = sImageFilesNeedingTermination.size(); + for(size_t i=imageCount; i > 0; --i){ + ImageLoader* image = sImageFilesNeedingTermination[i-1]; + image->doTermination(gLinkContext); + } + sImageFilesNeedingTermination.clear(); + notifyBatch(dyld_image_state_terminated, false); + } + catch (const char* msg) { + halt(msg); + } +} + +void initializeMainExecutable() +{ + // record that we've reached this step + gLinkContext.startedInitializingMainExecutable = true; + + // run initialzers for any inserted dylibs + ImageLoader::InitializerTimingList initializerTimes[allImagesCount()]; + initializerTimes[0].count = 0; + const size_t rootCount = sImageRoots.size(); + if ( rootCount > 1 ) { + for(size_t i=1; i < rootCount; ++i) { + sImageRoots[i]->runInitializers(gLinkContext, initializerTimes[0]); + } + } + + // run initializers for main executable and everything it brings up + sMainExecutable->runInitializers(gLinkContext, initializerTimes[0]); + + // register cxa_atexit() handler to run static terminators in all loaded images when this process exits + if ( gLibSystemHelpers != NULL ) + (*gLibSystemHelpers->cxa_atexit)(&runAllStaticTerminators, NULL, NULL); + + // dump info if requested + if ( sEnv.DYLD_PRINT_STATISTICS ) + ImageLoader::printStatistics((unsigned int)allImagesCount(), initializerTimes[0]); + if ( sEnv.DYLD_PRINT_STATISTICS_DETAILS ) + ImageLoaderMachO::printStatisticsDetails((unsigned int)allImagesCount(), initializerTimes[0]); +} + +bool mainExecutablePrebound() +{ + return sMainExecutable->usablePrebinding(gLinkContext); +} + +ImageLoader* mainExecutable() +{ + return sMainExecutable; +} + + + + +#if SUPPORT_VERSIONED_PATHS + +// forward reference +static bool getDylibVersionAndInstallname(const char* dylibPath, uint32_t* version, char* installName); + + +// +// Examines a dylib file and if its current_version is newer than the installed +// dylib at its install_name, then add the dylib file to sDylibOverrides. +// +static void checkDylibOverride(const char* dylibFile) +{ + //dyld::log("checkDylibOverride('%s')\n", dylibFile); + uint32_t altVersion; + char sysInstallName[PATH_MAX]; + if ( getDylibVersionAndInstallname(dylibFile, &altVersion, sysInstallName) && (sysInstallName[0] =='/') ) { + //dyld::log("%s has version 0x%08X and install name %s\n", dylibFile, altVersion, sysInstallName); + uint32_t sysVersion; + if ( getDylibVersionAndInstallname(sysInstallName, &sysVersion, NULL) ) { + //dyld::log("%s has version 0x%08X\n", sysInstallName, sysVersion); + if ( altVersion > sysVersion ) { + //dyld::log("override found: %s -> %s\n", sysInstallName, dylibFile); + // see if there already is an override for this dylib + bool entryExists = false; + for (std::vector::iterator it = sDylibOverrides.begin(); it != sDylibOverrides.end(); ++it) { + if ( strcmp(it->installName, sysInstallName) == 0 ) { + entryExists = true; + uint32_t prevVersion; + if ( getDylibVersionAndInstallname(it->override, &prevVersion, NULL) ) { + if ( altVersion > prevVersion ) { + // found an even newer override + free((void*)(it->override)); + char resolvedPath[PATH_MAX]; + if ( realpath(dylibFile, resolvedPath) != NULL ) + it->override = strdup(resolvedPath); + else + it->override = strdup(dylibFile); + break; + } + } + } + } + if ( ! entryExists ) { + DylibOverride entry; + entry.installName = strdup(sysInstallName); + char resolvedPath[PATH_MAX]; + if ( realpath(dylibFile, resolvedPath) != NULL ) + entry.override = strdup(resolvedPath); + else + entry.override = strdup(dylibFile); + sDylibOverrides.push_back(entry); + //dyld::log("added override: %s -> %s\n", entry.installName, entry.override); + } + } + } + } + +} + +static void checkDylibOverridesInDir(const char* dirPath) +{ + //dyld::log("checkDylibOverridesInDir('%s')\n", dirPath); + char dylibPath[PATH_MAX]; + long dirPathLen = strlcpy(dylibPath, dirPath, PATH_MAX-1); + if ( dirPathLen >= PATH_MAX ) + return; + DIR* dirp = opendir(dirPath); + if ( dirp != NULL) { + dirent entry; + dirent* entp = NULL; + while ( readdir_r(dirp, &entry, &entp) == 0 ) { + if ( entp == NULL ) + break; + if ( entp->d_type != DT_REG ) + continue; + dylibPath[dirPathLen] = '/'; + dylibPath[dirPathLen+1] = '\0'; + if ( strlcat(dylibPath, entp->d_name, PATH_MAX) >= PATH_MAX ) + continue; + checkDylibOverride(dylibPath); + } + closedir(dirp); + } +} + + +static void checkFrameworkOverridesInDir(const char* dirPath) +{ + //dyld::log("checkFrameworkOverridesInDir('%s')\n", dirPath); + char frameworkPath[PATH_MAX]; + long dirPathLen = strlcpy(frameworkPath, dirPath, PATH_MAX-1); + if ( dirPathLen >= PATH_MAX ) + return; + DIR* dirp = opendir(dirPath); + if ( dirp != NULL) { + dirent entry; + dirent* entp = NULL; + while ( readdir_r(dirp, &entry, &entp) == 0 ) { + if ( entp == NULL ) + break; + if ( entp->d_type != DT_DIR ) + continue; + frameworkPath[dirPathLen] = '/'; + frameworkPath[dirPathLen+1] = '\0'; + int dirNameLen = (int)strlen(entp->d_name); + if ( dirNameLen < 11 ) + continue; + if ( strcmp(&entp->d_name[dirNameLen-10], ".framework") != 0 ) + continue; + if ( strlcat(frameworkPath, entp->d_name, PATH_MAX) >= PATH_MAX ) + continue; + if ( strlcat(frameworkPath, "/", PATH_MAX) >= PATH_MAX ) + continue; + if ( strlcat(frameworkPath, entp->d_name, PATH_MAX) >= PATH_MAX ) + continue; + frameworkPath[strlen(frameworkPath)-10] = '\0'; + checkDylibOverride(frameworkPath); + } + closedir(dirp); + } +} +#endif // SUPPORT_VERSIONED_PATHS + + +// +// Turns a colon separated list of strings into a NULL terminated array +// of string pointers. If mainExecutableDir param is not NULL, +// substitutes @loader_path with main executable's dir. +// +static const char** parseColonList(const char* list, const char* mainExecutableDir) +{ + static const char* sEmptyList[] = { NULL }; + + if ( list[0] == '\0' ) + return sEmptyList; + + int colonCount = 0; + for(const char* s=list; *s != '\0'; ++s) { + if (*s == ':') + ++colonCount; + } + + int index = 0; + const char* start = list; + char** result = new char*[colonCount+2]; + for(const char* s=list; *s != '\0'; ++s) { + if (*s == ':') { + size_t len = s-start; + if ( (mainExecutableDir != NULL) && (strncmp(start, "@loader_path/", 13) == 0) ) { +#if __MAC_OS_X_VERSION_MIN_REQUIRED + if ( gLinkContext.processIsRestricted ) { + dyld::log("dyld: warning: @loader_path/ ignored in restricted process\n"); + continue; + } +#endif + size_t mainExecDirLen = strlen(mainExecutableDir); + char* str = new char[mainExecDirLen+len+1]; + strcpy(str, mainExecutableDir); + strlcat(str, &start[13], mainExecDirLen+len+1); + str[mainExecDirLen+len-13] = '\0'; + start = &s[1]; + result[index++] = str; + } + else if ( (mainExecutableDir != NULL) && (strncmp(start, "@executable_path/", 17) == 0) ) { +#if __MAC_OS_X_VERSION_MIN_REQUIRED + if ( gLinkContext.processIsRestricted ) { + dyld::log("dyld: warning: @executable_path/ ignored in restricted process\n"); + continue; + } +#endif + size_t mainExecDirLen = strlen(mainExecutableDir); + char* str = new char[mainExecDirLen+len+1]; + strcpy(str, mainExecutableDir); + strlcat(str, &start[17], mainExecDirLen+len+1); + str[mainExecDirLen+len-17] = '\0'; + start = &s[1]; + result[index++] = str; + } + else { + char* str = new char[len+1]; + strncpy(str, start, len); + str[len] = '\0'; + start = &s[1]; + result[index++] = str; + } + } + } + size_t len = strlen(start); + if ( (mainExecutableDir != NULL) && (strncmp(start, "@loader_path/", 13) == 0) ) { +#if __MAC_OS_X_VERSION_MIN_REQUIRED + if ( gLinkContext.processIsRestricted ) { + dyld::log("dyld: warning: @loader_path/ ignored in restricted process\n"); + } + else +#endif + { + size_t mainExecDirLen = strlen(mainExecutableDir); + char* str = new char[mainExecDirLen+len+1]; + strcpy(str, mainExecutableDir); + strlcat(str, &start[13], mainExecDirLen+len+1); + str[mainExecDirLen+len-13] = '\0'; + result[index++] = str; + } + } + else if ( (mainExecutableDir != NULL) && (strncmp(start, "@executable_path/", 17) == 0) ) { +#if __MAC_OS_X_VERSION_MIN_REQUIRED + if ( gLinkContext.processIsRestricted ) { + dyld::log("dyld: warning: @executable_path/ ignored in restricted process\n"); + } + else +#endif + { + size_t mainExecDirLen = strlen(mainExecutableDir); + char* str = new char[mainExecDirLen+len+1]; + strcpy(str, mainExecutableDir); + strlcat(str, &start[17], mainExecDirLen+len+1); + str[mainExecDirLen+len-17] = '\0'; + result[index++] = str; + } + } + else { + char* str = new char[len+1]; + strcpy(str, start); + result[index++] = str; + } + result[index] = NULL; + + //dyld::log("parseColonList(%s)\n", list); + //for(int i=0; result[i] != NULL; ++i) + // dyld::log(" %s\n", result[i]); + return (const char**)result; +} + +static void appendParsedColonList(const char* list, const char* mainExecutableDir, const char* const ** storage) +{ + const char** newlist = parseColonList(list, mainExecutableDir); + if ( *storage == NULL ) { + // first time, just set + *storage = newlist; + } + else { + // need to append to existing list + const char* const* existing = *storage; + int count = 0; + for(int i=0; existing[i] != NULL; ++i) + ++count; + for(int i=0; newlist[i] != NULL; ++i) + ++count; + const char** combinedList = new const char*[count+2]; + int index = 0; + for(int i=0; existing[i] != NULL; ++i) + combinedList[index++] = existing[i]; + for(int i=0; newlist[i] != NULL; ++i) + combinedList[index++] = newlist[i]; + combinedList[index] = NULL; + // leak old arrays + *storage = combinedList; + } +} + +#if __MAC_OS_X_VERSION_MIN_REQUIRED +static void paths_expand_roots(const char **paths, const char *key, const char *val) +{ +// assert(val != NULL); +// assert(paths != NULL); + if(NULL != key) { + size_t keyLen = strlen(key); + for(int i=0; paths[i] != NULL; ++i) { + if ( strncmp(paths[i], key, keyLen) == 0 ) { + char* newPath = new char[strlen(val) + (strlen(paths[i]) - keyLen) + 1]; + strcpy(newPath, val); + strcat(newPath, &paths[i][keyLen]); + paths[i] = newPath; + } + } + } + return; +} + +static void removePathWithPrefix(const char* paths[], const char* prefix) +{ + size_t prefixLen = strlen(prefix); + int skip = 0; + int i; + for(i = 0; paths[i] != NULL; ++i) { + if ( strncmp(paths[i], prefix, prefixLen) == 0 ) + ++skip; + else + paths[i-skip] = paths[i]; + } + paths[i-skip] = NULL; +} +#endif + + +#if 0 +static void paths_dump(const char **paths) +{ +// assert(paths != NULL); + const char **strs = paths; + while(*strs != NULL) + { + dyld::log("\"%s\"\n", *strs); + strs++; + } + return; +} +#endif + +static void printOptions(const char* argv[]) +{ + uint32_t i = 0; + while ( NULL != argv[i] ) { + dyld::log("opt[%i] = \"%s\"\n", i, argv[i]); + i++; + } +} + +static void printEnvironmentVariables(const char* envp[]) +{ + while ( NULL != *envp ) { + dyld::log("%s\n", *envp); + envp++; + } +} + +void processDyldEnvironmentVariable(const char* key, const char* value, const char* mainExecutableDir) +{ + if ( strcmp(key, "DYLD_FRAMEWORK_PATH") == 0 ) { + appendParsedColonList(value, mainExecutableDir, &sEnv.DYLD_FRAMEWORK_PATH); + } + else if ( strcmp(key, "DYLD_FALLBACK_FRAMEWORK_PATH") == 0 ) { + appendParsedColonList(value, mainExecutableDir, &sEnv.DYLD_FALLBACK_FRAMEWORK_PATH); + } + else if ( strcmp(key, "DYLD_LIBRARY_PATH") == 0 ) { + appendParsedColonList(value, mainExecutableDir, &sEnv.DYLD_LIBRARY_PATH); + } + else if ( strcmp(key, "DYLD_FALLBACK_LIBRARY_PATH") == 0 ) { + appendParsedColonList(value, mainExecutableDir, &sEnv.DYLD_FALLBACK_LIBRARY_PATH); + } +#if SUPPORT_ROOT_PATH + else if ( (strcmp(key, "DYLD_ROOT_PATH") == 0) || (strcmp(key, "DYLD_PATHS_ROOT") == 0) ) { + if ( strcmp(value, "/") != 0 ) { + gLinkContext.rootPaths = parseColonList(value, mainExecutableDir); + for (int i=0; gLinkContext.rootPaths[i] != NULL; ++i) { + if ( gLinkContext.rootPaths[i][0] != '/' ) { + dyld::warn("DYLD_ROOT_PATH not used because it contains a non-absolute path\n"); + gLinkContext.rootPaths = NULL; + break; + } + } + } + } +#endif + else if ( strcmp(key, "DYLD_IMAGE_SUFFIX") == 0 ) { + gLinkContext.imageSuffix = value; + } + else if ( strcmp(key, "DYLD_INSERT_LIBRARIES") == 0 ) { + sEnv.DYLD_INSERT_LIBRARIES = parseColonList(value, NULL); +#if SUPPORT_ACCELERATE_TABLES + sDisableAcceleratorTables = true; +#endif + } + else if ( strcmp(key, "DYLD_PRINT_OPTS") == 0 ) { + sEnv.DYLD_PRINT_OPTS = true; + } + else if ( strcmp(key, "DYLD_PRINT_ENV") == 0 ) { + sEnv.DYLD_PRINT_ENV = true; + } + else if ( strcmp(key, "DYLD_DISABLE_DOFS") == 0 ) { + sEnv.DYLD_DISABLE_DOFS = true; + } + else if ( strcmp(key, "DYLD_DISABLE_PREFETCH") == 0 ) { + gLinkContext.preFetchDisabled = true; + } + else if ( strcmp(key, "DYLD_PRINT_LIBRARIES") == 0 ) { + gLinkContext.verboseLoading = true; + } + else if ( strcmp(key, "DYLD_PRINT_LIBRARIES_POST_LAUNCH") == 0 ) { + sEnv.DYLD_PRINT_LIBRARIES_POST_LAUNCH = true; + } + else if ( strcmp(key, "DYLD_BIND_AT_LAUNCH") == 0 ) { + sEnv.DYLD_BIND_AT_LAUNCH = true; + } + else if ( strcmp(key, "DYLD_FORCE_FLAT_NAMESPACE") == 0 ) { + gLinkContext.bindFlat = true; + } + else if ( strcmp(key, "DYLD_NEW_LOCAL_SHARED_REGIONS") == 0 ) { + // ignore, no longer relevant but some scripts still set it + } + else if ( strcmp(key, "DYLD_NO_FIX_PREBINDING") == 0 ) { + } + else if ( strcmp(key, "DYLD_PREBIND_DEBUG") == 0 ) { + gLinkContext.verbosePrebinding = true; + } + else if ( strcmp(key, "DYLD_PRINT_INITIALIZERS") == 0 ) { + gLinkContext.verboseInit = true; + } + else if ( strcmp(key, "DYLD_PRINT_DOFS") == 0 ) { + gLinkContext.verboseDOF = true; + } + else if ( strcmp(key, "DYLD_PRINT_STATISTICS") == 0 ) { + sEnv.DYLD_PRINT_STATISTICS = true; +#if __IPHONE_OS_VERSION_MIN_REQUIRED && !TARGET_IPHONE_SIMULATOR + // DYLD_PRINT_STATISTICS no longer logs to xcode console for device apps + sForceStderr = true; +#endif + } + else if ( strcmp(key, "DYLD_PRINT_TO_STDERR") == 0 ) { +#if __IPHONE_OS_VERSION_MIN_REQUIRED && !TARGET_IPHONE_SIMULATOR + // DYLD_PRINT_STATISTICS no longer logs to xcode console for device apps + sForceStderr = true; +#endif + } + else if ( strcmp(key, "DYLD_PRINT_STATISTICS_DETAILS") == 0 ) { + sEnv.DYLD_PRINT_STATISTICS_DETAILS = true; + } + else if ( strcmp(key, "DYLD_PRINT_SEGMENTS") == 0 ) { + gLinkContext.verboseMapping = true; + } + else if ( strcmp(key, "DYLD_PRINT_BINDINGS") == 0 ) { + gLinkContext.verboseBind = true; + } + else if ( strcmp(key, "DYLD_PRINT_WEAK_BINDINGS") == 0 ) { + gLinkContext.verboseWeakBind = true; + } + else if ( strcmp(key, "DYLD_PRINT_REBASINGS") == 0 ) { + gLinkContext.verboseRebase = true; + } + else if ( strcmp(key, "DYLD_PRINT_APIS") == 0 ) { + gLogAPIs = true; + } +#if SUPPORT_ACCELERATE_TABLES + else if ( strcmp(key, "DYLD_PRINT_APIS_APP") == 0 ) { + gLogAppAPIs = true; + } +#endif + else if ( strcmp(key, "DYLD_PRINT_WARNINGS") == 0 ) { + gLinkContext.verboseWarnings = true; + } + else if ( strcmp(key, "DYLD_PRINT_RPATHS") == 0 ) { + gLinkContext.verboseRPaths = true; + } + else if ( strcmp(key, "DYLD_PRINT_CS_NOTIFICATIONS") == 0 ) { + sEnv.DYLD_PRINT_CS_NOTIFICATIONS = true; + } + else if ( strcmp(key, "DYLD_PRINT_INTERPOSING") == 0 ) { + gLinkContext.verboseInterposing = true; + } + else if ( strcmp(key, "DYLD_PRINT_CODE_SIGNATURES") == 0 ) { + gLinkContext.verboseCodeSignatures = true; + } + else if ( strcmp(key, "DYLD_SHARED_REGION") == 0 ) { + if ( strcmp(value, "private") == 0 ) { + gLinkContext.sharedRegionMode = ImageLoader::kUsePrivateSharedRegion; + } + else if ( strcmp(value, "avoid") == 0 ) { + gLinkContext.sharedRegionMode = ImageLoader::kDontUseSharedRegion; + } + else if ( strcmp(value, "use") == 0 ) { + gLinkContext.sharedRegionMode = ImageLoader::kUseSharedRegion; + } + else if ( value[0] == '\0' ) { + gLinkContext.sharedRegionMode = ImageLoader::kUseSharedRegion; + } + else { + dyld::warn("unknown option to DYLD_SHARED_REGION. Valid options are: use, private, avoid\n"); + } + } +#if DYLD_SHARED_CACHE_SUPPORT + else if ( strcmp(key, "DYLD_SHARED_CACHE_DIR") == 0 ) { + sSharedCacheDir = value; + } + else if ( strcmp(key, "DYLD_SHARED_CACHE_DONT_VALIDATE") == 0 ) { + sSharedCacheIgnoreInodeAndTimeStamp = true; + } +#endif + else if ( strcmp(key, "DYLD_IGNORE_PREBINDING") == 0 ) { + if ( strcmp(value, "all") == 0 ) { + gLinkContext.prebindUsage = ImageLoader::kUseNoPrebinding; + } + else if ( strcmp(value, "app") == 0 ) { + gLinkContext.prebindUsage = ImageLoader::kUseAllButAppPredbinding; + } + else if ( strcmp(value, "nonsplit") == 0 ) { + gLinkContext.prebindUsage = ImageLoader::kUseSplitSegPrebinding; + } + else if ( value[0] == '\0' ) { + gLinkContext.prebindUsage = ImageLoader::kUseSplitSegPrebinding; + } + else { + dyld::warn("unknown option to DYLD_IGNORE_PREBINDING. Valid options are: all, app, nonsplit\n"); + } + } +#if SUPPORT_VERSIONED_PATHS + else if ( strcmp(key, "DYLD_VERSIONED_LIBRARY_PATH") == 0 ) { + appendParsedColonList(value, mainExecutableDir, &sEnv.DYLD_VERSIONED_LIBRARY_PATH); + #if SUPPORT_ACCELERATE_TABLES + sDisableAcceleratorTables = true; + #endif + } + else if ( strcmp(key, "DYLD_VERSIONED_FRAMEWORK_PATH") == 0 ) { + appendParsedColonList(value, mainExecutableDir, &sEnv.DYLD_VERSIONED_FRAMEWORK_PATH); + #if SUPPORT_ACCELERATE_TABLES + sDisableAcceleratorTables = true; + #endif + } +#endif +#if !TARGET_IPHONE_SIMULATOR + else if ( (strcmp(key, "DYLD_PRINT_TO_FILE") == 0) && (mainExecutableDir == NULL) ) { + int fd = open(value, O_WRONLY | O_CREAT | O_APPEND, 0644); + if ( fd != -1 ) { + sLogfile = fd; + sLogToFile = true; + } + else { + dyld::log("dyld: could not open DYLD_PRINT_TO_FILE='%s', errno=%d\n", value, errno); + } + } +#endif + else { + dyld::warn("unknown environment variable: %s\n", key); + } +} + + +#if SUPPORT_LC_DYLD_ENVIRONMENT +static void checkLoadCommandEnvironmentVariables() +{ + // Support augmenting dyld environment variables in load commands + const uint32_t cmd_count = sMainExecutableMachHeader->ncmds; + const struct load_command* const cmds = (struct load_command*)(((char*)sMainExecutableMachHeader)+sizeof(macho_header)); + const struct load_command* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd) { + case LC_DYLD_ENVIRONMENT: + { + const struct dylinker_command* envcmd = (struct dylinker_command*)cmd; + const char* keyEqualsValue = (char*)envcmd + envcmd->name.offset; + char mainExecutableDir[strlen(sExecPath)+2]; + strcpy(mainExecutableDir, sExecPath); + char* lastSlash = strrchr(mainExecutableDir, '/'); + if ( lastSlash != NULL) + lastSlash[1] = '\0'; + // only process variables that start with DYLD_ and end in _PATH + if ( (strncmp(keyEqualsValue, "DYLD_", 5) == 0) ) { + const char* equals = strchr(keyEqualsValue, '='); + if ( equals != NULL ) { + if ( strncmp(&equals[-5], "_PATH", 5) == 0 ) { + const char* value = &equals[1]; + const size_t keyLen = equals-keyEqualsValue; + // don't let malformed load command overflow stack + if ( keyLen < 40 ) { + char key[keyLen+1]; + strncpy(key, keyEqualsValue, keyLen); + key[keyLen] = '\0'; + //dyld::log("processing: %s\n", keyEqualsValue); + //dyld::log("mainExecutableDir: %s\n", mainExecutableDir); + processDyldEnvironmentVariable(key, value, mainExecutableDir); + } + } + } + } + } + break; + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } +} +#endif // SUPPORT_LC_DYLD_ENVIRONMENT + + +static bool hasCodeSignatureLoadCommand(const macho_header* mh) +{ + const uint32_t cmd_count = mh->ncmds; + const struct load_command* const cmds = (struct load_command*)(((char*)mh)+sizeof(macho_header)); + const struct load_command* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + if (cmd->cmd == LC_CODE_SIGNATURE) + return true; + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + return false; +} + + +#if SUPPORT_VERSIONED_PATHS +static void checkVersionedPaths() +{ + // search DYLD_VERSIONED_LIBRARY_PATH directories for dylibs and check if they are newer + if ( sEnv.DYLD_VERSIONED_LIBRARY_PATH != NULL ) { + for(const char* const* lp = sEnv.DYLD_VERSIONED_LIBRARY_PATH; *lp != NULL; ++lp) { + checkDylibOverridesInDir(*lp); + } + } + + // search DYLD_VERSIONED_FRAMEWORK_PATH directories for dylibs and check if they are newer + if ( sEnv.DYLD_VERSIONED_FRAMEWORK_PATH != NULL ) { + for(const char* const* fp = sEnv.DYLD_VERSIONED_FRAMEWORK_PATH; *fp != NULL; ++fp) { + checkFrameworkOverridesInDir(*fp); + } + } +} +#endif + + +#if __MAC_OS_X_VERSION_MIN_REQUIRED +// +// For security, setuid programs ignore DYLD_* environment variables. +// Additionally, the DYLD_* enviroment variables are removed +// from the environment, so that any child processes don't see them. +// +static void pruneEnvironmentVariables(const char* envp[], const char*** applep) +{ +#if SUPPORT_LC_DYLD_ENVIRONMENT + checkLoadCommandEnvironmentVariables(); +#endif + + // delete all DYLD_* and LD_LIBRARY_PATH environment variables + int removedCount = 0; + const char** d = envp; + for(const char** s = envp; *s != NULL; s++) { + if ( (strncmp(*s, "DYLD_", 5) != 0) && (strncmp(*s, "LD_LIBRARY_PATH=", 16) != 0) ) { + *d++ = *s; + } + else { + ++removedCount; + } + } + *d++ = NULL; + // slide apple parameters + if ( removedCount > 0 ) { + *applep = d; + do { + *d = d[removedCount]; + } while ( *d++ != NULL ); + for(int i=0; i < removedCount; ++i) + *d++ = NULL; + } + + // disable framework and library fallback paths for setuid binaries rdar://problem/4589305 + sEnv.DYLD_FALLBACK_FRAMEWORK_PATH = NULL; + sEnv.DYLD_FALLBACK_LIBRARY_PATH = NULL; + + if ( removedCount > 0 ) + strlcat(sLoadingCrashMessage, ", ignoring DYLD_* env vars", sizeof(sLoadingCrashMessage)); +} +#endif + +static void defaultUninitializedFallbackPaths(const char* envp[]) +{ +#if __MAC_OS_X_VERSION_MIN_REQUIRED + if ( gLinkContext.processIsRestricted ) { + sEnv.DYLD_FALLBACK_FRAMEWORK_PATH = sRestrictedFrameworkFallbackPaths; + sEnv.DYLD_FALLBACK_LIBRARY_PATH = sRestrictedLibraryFallbackPaths; + return; + } + + // default value for DYLD_FALLBACK_FRAMEWORK_PATH, if not set in environment + const char* home = _simple_getenv(envp, "HOME");; + if ( sEnv.DYLD_FALLBACK_FRAMEWORK_PATH == NULL ) { + const char** fpaths = sFrameworkFallbackPaths; + if ( home == NULL ) + removePathWithPrefix(fpaths, "$HOME"); + else + paths_expand_roots(fpaths, "$HOME", home); + sEnv.DYLD_FALLBACK_FRAMEWORK_PATH = fpaths; + } + + // default value for DYLD_FALLBACK_LIBRARY_PATH, if not set in environment + if ( sEnv.DYLD_FALLBACK_LIBRARY_PATH == NULL ) { + const char** lpaths = sLibraryFallbackPaths; + if ( home == NULL ) + removePathWithPrefix(lpaths, "$HOME"); + else + paths_expand_roots(lpaths, "$HOME", home); + sEnv.DYLD_FALLBACK_LIBRARY_PATH = lpaths; + } +#else + if ( sEnv.DYLD_FALLBACK_FRAMEWORK_PATH == NULL ) + sEnv.DYLD_FALLBACK_FRAMEWORK_PATH = sFrameworkFallbackPaths; + + if ( sEnv.DYLD_FALLBACK_LIBRARY_PATH == NULL ) + sEnv.DYLD_FALLBACK_LIBRARY_PATH = sLibraryFallbackPaths; +#endif +} + + +static void checkEnvironmentVariables(const char* envp[]) +{ + if ( sEnvMode == envNone ) + return; + const char** p; + for(p = envp; *p != NULL; p++) { + const char* keyEqualsValue = *p; + if ( strncmp(keyEqualsValue, "DYLD_", 5) == 0 ) { + const char* equals = strchr(keyEqualsValue, '='); + if ( equals != NULL ) { + strlcat(sLoadingCrashMessage, "\n", sizeof(sLoadingCrashMessage)); + strlcat(sLoadingCrashMessage, keyEqualsValue, sizeof(sLoadingCrashMessage)); + const char* value = &equals[1]; + const size_t keyLen = equals-keyEqualsValue; + char key[keyLen+1]; + strncpy(key, keyEqualsValue, keyLen); + key[keyLen] = '\0'; + if ( (sEnvMode == envPrintOnly) && (strncmp(key, "DYLD_PRINT_", 11) != 0) ) + continue; + processDyldEnvironmentVariable(key, value, NULL); + } + } + else if ( strncmp(keyEqualsValue, "LD_LIBRARY_PATH=", 16) == 0 ) { + const char* path = &keyEqualsValue[16]; + sEnv.LD_LIBRARY_PATH = parseColonList(path, NULL); + } + } + +#if SUPPORT_LC_DYLD_ENVIRONMENT + checkLoadCommandEnvironmentVariables(); +#endif // SUPPORT_LC_DYLD_ENVIRONMENT + +#if SUPPORT_ROOT_PATH + // DYLD_IMAGE_SUFFIX and DYLD_ROOT_PATH cannot be used together + if ( (gLinkContext.imageSuffix != NULL) && (gLinkContext.rootPaths != NULL) ) { + dyld::warn("Ignoring DYLD_IMAGE_SUFFIX because DYLD_ROOT_PATH is used.\n"); + gLinkContext.imageSuffix = NULL; + } +#endif +} + +#if __x86_64__ && DYLD_SHARED_CACHE_SUPPORT +static bool isGCProgram(const macho_header* mh, uintptr_t slide) +{ + const uint32_t cmd_count = mh->ncmds; + const struct load_command* const cmds = (struct load_command*)(((char*)mh)+sizeof(macho_header)); + const struct load_command* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd) { + case LC_SEGMENT_COMMAND: + { + const struct macho_segment_command* seg = (struct macho_segment_command*)cmd; + if (strcmp(seg->segname, "__DATA") == 0) { + const struct macho_section* const sectionsStart = (struct macho_section*)((char*)seg + sizeof(struct macho_segment_command)); + const struct macho_section* const sectionsEnd = §ionsStart[seg->nsects]; + for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) { + if (strncmp(sect->sectname, "__objc_imageinfo", 16) == 0) { + const uint32_t* objcInfo = (uint32_t*)(sect->addr + slide); + return (objcInfo[1] & 6); // 6 = (OBJC_IMAGE_SUPPORTS_GC | OBJC_IMAGE_REQUIRES_GC) + } + } + } + } + break; + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + return false; +} +#endif + +static void getHostInfo(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide) +{ +#if CPU_SUBTYPES_SUPPORTED +#if __ARM_ARCH_7K__ + sHostCPU = CPU_TYPE_ARM; + sHostCPUsubtype = CPU_SUBTYPE_ARM_V7K; +#elif __ARM_ARCH_7A__ + sHostCPU = CPU_TYPE_ARM; + sHostCPUsubtype = CPU_SUBTYPE_ARM_V7; +#elif __ARM_ARCH_6K__ + sHostCPU = CPU_TYPE_ARM; + sHostCPUsubtype = CPU_SUBTYPE_ARM_V6; +#elif __ARM_ARCH_7F__ + sHostCPU = CPU_TYPE_ARM; + sHostCPUsubtype = CPU_SUBTYPE_ARM_V7F; +#elif __ARM_ARCH_7S__ + sHostCPU = CPU_TYPE_ARM; + sHostCPUsubtype = CPU_SUBTYPE_ARM_V7S; +#else + struct host_basic_info info; + mach_msg_type_number_t count = HOST_BASIC_INFO_COUNT; + mach_port_t hostPort = mach_host_self(); + kern_return_t result = host_info(hostPort, HOST_BASIC_INFO, (host_info_t)&info, &count); + if ( result != KERN_SUCCESS ) + throw "host_info() failed"; + sHostCPU = info.cpu_type; + sHostCPUsubtype = info.cpu_subtype; + mach_port_deallocate(mach_task_self(), hostPort); + #if __x86_64__ + #if DYLD_SHARED_CACHE_SUPPORT + sHaswell = (sHostCPUsubtype == CPU_SUBTYPE_X86_64_H); + // x86_64h: Fall back to the x86_64 slice if an app requires GC. + if ( sHaswell ) { + if ( isGCProgram(mainExecutableMH, mainExecutableSlide) ) { + // When running a GC program on a haswell machine, don't use and 'h slices + sHostCPUsubtype = CPU_SUBTYPE_X86_64_ALL; + sHaswell = false; + gLinkContext.sharedRegionMode = ImageLoader::kDontUseSharedRegion; + } + } + #endif + #endif +#endif +#endif +} + +static void checkSharedRegionDisable() +{ +#if __MAC_OS_X_VERSION_MIN_REQUIRED + // if main executable has segments that overlap the shared region, + // then disable using the shared region + if ( sMainExecutable->overlapsWithAddressRange((void*)(uintptr_t)SHARED_REGION_BASE, (void*)(uintptr_t)(SHARED_REGION_BASE + SHARED_REGION_SIZE)) ) { + gLinkContext.sharedRegionMode = ImageLoader::kDontUseSharedRegion; + if ( gLinkContext.verboseMapping ) + dyld::warn("disabling shared region because main executable overlaps\n"); + } +#if __i386__ + if ( gLinkContext.processIsRestricted ) { + // use private or no shared region for suid processes + gLinkContext.sharedRegionMode = ImageLoader::kUsePrivateSharedRegion; + } +#endif +#endif + // iPhoneOS cannot run without shared region +} + +bool validImage(const ImageLoader* possibleImage) +{ + const size_t imageCount = sAllImages.size(); + for(size_t i=0; i < imageCount; ++i) { + if ( possibleImage == sAllImages[i] ) { + return true; + } + } + return false; +} + +uint32_t getImageCount() +{ + return (uint32_t)sAllImages.size(); +} + +ImageLoader* getIndexedImage(unsigned int index) +{ + if ( index < sAllImages.size() ) + return sAllImages[index]; + return NULL; +} + +ImageLoader* findImageByMachHeader(const struct mach_header* target) +{ + return findMappedRange((uintptr_t)target); +} + + +ImageLoader* findImageContainingAddress(const void* addr) +{ + #if SUPPORT_ACCELERATE_TABLES + if ( sAllCacheImagesProxy != NULL ) { + const mach_header* mh; + const char* path; + unsigned index; + if ( sAllCacheImagesProxy->addressInCache(addr, &mh, &path, &index) ) + return sAllCacheImagesProxy; + } + #endif + return findMappedRange((uintptr_t)addr); +} + + +ImageLoader* findImageContainingSymbol(const void* symbol) +{ + for (std::vector::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) { + ImageLoader* anImage = *it; + if ( anImage->containsSymbol(symbol) ) + return anImage; + } + return NULL; +} + + + +void forEachImageDo( void (*callback)(ImageLoader*, void* userData), void* userData) +{ + const size_t imageCount = sAllImages.size(); + for(size_t i=0; i < imageCount; ++i) { + ImageLoader* anImage = sAllImages[i]; + (*callback)(anImage, userData); + } +} + +ImageLoader* findLoadedImage(const struct stat& stat_buf) +{ + const size_t imageCount = sAllImages.size(); + for(size_t i=0; i < imageCount; ++i){ + ImageLoader* anImage = sAllImages[i]; + if ( anImage->statMatch(stat_buf) ) + return anImage; + } + return NULL; +} + +// based on ANSI-C strstr() +static const char* strrstr(const char* str, const char* sub) +{ + const size_t sublen = strlen(sub); + for(const char* p = &str[strlen(str)]; p != str; --p) { + if ( strncmp(p, sub, sublen) == 0 ) + return p; + } + return NULL; +} + + +// +// Find framework path +// +// /path/foo.framework/foo => foo.framework/foo +// /path/foo.framework/Versions/A/foo => foo.framework/Versions/A/foo +// /path/foo.framework/Frameworks/bar.framework/bar => bar.framework/bar +// /path/foo.framework/Libraries/bar.dylb => NULL +// /path/foo.framework/bar => NULL +// +// Returns NULL if not a framework path +// +static const char* getFrameworkPartialPath(const char* path) +{ + const char* dirDot = strrstr(path, ".framework/"); + if ( dirDot != NULL ) { + const char* dirStart = dirDot; + for ( ; dirStart >= path; --dirStart) { + if ( (*dirStart == '/') || (dirStart == path) ) { + const char* frameworkStart = &dirStart[1]; + if ( dirStart == path ) + --frameworkStart; + size_t len = dirDot - frameworkStart; + char framework[len+1]; + strncpy(framework, frameworkStart, len); + framework[len] = '\0'; + const char* leaf = strrchr(path, '/'); + if ( leaf != NULL ) { + if ( strcmp(framework, &leaf[1]) == 0 ) { + return frameworkStart; + } + if ( gLinkContext.imageSuffix != NULL ) { + // some debug frameworks have install names that end in _debug + if ( strncmp(framework, &leaf[1], len) == 0 ) { + if ( strcmp( gLinkContext.imageSuffix, &leaf[len+1]) == 0 ) + return frameworkStart; + } + } + } + } + } + } + return NULL; +} + + +static const char* getLibraryLeafName(const char* path) +{ + const char* start = strrchr(path, '/'); + if ( start != NULL ) + return &start[1]; + else + return path; +} + + +// only for architectures that use cpu-sub-types +#if CPU_SUBTYPES_SUPPORTED + +const cpu_subtype_t CPU_SUBTYPE_END_OF_LIST = -1; + + +// +// A fat file may contain multiple sub-images for the same CPU type. +// In that case, dyld picks which sub-image to use by scanning a table +// of preferred cpu-sub-types for the running cpu. +// +// There is one row in the table for each cpu-sub-type on which dyld might run. +// The first entry in a row is that cpu-sub-type. It is followed by all +// cpu-sub-types that can run on that cpu, if preferred order. Each row ends with +// a "SUBTYPE_ALL" (to denote that images written to run on any cpu-sub-type are usable), +// followed by one or more CPU_SUBTYPE_END_OF_LIST to pad out this row. +// + + +#if __arm__ +// +// ARM sub-type lists +// +const int kARM_RowCount = 8; +static const cpu_subtype_t kARM[kARM_RowCount][9] = { + + // armv7f can run: v7f, v7, v6, v5, and v4 + { CPU_SUBTYPE_ARM_V7F, CPU_SUBTYPE_ARM_V7, CPU_SUBTYPE_ARM_V6, CPU_SUBTYPE_ARM_V5TEJ, CPU_SUBTYPE_ARM_V4T, CPU_SUBTYPE_ARM_ALL, CPU_SUBTYPE_END_OF_LIST }, + + // armv7k can run: v7k + { CPU_SUBTYPE_ARM_V7K, CPU_SUBTYPE_END_OF_LIST }, + + // armv7s can run: v7s, v7, v7f, v7k, v6, v5, and v4 + { CPU_SUBTYPE_ARM_V7S, CPU_SUBTYPE_ARM_V7, CPU_SUBTYPE_ARM_V7F, CPU_SUBTYPE_ARM_V6, CPU_SUBTYPE_ARM_V5TEJ, CPU_SUBTYPE_ARM_V4T, CPU_SUBTYPE_ARM_ALL, CPU_SUBTYPE_END_OF_LIST }, + + // armv7 can run: v7, v6, v5, and v4 + { CPU_SUBTYPE_ARM_V7, CPU_SUBTYPE_ARM_V6, CPU_SUBTYPE_ARM_V5TEJ, CPU_SUBTYPE_ARM_V4T, CPU_SUBTYPE_ARM_ALL, CPU_SUBTYPE_END_OF_LIST }, + + // armv6 can run: v6, v5, and v4 + { CPU_SUBTYPE_ARM_V6, CPU_SUBTYPE_ARM_V5TEJ, CPU_SUBTYPE_ARM_V4T, CPU_SUBTYPE_ARM_ALL, CPU_SUBTYPE_END_OF_LIST, CPU_SUBTYPE_END_OF_LIST }, + + // xscale can run: xscale, v5, and v4 + { CPU_SUBTYPE_ARM_XSCALE, CPU_SUBTYPE_ARM_V5TEJ, CPU_SUBTYPE_ARM_V4T, CPU_SUBTYPE_ARM_ALL, CPU_SUBTYPE_END_OF_LIST, CPU_SUBTYPE_END_OF_LIST }, + + // armv5 can run: v5 and v4 + { CPU_SUBTYPE_ARM_V5TEJ, CPU_SUBTYPE_ARM_V4T, CPU_SUBTYPE_ARM_ALL, CPU_SUBTYPE_END_OF_LIST, CPU_SUBTYPE_END_OF_LIST, CPU_SUBTYPE_END_OF_LIST }, + + // armv4 can run: v4 + { CPU_SUBTYPE_ARM_V4T, CPU_SUBTYPE_ARM_ALL, CPU_SUBTYPE_END_OF_LIST, CPU_SUBTYPE_END_OF_LIST, CPU_SUBTYPE_END_OF_LIST, CPU_SUBTYPE_END_OF_LIST }, +}; +#endif + +#if __x86_64__ +// +// x86_64 sub-type lists +// +const int kX86_64_RowCount = 2; +static const cpu_subtype_t kX86_64[kX86_64_RowCount][5] = { + + // x86_64h can run: x86_64h, x86_64h(lib), x86_64(lib), and x86_64 + { CPU_SUBTYPE_X86_64_H, (cpu_subtype_t) CPU_SUBTYPE_LIB64|CPU_SUBTYPE_X86_64_H, (cpu_subtype_t) CPU_SUBTYPE_LIB64|CPU_SUBTYPE_X86_64_ALL, CPU_SUBTYPE_X86_64_ALL, CPU_SUBTYPE_END_OF_LIST }, + + // x86_64 can run: x86_64(lib) and x86_64 + { CPU_SUBTYPE_X86_64_ALL, (cpu_subtype_t) CPU_SUBTYPE_LIB64|CPU_SUBTYPE_X86_64_ALL, CPU_SUBTYPE_END_OF_LIST }, + +}; +#endif + + +// scan the tables above to find the cpu-sub-type-list for this machine +static const cpu_subtype_t* findCPUSubtypeList(cpu_type_t cpu, cpu_subtype_t subtype) +{ + switch (cpu) { +#if __arm__ + case CPU_TYPE_ARM: + for (int i=0; i < kARM_RowCount ; ++i) { + if ( kARM[i][0] == subtype ) + return kARM[i]; + } + break; +#endif +#if __x86_64__ + case CPU_TYPE_X86_64: + for (int i=0; i < kX86_64_RowCount ; ++i) { + if ( kX86_64[i][0] == subtype ) + return kX86_64[i]; + } + break; +#endif + } + return NULL; +} + + + + +// scan fat table-of-contents for best most preferred subtype +static bool fatFindBestFromOrderedList(cpu_type_t cpu, const cpu_subtype_t list[], const fat_header* fh, uint64_t* offset, uint64_t* len) +{ + const fat_arch* const archs = (fat_arch*)(((char*)fh)+sizeof(fat_header)); + for (uint32_t subTypeIndex=0; list[subTypeIndex] != CPU_SUBTYPE_END_OF_LIST; ++subTypeIndex) { + for(uint32_t fatIndex=0; fatIndex < OSSwapBigToHostInt32(fh->nfat_arch); ++fatIndex) { + if ( ((cpu_type_t)OSSwapBigToHostInt32(archs[fatIndex].cputype) == cpu) + && (list[subTypeIndex] == (cpu_subtype_t)OSSwapBigToHostInt32(archs[fatIndex].cpusubtype)) ) { + *offset = OSSwapBigToHostInt32(archs[fatIndex].offset); + *len = OSSwapBigToHostInt32(archs[fatIndex].size); + return true; + } + } + } + return false; +} + +// scan fat table-of-contents for exact match of cpu and cpu-sub-type +static bool fatFindExactMatch(cpu_type_t cpu, cpu_subtype_t subtype, const fat_header* fh, uint64_t* offset, uint64_t* len) +{ + const fat_arch* archs = (fat_arch*)(((char*)fh)+sizeof(fat_header)); + for(uint32_t i=0; i < OSSwapBigToHostInt32(fh->nfat_arch); ++i) { + if ( ((cpu_type_t)OSSwapBigToHostInt32(archs[i].cputype) == cpu) + && ((cpu_subtype_t)OSSwapBigToHostInt32(archs[i].cpusubtype) == subtype) ) { + *offset = OSSwapBigToHostInt32(archs[i].offset); + *len = OSSwapBigToHostInt32(archs[i].size); + return true; + } + } + return false; +} + +// scan fat table-of-contents for image with matching cpu-type and runs-on-all-sub-types +static bool fatFindRunsOnAllCPUs(cpu_type_t cpu, const fat_header* fh, uint64_t* offset, uint64_t* len) +{ + const fat_arch* archs = (fat_arch*)(((char*)fh)+sizeof(fat_header)); + for(uint32_t i=0; i < OSSwapBigToHostInt32(fh->nfat_arch); ++i) { + if ( (cpu_type_t)OSSwapBigToHostInt32(archs[i].cputype) == cpu) { + switch (cpu) { +#if __arm__ + case CPU_TYPE_ARM: + if ( (cpu_subtype_t)OSSwapBigToHostInt32(archs[i].cpusubtype) == CPU_SUBTYPE_ARM_ALL ) { + *offset = OSSwapBigToHostInt32(archs[i].offset); + *len = OSSwapBigToHostInt32(archs[i].size); + return true; + } + break; +#endif +#if __x86_64__ + case CPU_TYPE_X86_64: + if ( (cpu_subtype_t)OSSwapBigToHostInt32(archs[i].cpusubtype) == CPU_SUBTYPE_X86_64_ALL ) { + *offset = OSSwapBigToHostInt32(archs[i].offset); + *len = OSSwapBigToHostInt32(archs[i].size); + return true; + } + break; +#endif + } + } + } + return false; +} + +#endif // CPU_SUBTYPES_SUPPORTED + + +// +// Validate the fat_header and fat_arch array: +// +// 1) arch count would not cause array to extend past 4096 byte read buffer +// 2) no slice overlaps the fat_header and arch array +// 3) arch list does not contain duplicate cputype/cpusubtype tuples +// 4) arch list does not have two overlapping slices. +// +static bool fatValidate(const fat_header* fh) +{ + if ( fh->magic != OSSwapBigToHostInt32(FAT_MAGIC) ) + return false; + + // since only first 4096 bytes of file read, we can only handle up to 204 slices. + const uint32_t sliceCount = OSSwapBigToHostInt32(fh->nfat_arch); + if ( sliceCount > 204 ) + return false; + + // compare all slices looking for conflicts + const fat_arch* archs = (fat_arch*)(((char*)fh)+sizeof(fat_header)); + for (uint32_t i=0; i < sliceCount; ++i) { + uint32_t i_offset = OSSwapBigToHostInt32(archs[i].offset); + uint32_t i_size = OSSwapBigToHostInt32(archs[i].size); + uint32_t i_cputype = OSSwapBigToHostInt32(archs[i].cputype); + uint32_t i_cpusubtype = OSSwapBigToHostInt32(archs[i].cpusubtype); + uint32_t i_end = i_offset + i_size; + // slice cannot overlap with header + if ( i_offset < 4096 ) + return false; + // slice size cannot overflow + if ( i_end < i_offset ) + return false; + for (uint32_t j=i+1; j < sliceCount; ++j) { + uint32_t j_offset = OSSwapBigToHostInt32(archs[j].offset); + uint32_t j_size = OSSwapBigToHostInt32(archs[j].size); + uint32_t j_cputype = OSSwapBigToHostInt32(archs[j].cputype); + uint32_t j_cpusubtype = OSSwapBigToHostInt32(archs[j].cpusubtype); + uint32_t j_end = j_offset + j_size; + // duplicate slices types not allowed + if ( (i_cputype == j_cputype) && (i_cpusubtype == j_cpusubtype) ) + return false; + // slice size cannot overflow + if ( j_end < j_offset ) + return false; + // check for overlap of slices + if ( i_offset <= j_offset ) { + if ( j_offset < i_end ) + return false; // j overlaps end of i + } + else { + // j overlaps end of i + if ( i_offset < j_end ) + return false; // i overlaps end of j + } + } + } + return true; +} + +// +// A fat file may contain multiple sub-images for the same cpu-type, +// each optimized for a different cpu-sub-type (e.g G3 or G5). +// This routine picks the optimal sub-image. +// +static bool fatFindBest(const fat_header* fh, uint64_t* offset, uint64_t* len) +{ + if ( !fatValidate(fh) ) + return false; + +#if CPU_SUBTYPES_SUPPORTED + // assume all dylibs loaded must have same cpu type as main executable + const cpu_type_t cpu = sMainExecutableMachHeader->cputype; + + // We only know the subtype to use if the main executable cpu type matches the host + if ( (cpu & CPU_TYPE_MASK) == sHostCPU ) { + // get preference ordered list of subtypes + const cpu_subtype_t* subTypePreferenceList = findCPUSubtypeList(cpu, sHostCPUsubtype); + + // use ordered list to find best sub-image in fat file + if ( subTypePreferenceList != NULL ) { + if ( fatFindBestFromOrderedList(cpu, subTypePreferenceList, fh, offset, len) ) + return true; + } + + // if running cpu is not in list, try for an exact match + if ( fatFindExactMatch(cpu, sHostCPUsubtype, fh, offset, len) ) + return true; + } + + // running on an uknown cpu, can only load generic code + return fatFindRunsOnAllCPUs(cpu, fh, offset, len); +#else + // just find first slice with matching architecture + const fat_arch* archs = (fat_arch*)(((char*)fh)+sizeof(fat_header)); + for(uint32_t i=0; i < OSSwapBigToHostInt32(fh->nfat_arch); ++i) { + if ( (cpu_type_t)OSSwapBigToHostInt32(archs[i].cputype) == sMainExecutableMachHeader->cputype) { + *offset = OSSwapBigToHostInt32(archs[i].offset); + *len = OSSwapBigToHostInt32(archs[i].size); + return true; + } + } + return false; +#endif +} + + + +// +// This is used to validate if a non-fat (aka thin or raw) mach-o file can be used +// on the current processor. // +bool isCompatibleMachO(const uint8_t* firstPage, const char* path) +{ +#if CPU_SUBTYPES_SUPPORTED + // It is deemed compatible if any of the following are true: + // 1) mach_header subtype is in list of compatible subtypes for running processor + // 2) mach_header subtype is same as running processor subtype + // 3) mach_header subtype runs on all processor variants + const mach_header* mh = (mach_header*)firstPage; + if ( mh->magic == sMainExecutableMachHeader->magic ) { + if ( mh->cputype == sMainExecutableMachHeader->cputype ) { + if ( (mh->cputype & CPU_TYPE_MASK) == sHostCPU ) { + // get preference ordered list of subtypes that this machine can use + const cpu_subtype_t* subTypePreferenceList = findCPUSubtypeList(mh->cputype, sHostCPUsubtype); + if ( subTypePreferenceList != NULL ) { + // if image's subtype is in the list, it is compatible + for (const cpu_subtype_t* p = subTypePreferenceList; *p != CPU_SUBTYPE_END_OF_LIST; ++p) { + if ( *p == mh->cpusubtype ) + return true; + } + // have list and not in list, so not compatible + throwf("incompatible cpu-subtype: 0x%08X in %s", mh->cpusubtype, path); + } + // unknown cpu sub-type, but if exact match for current subtype then ok to use + if ( mh->cpusubtype == sHostCPUsubtype ) + return true; + } + + // cpu type has no ordered list of subtypes + switch (mh->cputype) { + case CPU_TYPE_I386: + case CPU_TYPE_X86_64: + // subtypes are not used or these architectures + return true; + } + } + } +#else + // For architectures that don't support cpu-sub-types + // this just check the cpu type. + const mach_header* mh = (mach_header*)firstPage; + if ( mh->magic == sMainExecutableMachHeader->magic ) { + if ( mh->cputype == sMainExecutableMachHeader->cputype ) { + return true; + } + } +#endif + return false; +} + + + + +// The kernel maps in main executable before dyld gets control. We need to +// make an ImageLoader* for the already mapped in main executable. +static ImageLoaderMachO* instantiateFromLoadedImage(const macho_header* mh, uintptr_t slide, const char* path) +{ + // try mach-o loader + if ( isCompatibleMachO((const uint8_t*)mh, path) ) { + ImageLoader* image = ImageLoaderMachO::instantiateMainExecutable(mh, slide, path, gLinkContext); + addImage(image); + return (ImageLoaderMachO*)image; + } + + throw "main executable not a known format"; +} + +#if DYLD_SHARED_CACHE_SUPPORT + +#if __IPHONE_OS_VERSION_MIN_REQUIRED +static bool dylibsCanOverrideCache() +{ + uint32_t devFlags = *((uint32_t*)_COMM_PAGE_DEV_FIRM); + if ( (devFlags & 1) == 0 ) + return false; + return ( (sSharedCache != NULL) && (sSharedCache->cacheType == kDyldSharedCacheTypeDevelopment) ); +} +#endif + +static bool findInSharedCacheImage(const char* path, bool searchByPath, const struct stat* stat_buf, const macho_header** mh, const char** pathInCache, long* slide) +{ + if ( sSharedCache != NULL ) { +#if __MAC_OS_X_VERSION_MIN_REQUIRED + // Mac OS X always requires inode/mtime to valid cache + // if stat() not done yet, do it now + struct stat statb; + if ( stat_buf == NULL ) { + if ( my_stat(path, &statb) == -1 ) + return false; + stat_buf = &statb; + } +#endif +#if __IPHONE_OS_VERSION_MIN_REQUIRED + uint64_t hash = 0; + for (const char* s=path; *s != '\0'; ++s) + hash += hash*4 + *s; +#endif + + // walk shared cache to see if there is a cached image that matches the inode/mtime/path desired + const dyld_cache_image_info* const start = (dyld_cache_image_info*)((uint8_t*)sSharedCache + sSharedCache->imagesOffset); + const dyld_cache_image_info* const end = &start[sSharedCache->imagesCount]; +#if __IPHONE_OS_VERSION_MIN_REQUIRED + const bool cacheHasHashInfo = (start->modTime == 0); +#endif + for( const dyld_cache_image_info* p = start; p != end; ++p) { +#if __IPHONE_OS_VERSION_MIN_REQUIRED + // just check path + const char* aPath = (char*)sSharedCache + p->pathFileOffset; + if ( cacheHasHashInfo && (p->inode != hash) ) + continue; + if ( strcmp(path, aPath) == 0 ) { + // found image in cache + *mh = (macho_header*)(p->address+sSharedCacheSlide); + *pathInCache = aPath; + *slide = sSharedCacheSlide; + if ( aPath < (char*)(*mh) ) { + // found alias, rescan list to get canonical name + for (const dyld_cache_image_info* p2 = start; p2 != end; ++p2) { + if ( p2->address == p->address ) { + *pathInCache = (char*)sSharedCache + p2->pathFileOffset; + break; + } + } + } + return true; + } +#elif __MAC_OS_X_VERSION_MIN_REQUIRED + // check mtime and inode first because it is fast + bool inodeMatch = ( ((time_t)p->modTime == stat_buf->st_mtime) && ((ino_t)p->inode == stat_buf->st_ino) ); + if ( searchByPath || sSharedCacheIgnoreInodeAndTimeStamp || inodeMatch ) { + // mod-time and inode match an image in the shared cache, now check path + const char* aPath = (char*)sSharedCache + p->pathFileOffset; + bool cacheHit = (strcmp(path, aPath) == 0); + if ( inodeMatch && !cacheHit ) { + // path does not match install name of dylib in cache, but inode and mtime does match + // perhaps path is a symlink to the cached dylib + struct stat pathInCacheStatBuf; + if ( my_stat(aPath, &pathInCacheStatBuf) != -1 ) + cacheHit = ( (pathInCacheStatBuf.st_dev == stat_buf->st_dev) && (pathInCacheStatBuf.st_ino == stat_buf->st_ino) ); + } + if ( cacheHit ) { + // found image in cache, return info + *mh = (macho_header*)(p->address+sSharedCacheSlide); + //dyld::log("findInSharedCacheImage(), mh=%p, p->address=0x%0llX, slid=0x%0lX, path=%s\n", + // *mh, p->address, sSharedCacheSlide, aPath); + *pathInCache = aPath; + *slide = sSharedCacheSlide; + return true; + } + } +#endif + } + } + return false; +} + +bool inSharedCache(const char* path) +{ + const macho_header* mhInCache; + const char* pathInCache; + long slide; + return findInSharedCacheImage(path, true, NULL, &mhInCache, &pathInCache, &slide); +} + +#endif + +static ImageLoader* checkandAddImage(ImageLoader* image, const LoadContext& context) +{ + // now sanity check that this loaded image does not have the same install path as any existing image + const char* loadedImageInstallPath = image->getInstallPath(); + if ( image->isDylib() && (loadedImageInstallPath != NULL) && (loadedImageInstallPath[0] == '/') ) { + for (std::vector::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) { + ImageLoader* anImage = *it; + const char* installPath = anImage->getInstallPath(); + if ( installPath != NULL) { + if ( strcmp(loadedImageInstallPath, installPath) == 0 ) { + //dyld::log("duplicate(%s) => %p\n", installPath, anImage); + removeImage(image); + ImageLoader::deleteImage(image); + return anImage; + } + } + } + } + + // some API's restrict what they can load + if ( context.mustBeBundle && !image->isBundle() ) + throw "not a bundle"; + if ( context.mustBeDylib && !image->isDylib() ) + throw "not a dylib"; + + // regular main executables cannot be loaded + if ( image->isExecutable() ) { + if ( !context.canBePIE || !image->isPositionIndependentExecutable() ) + throw "can't load a main executable"; + } + + // don't add bundles to global list, they can be loaded but not linked. When linked it will be added to list + if ( ! image->isBundle() ) + addImage(image); + + return image; +} + +#if TARGET_IPHONE_SIMULATOR +static bool isSimulatorBinary(const uint8_t* firstPages, const char* path) +{ + const macho_header* mh = (macho_header*)firstPages; + const uint32_t cmd_count = mh->ncmds; + const load_command* const cmds = (struct load_command*)(((char*)mh)+sizeof(macho_header)); + const load_command* const cmdsEnd = (load_command*)((char*)cmds + mh->sizeofcmds); + const struct load_command* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd) { + #if TARGET_OS_WATCH + case LC_VERSION_MIN_WATCHOS: + return true; + #elif TARGET_OS_TV + case LC_VERSION_MIN_TVOS: + return true; + #elif TARGET_OS_IOS + case LC_VERSION_MIN_IPHONEOS: + return true; + #endif + case LC_VERSION_MIN_MACOSX: + // grandfather in a few libSystem dylibs + if ((strcmp(path, "/usr/lib/system/libsystem_kernel.dylib") == 0) || + (strcmp(path, "/usr/lib/system/libsystem_platform.dylib") == 0) || + (strcmp(path, "/usr/lib/system/libsystem_pthread.dylib") == 0)) + return true; + return false; + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + if ( cmd > cmdsEnd ) + return false; + } + return false; +} +#endif + +// map in file and instantiate an ImageLoader +static ImageLoader* loadPhase6(int fd, const struct stat& stat_buf, const char* path, const LoadContext& context) +{ + //dyld::log("%s(%s)\n", __func__ , path); + uint64_t fileOffset = 0; + uint64_t fileLength = stat_buf.st_size; + + // validate it is a file (not directory) + if ( (stat_buf.st_mode & S_IFMT) != S_IFREG ) + throw "not a file"; + + uint8_t firstPages[MAX_MACH_O_HEADER_AND_LOAD_COMMANDS_SIZE]; + bool shortPage = false; + + // min mach-o file is 4K + if ( fileLength < 4096 ) { + if ( pread(fd, firstPages, fileLength, 0) != (ssize_t)fileLength ) + throwf("pread of short file failed: %d", errno); + shortPage = true; + } + else { + // optimistically read only first 4KB + if ( pread(fd, firstPages, 4096, 0) != 4096 ) + throwf("pread of first 4K failed: %d", errno); + } + + // if fat wrapper, find usable sub-file + const fat_header* fileStartAsFat = (fat_header*)firstPages; + if ( fileStartAsFat->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) { + if ( OSSwapBigToHostInt32(fileStartAsFat->nfat_arch) > ((4096 - sizeof(fat_header)) / sizeof(fat_arch)) ) + throwf("fat header too large: %u entries", OSSwapBigToHostInt32(fileStartAsFat->nfat_arch)); + if ( fatFindBest(fileStartAsFat, &fileOffset, &fileLength) ) { + if ( (fileOffset+fileLength) > (uint64_t)(stat_buf.st_size) ) + throwf("truncated fat file. file length=%llu, but needed slice goes to %llu", stat_buf.st_size, fileOffset+fileLength); + if (pread(fd, firstPages, 4096, fileOffset) != 4096) + throwf("pread of fat file failed: %d", errno); + } + else { + throw "no matching architecture in universal wrapper"; + } + } + + // try mach-o loader + if ( shortPage ) + throw "file too short"; + if ( isCompatibleMachO(firstPages, path) ) { + + // only MH_BUNDLE, MH_DYLIB, and some MH_EXECUTE can be dynamically loaded + const mach_header* mh = (mach_header*)firstPages; + switch ( mh->filetype ) { + case MH_EXECUTE: + case MH_DYLIB: + case MH_BUNDLE: + break; + default: + throw "mach-o, but wrong filetype"; + } + + uint32_t headerAndLoadCommandsSize = sizeof(macho_header) + mh->sizeofcmds; + if ( headerAndLoadCommandsSize > MAX_MACH_O_HEADER_AND_LOAD_COMMANDS_SIZE ) + throwf("malformed mach-o: load commands size (%u) > %u", headerAndLoadCommandsSize, MAX_MACH_O_HEADER_AND_LOAD_COMMANDS_SIZE); + + if ( headerAndLoadCommandsSize > fileLength ) + dyld::throwf("malformed mach-o: load commands size (%u) > mach-o file size (%llu)", headerAndLoadCommandsSize, fileLength); + + if ( headerAndLoadCommandsSize > 4096 ) { + // read more pages + unsigned readAmount = headerAndLoadCommandsSize - 4096; + if ( pread(fd, &firstPages[4096], readAmount, fileOffset+4096) != readAmount ) + throwf("pread of extra load commands past 4KB failed: %d", errno); + } + +#if TARGET_IPHONE_SIMULATOR + // dyld_sim should restrict loading osx binaries + if ( !isSimulatorBinary(firstPages, path) ) { + #if TARGET_OS_WATCH + throw "mach-o, but not built for watchOS simulator"; + #elif TARGET_OS_TV + throw "mach-o, but not built for tvOS simulator"; + #else + throw "mach-o, but not built for iOS simulator"; + #endif + } +#endif + + // instantiate an image + ImageLoader* image = ImageLoaderMachO::instantiateFromFile(path, fd, firstPages, headerAndLoadCommandsSize, fileOffset, fileLength, stat_buf, gLinkContext); + + // validate + return checkandAddImage(image, context); + } + + // try other file formats here... + + + // throw error about what was found + switch (*(uint32_t*)firstPages) { + case MH_MAGIC: + case MH_CIGAM: + case MH_MAGIC_64: + case MH_CIGAM_64: + throw "mach-o, but wrong architecture"; + default: + throwf("unknown file type, first eight bytes: 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X", + firstPages[0], firstPages[1], firstPages[2], firstPages[3], firstPages[4], firstPages[5], firstPages[6],firstPages[7]); + } +} + + +static ImageLoader* loadPhase5open(const char* path, const LoadContext& context, const struct stat& stat_buf, std::vector* exceptions) +{ + //dyld::log("%s(%s, %p)\n", __func__ , path, exceptions); + + // open file (automagically closed when this function exits) + FileOpener file(path); + + // just return NULL if file not found, but record any other errors + if ( file.getFileDescriptor() == -1 ) { + int err = errno; + if ( err != ENOENT ) { + const char* newMsg; + if ( (err == EPERM) && sandboxBlockedOpen(path) ) + newMsg = dyld::mkstringf("file system sandbox blocked open() of '%s'", path); + else + newMsg = dyld::mkstringf("%s: open() failed with errno=%d", path, err); + exceptions->push_back(newMsg); + } + return NULL; + } + + try { + return loadPhase6(file.getFileDescriptor(), stat_buf, path, context); + } + catch (const char* msg) { + const char* newMsg = dyld::mkstringf("%s: %s", path, msg); + exceptions->push_back(newMsg); + free((void*)msg); + return NULL; + } +} + + +#if __MAC_OS_X_VERSION_MIN_REQUIRED +static ImageLoader* loadPhase5load(const char* path, const char* orgPath, const LoadContext& context, unsigned& cacheIndex, std::vector* exceptions) +{ + //dyld::log("%s(%s, %p)\n", __func__ , path, exceptions); + ImageLoader* image = NULL; + + #if SUPPORT_ACCELERATE_TABLES + if ( sAllCacheImagesProxy != NULL ) { + unsigned index; + if ( sAllCacheImagesProxy->hasDylib(path, &index) ) + return sAllCacheImagesProxy; + } + #endif + + // just return NULL if file not found, but record any other errors + struct stat stat_buf; + if ( my_stat(path, &stat_buf) == -1 ) { + int err = errno; + if ( err != ENOENT ) { + if ( (err == EPERM) && sandboxBlockedStat(path) ) + exceptions->push_back(dyld::mkstringf("%s: file system sandbox blocked stat()", path)); + else + exceptions->push_back(dyld::mkstringf("%s: stat() failed with errno=%d", path, err)); + } + return NULL; + } + + // in case image was renamed or found via symlinks, check for inode match + image = findLoadedImage(stat_buf); + if ( image != NULL ) + return image; + + // do nothing if not already loaded and if RTLD_NOLOAD or NSADDIMAGE_OPTION_RETURN_ONLY_IF_LOADED + if ( context.dontLoad ) + return NULL; + +#if DYLD_SHARED_CACHE_SUPPORT + // see if this image is in shared cache + const macho_header* mhInCache; + const char* pathInCache; + long slideInCache; + if ( findInSharedCacheImage(path, false, &stat_buf, &mhInCache, &pathInCache, &slideInCache) ) { + image = ImageLoaderMachO::instantiateFromCache(mhInCache, pathInCache, slideInCache, stat_buf, gLinkContext); + return checkandAddImage(image, context); + } +#endif + // file exists and is not in dyld shared cache, so open it + return loadPhase5open(path, context, stat_buf, exceptions); +} +#endif // __MAC_OS_X_VERSION_MIN_REQUIRED + + + +#if __IPHONE_OS_VERSION_MIN_REQUIRED +static ImageLoader* loadPhase5stat(const char* path, const LoadContext& context, struct stat* stat_buf, + int* statErrNo, bool* imageFound, std::vector* exceptions) +{ + ImageLoader* image = NULL; + *imageFound = false; + *statErrNo = 0; + if ( my_stat(path, stat_buf) == 0 ) { + // in case image was renamed or found via symlinks, check for inode match + image = findLoadedImage(*stat_buf); + if ( image != NULL ) { + *imageFound = true; + return image; + } + // do nothing if not already loaded and if RTLD_NOLOAD + if ( context.dontLoad ) { + *imageFound = true; + return NULL; + } + image = loadPhase5open(path, context, *stat_buf, exceptions); + if ( image != NULL ) { + *imageFound = true; + return image; + } + } + else { + *statErrNo = errno; + } + return NULL; +} + +// try to open file +static ImageLoader* loadPhase5load(const char* path, const char* orgPath, const LoadContext& context, unsigned& cacheIndex, std::vector* exceptions) +{ + //dyld::log("%s(%s, %p)\n", __func__ , path, exceptions); + struct stat stat_buf; + bool imageFound; + int statErrNo; + ImageLoader* image; +#if DYLD_SHARED_CACHE_SUPPORT + #if SUPPORT_ACCELERATE_TABLES + if ( sAllCacheImagesProxy != NULL ) { + if ( sAllCacheImagesProxy->hasDylib(path, &cacheIndex) ) + return sAllCacheImagesProxy; + } + #endif + if ( dylibsCanOverrideCache() ) { + // flag is set that allows installed framework roots to override dyld shared cache + image = loadPhase5stat(path, context, &stat_buf, &statErrNo, &imageFound, exceptions); + if ( imageFound ) + return image; + } + // see if this image is in shared cache + const macho_header* mhInCache; + const char* pathInCache; + long slideInCache; + if ( findInSharedCacheImage(path, true, NULL, &mhInCache, &pathInCache, &slideInCache) ) { + // see if this image in the cache was already loaded via a different path + for (std::vector::iterator it=sAllImages.begin(); it != sAllImages.end(); ++it) { + ImageLoader* anImage = *it; + if ( (const macho_header*)anImage->machHeader() == mhInCache ) + return anImage; + } + // do nothing if not already loaded and if RTLD_NOLOAD + if ( context.dontLoad ) + return NULL; + // nope, so instantiate a new image from dyld shared cache + // zero out stat buffer so mtime, etc are zero for items from the shared cache + bzero(&stat_buf, sizeof(stat_buf)); + image = ImageLoaderMachO::instantiateFromCache(mhInCache, pathInCache, slideInCache, stat_buf, gLinkContext); + return checkandAddImage(image, context); + } + + if ( !dylibsCanOverrideCache() ) { + // flag is not set, and not in cache to try opening it + image = loadPhase5stat(path, context, &stat_buf, &statErrNo, &imageFound, exceptions); + if ( imageFound ) + return image; + } +#else + image = loadPhase5stat(path, context, &stat_buf, &statErrNo, &imageFound, exceptions); + if ( imageFound ) + return image; +#endif + // just return NULL if file not found, but record any other errors + if ( (statErrNo != ENOENT) && (statErrNo != 0) ) { + if ( (statErrNo == EPERM) && sandboxBlockedStat(path) ) + exceptions->push_back(dyld::mkstringf("%s: file system sandbox blocked stat()", path)); + else + exceptions->push_back(dyld::mkstringf("%s: stat() failed with errno=%d", path, statErrNo)); + } + return NULL; +} +#endif // __IPHONE_OS_VERSION_MIN_REQUIRED + + +// look for path match with existing loaded images +static ImageLoader* loadPhase5check(const char* path, const char* orgPath, const LoadContext& context) +{ + //dyld::log("%s(%s, %s)\n", __func__ , path, orgPath); + // search path against load-path and install-path of all already loaded images + uint32_t hash = ImageLoader::hash(path); + //dyld::log("check() hash=%d, path=%s\n", hash, path); + for (std::vector::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) { + ImageLoader* anImage = *it; + // check hash first to cut down on strcmp calls + //dyld::log(" check() hash=%d, path=%s\n", anImage->getPathHash(), anImage->getPath()); + if ( anImage->getPathHash() == hash ) { + if ( strcmp(path, anImage->getPath()) == 0 ) { + // if we are looking for a dylib don't return something else + if ( !context.mustBeDylib || anImage->isDylib() ) + return anImage; + } + } + if ( context.matchByInstallName || anImage->matchInstallPath() ) { + const char* installPath = anImage->getInstallPath(); + if ( installPath != NULL) { + if ( strcmp(path, installPath) == 0 ) { + // if we are looking for a dylib don't return something else + if ( !context.mustBeDylib || anImage->isDylib() ) + return anImage; + } + } + } + // an install name starting with @rpath should match by install name, not just real path + if ( (orgPath[0] == '@') && (strncmp(orgPath, "@rpath/", 7) == 0) ) { + const char* installPath = anImage->getInstallPath(); + if ( installPath != NULL) { + if ( !context.mustBeDylib || anImage->isDylib() ) { + if ( strcmp(orgPath, installPath) == 0 ) + return anImage; + } + } + } + } + + //dyld::log("%s(%s) => NULL\n", __func__, path); + return NULL; +} + + +// open or check existing +static ImageLoader* loadPhase5(const char* path, const char* orgPath, const LoadContext& context, unsigned& cacheIndex, std::vector* exceptions) +{ + //dyld::log("%s(%s, %p)\n", __func__ , path, exceptions); + + // check for specific dylib overrides + for (std::vector::iterator it = sDylibOverrides.begin(); it != sDylibOverrides.end(); ++it) { + if ( strcmp(it->installName, path) == 0 ) { + path = it->override; + break; + } + } + + if ( exceptions != NULL ) + return loadPhase5load(path, orgPath, context, cacheIndex, exceptions); + else + return loadPhase5check(path, orgPath, context); +} + +// try with and without image suffix +static ImageLoader* loadPhase4(const char* path, const char* orgPath, const LoadContext& context, unsigned& cacheIndex, std::vector* exceptions) +{ + //dyld::log("%s(%s, %p)\n", __func__ , path, exceptions); + ImageLoader* image = NULL; + if ( gLinkContext.imageSuffix != NULL ) { + char pathWithSuffix[strlen(path)+strlen( gLinkContext.imageSuffix)+2]; + ImageLoader::addSuffix(path, gLinkContext.imageSuffix, pathWithSuffix); + image = loadPhase5(pathWithSuffix, orgPath, context, cacheIndex, exceptions); + } + if ( image == NULL ) + image = loadPhase5(path, orgPath, context, cacheIndex, exceptions); + return image; +} + +static ImageLoader* loadPhase2(const char* path, const char* orgPath, const LoadContext& context, + const char* const frameworkPaths[], const char* const libraryPaths[], + unsigned& cacheIndex, std::vector* exceptions); // forward reference + + +// expand @ variables +static ImageLoader* loadPhase3(const char* path, const char* orgPath, const LoadContext& context, unsigned& cacheIndex, std::vector* exceptions) +{ + //dyld::log("%s(%s, %p)\n", __func__ , path, exceptions); + ImageLoader* image = NULL; + if ( strncmp(path, "@executable_path/", 17) == 0 ) { +#if __MAC_OS_X_VERSION_MIN_REQUIRED + // executable_path cannot be in used in any binary in a setuid process rdar://problem/4589305 + if ( gLinkContext.processIsRestricted ) + throwf("unsafe use of @executable_path in %s with restricted binary", context.origin); +#endif + // handle @executable_path path prefix + const char* executablePath = sExecPath; + char newPath[strlen(executablePath) + strlen(path)]; + strcpy(newPath, executablePath); + char* addPoint = strrchr(newPath,'/'); + if ( addPoint != NULL ) + strcpy(&addPoint[1], &path[17]); + else + strcpy(newPath, &path[17]); + image = loadPhase4(newPath, orgPath, context, cacheIndex, exceptions); + if ( image != NULL ) + return image; + + // perhaps main executable path is a sym link, find realpath and retry + char resolvedPath[PATH_MAX]; + if ( realpath(sExecPath, resolvedPath) != NULL ) { + char newRealPath[strlen(resolvedPath) + strlen(path)]; + strcpy(newRealPath, resolvedPath); + addPoint = strrchr(newRealPath,'/'); + if ( addPoint != NULL ) + strcpy(&addPoint[1], &path[17]); + else + strcpy(newRealPath, &path[17]); + image = loadPhase4(newRealPath, orgPath, context, cacheIndex, exceptions); + if ( image != NULL ) + return image; + } + } + else if ( (strncmp(path, "@loader_path/", 13) == 0) && (context.origin != NULL) ) { +#if __MAC_OS_X_VERSION_MIN_REQUIRED + // @loader_path cannot be used from the main executable of a setuid process rdar://problem/4589305 + if ( gLinkContext.processIsRestricted && (strcmp(context.origin, sExecPath) == 0) ) + throwf("unsafe use of @loader_path in %s with restricted binary", context.origin); +#endif + // handle @loader_path path prefix + char newPath[strlen(context.origin) + strlen(path)]; + strcpy(newPath, context.origin); + char* addPoint = strrchr(newPath,'/'); + if ( addPoint != NULL ) + strcpy(&addPoint[1], &path[13]); + else + strcpy(newPath, &path[13]); + image = loadPhase4(newPath, orgPath, context, cacheIndex, exceptions); + if ( image != NULL ) + return image; + + // perhaps loader path is a sym link, find realpath and retry + char resolvedPath[PATH_MAX]; + if ( realpath(context.origin, resolvedPath) != NULL ) { + char newRealPath[strlen(resolvedPath) + strlen(path)]; + strcpy(newRealPath, resolvedPath); + addPoint = strrchr(newRealPath,'/'); + if ( addPoint != NULL ) + strcpy(&addPoint[1], &path[13]); + else + strcpy(newRealPath, &path[13]); + image = loadPhase4(newRealPath, orgPath, context, cacheIndex, exceptions); + if ( image != NULL ) + return image; + } + } + else if ( context.implicitRPath || (strncmp(path, "@rpath/", 7) == 0) ) { + const char* trailingPath = (strncmp(path, "@rpath/", 7) == 0) ? &path[7] : path; + // substitute @rpath with all -rpath paths up the load chain + for(const ImageLoader::RPathChain* rp=context.rpath; rp != NULL; rp=rp->next) { + if (rp->paths != NULL ) { + for(std::vector::iterator it=rp->paths->begin(); it != rp->paths->end(); ++it) { + const char* anRPath = *it; + char newPath[strlen(anRPath) + strlen(trailingPath)+2]; + strcpy(newPath, anRPath); + if ( newPath[strlen(newPath)-1] != '/' ) + strcat(newPath, "/"); + strcat(newPath, trailingPath); + image = loadPhase4(newPath, orgPath, context, cacheIndex, exceptions); + if ( gLinkContext.verboseRPaths && (exceptions != NULL) ) { + if ( image != NULL ) + dyld::log("RPATH successful expansion of %s to: %s\n", orgPath, newPath); + else + dyld::log("RPATH failed to expanding %s to: %s\n", orgPath, newPath); + } + if ( image != NULL ) + return image; + } + } + } + + // substitute @rpath with LD_LIBRARY_PATH + if ( sEnv.LD_LIBRARY_PATH != NULL ) { + image = loadPhase2(trailingPath, orgPath, context, NULL, sEnv.LD_LIBRARY_PATH, cacheIndex, exceptions); + if ( image != NULL ) + return image; + } + + // if this is the "open" pass, don't try to open @rpath/... as a relative path + if ( (exceptions != NULL) && (trailingPath != path) ) + return NULL; + } +#if __MAC_OS_X_VERSION_MIN_REQUIRED + else if ( gLinkContext.processIsRestricted && (path[0] != '/' ) ) { + throwf("unsafe use of relative rpath %s in %s with restricted binary", path, context.origin); + } +#endif + + return loadPhase4(path, orgPath, context, cacheIndex, exceptions); +} + + +// try search paths +static ImageLoader* loadPhase2(const char* path, const char* orgPath, const LoadContext& context, + const char* const frameworkPaths[], const char* const libraryPaths[], + unsigned& cacheIndex, std::vector* exceptions) +{ + //dyld::log("%s(%s, %p)\n", __func__ , path, exceptions); + ImageLoader* image = NULL; + const char* frameworkPartialPath = getFrameworkPartialPath(path); + if ( frameworkPaths != NULL ) { + if ( frameworkPartialPath != NULL ) { + const size_t frameworkPartialPathLen = strlen(frameworkPartialPath); + for(const char* const* fp = frameworkPaths; *fp != NULL; ++fp) { + char npath[strlen(*fp)+frameworkPartialPathLen+8]; + strcpy(npath, *fp); + strcat(npath, "/"); + strcat(npath, frameworkPartialPath); + //dyld::log("dyld: fallback framework path used: %s() -> loadPhase4(\"%s\", ...)\n", __func__, npath); + image = loadPhase4(npath, orgPath, context, cacheIndex, exceptions); + if ( image != NULL ) + return image; + } + } + } + // An executable with the same name as a framework & DYLD_LIBRARY_PATH pointing to it gets loaded twice + // Some apps depend on frameworks being found via library paths + if ( (libraryPaths != NULL) && ((frameworkPartialPath == NULL) || sFrameworksFoundAsDylibs) ) { + const char* libraryLeafName = getLibraryLeafName(path); + const size_t libraryLeafNameLen = strlen(libraryLeafName); + for(const char* const* lp = libraryPaths; *lp != NULL; ++lp) { + char libpath[strlen(*lp)+libraryLeafNameLen+8]; + strcpy(libpath, *lp); + strcat(libpath, "/"); + strcat(libpath, libraryLeafName); + //dyld::log("dyld: fallback library path used: %s() -> loadPhase4(\"%s\", ...)\n", __func__, libpath); + image = loadPhase4(libpath, orgPath, context, cacheIndex, exceptions); + if ( image != NULL ) + return image; + } + } + return NULL; +} + +// try search overrides and fallbacks +static ImageLoader* loadPhase1(const char* path, const char* orgPath, const LoadContext& context, unsigned& cacheIndex, std::vector* exceptions) +{ + //dyld::log("%s(%s, %p)\n", __func__ , path, exceptions); + ImageLoader* image = NULL; + + // handle LD_LIBRARY_PATH environment variables that force searching + if ( context.useLdLibraryPath && (sEnv.LD_LIBRARY_PATH != NULL) ) { + image = loadPhase2(path, orgPath, context, NULL, sEnv.LD_LIBRARY_PATH, cacheIndex,exceptions); + if ( image != NULL ) + return image; + } + + // handle DYLD_ environment variables that force searching + if ( context.useSearchPaths && ((sEnv.DYLD_FRAMEWORK_PATH != NULL) || (sEnv.DYLD_LIBRARY_PATH != NULL)) ) { + image = loadPhase2(path, orgPath, context, sEnv.DYLD_FRAMEWORK_PATH, sEnv.DYLD_LIBRARY_PATH, cacheIndex, exceptions); + if ( image != NULL ) + return image; + } + + // try raw path + image = loadPhase3(path, orgPath, context, cacheIndex, exceptions); + if ( image != NULL ) + return image; + + // try fallback paths during second time (will open file) + const char* const* fallbackLibraryPaths = sEnv.DYLD_FALLBACK_LIBRARY_PATH; + if ( (fallbackLibraryPaths != NULL) && !context.useFallbackPaths ) + fallbackLibraryPaths = NULL; + if ( !context.dontLoad && (exceptions != NULL) && ((sEnv.DYLD_FALLBACK_FRAMEWORK_PATH != NULL) || (fallbackLibraryPaths != NULL)) ) { + image = loadPhase2(path, orgPath, context, sEnv.DYLD_FALLBACK_FRAMEWORK_PATH, fallbackLibraryPaths, cacheIndex, exceptions); + if ( image != NULL ) + return image; + } + + return NULL; +} + +// try root substitutions +static ImageLoader* loadPhase0(const char* path, const char* orgPath, const LoadContext& context, unsigned& cacheIndex, std::vector* exceptions) +{ + //dyld::log("%s(%s, %p)\n", __func__ , path, exceptions); + +#if SUPPORT_ROOT_PATH + // handle DYLD_ROOT_PATH which forces absolute paths to use a new root + if ( (gLinkContext.rootPaths != NULL) && (path[0] == '/') ) { + for(const char* const* rootPath = gLinkContext.rootPaths ; *rootPath != NULL; ++rootPath) { + char newPath[strlen(*rootPath) + strlen(path)+2]; + strcpy(newPath, *rootPath); + strcat(newPath, path); + ImageLoader* image = loadPhase1(newPath, orgPath, context, cacheIndex, exceptions); + if ( image != NULL ) + return image; + } + } +#endif + + // try raw path + return loadPhase1(path, orgPath, context, cacheIndex, exceptions); +} + +#if DYLD_SHARED_CACHE_SUPPORT + static bool cacheablePath(const char* path) { + if (strncmp(path, "/usr/lib/", 9) == 0) + return true; + if (strncmp(path, "/System/Library/", 16) == 0) + return true; + return false; + } +#endif + +// +// Given all the DYLD_ environment variables, the general case for loading libraries +// is that any given path expands into a list of possible locations to load. We +// also must take care to ensure two copies of the "same" library are never loaded. +// +// The algorithm used here is that there is a separate function for each "phase" of the +// path expansion. Each phase function calls the next phase with each possible expansion +// of that phase. The result is the last phase is called with all possible paths. +// +// To catch duplicates the algorithm is run twice. The first time, the last phase checks +// the path against all loaded images. The second time, the last phase calls open() on +// the path. Either time, if an image is found, the phases all unwind without checking +// for other paths. +// +ImageLoader* load(const char* path, const LoadContext& context, unsigned& cacheIndex) +{ + CRSetCrashLogMessage2(path); + const char* orgPath = path; + cacheIndex = UINT32_MAX; + + //dyld::log("%s(%s)\n", __func__ , path); + char realPath[PATH_MAX]; + // when DYLD_IMAGE_SUFFIX is in used, do a realpath(), otherwise a load of "Foo.framework/Foo" will not match + if ( context.useSearchPaths && ( gLinkContext.imageSuffix != NULL) ) { + if ( realpath(path, realPath) != NULL ) + path = realPath; + } + + // try all path permutations and check against existing loaded images + + ImageLoader* image = loadPhase0(path, orgPath, context, cacheIndex, NULL); + if ( image != NULL ) { + CRSetCrashLogMessage2(NULL); + return image; + } + + // try all path permutations and try open() until first success + std::vector exceptions; + image = loadPhase0(path, orgPath, context, cacheIndex, &exceptions); +#if __IPHONE_OS_VERSION_MIN_REQUIRED && DYLD_SHARED_CACHE_SUPPORT && !TARGET_IPHONE_SIMULATOR + // support symlinks on disk to a path in dyld shared cache + if ( (image == NULL) && cacheablePath(path) && !context.dontLoad ) { + char resolvedPath[PATH_MAX]; + realpath(path, resolvedPath); + int myerr = errno; + // If realpath() resolves to a path which does not exist on disk, errno is set to ENOENT + if ( (myerr == ENOENT) || (myerr == 0) ) + { + // see if this image is in shared cache + const macho_header* mhInCache; + const char* pathInCache; + long slideInCache; + if ( findInSharedCacheImage(resolvedPath, false, NULL, &mhInCache, &pathInCache, &slideInCache) ) { + struct stat stat_buf; + bzero(&stat_buf, sizeof(stat_buf)); + try { + image = ImageLoaderMachO::instantiateFromCache(mhInCache, pathInCache, slideInCache, stat_buf, gLinkContext); + image = checkandAddImage(image, context); + } + catch (...) { + image = NULL; + } + } + } + } +#endif + CRSetCrashLogMessage2(NULL); + if ( image != NULL ) { + // leak in dyld during dlopen when using DYLD_ variables + for (std::vector::iterator it = exceptions.begin(); it != exceptions.end(); ++it) { + free((void*)(*it)); + } +#if DYLD_SHARED_CACHE_SUPPORT + // if loaded image is not from cache, but original path is in cache + // set gSharedCacheOverridden flag to disable some ObjC optimizations + if ( !gSharedCacheOverridden && !image->inSharedCache() && image->isDylib() && cacheablePath(path) && inSharedCache(path) ) { + gSharedCacheOverridden = true; + } +#endif + return image; + } + else if ( exceptions.size() == 0 ) { + if ( context.dontLoad ) { + return NULL; + } + else + throw "image not found"; + } + else { + const char* msgStart = "no suitable image found. Did find:"; + const char* delim = "\n\t"; + size_t allsizes = strlen(msgStart)+8; + for (size_t i=0; i < exceptions.size(); ++i) + allsizes += (strlen(exceptions[i]) + strlen(delim)); + char* fullMsg = new char[allsizes]; + strcpy(fullMsg, msgStart); + for (size_t i=0; i < exceptions.size(); ++i) { + strcat(fullMsg, delim); + strcat(fullMsg, exceptions[i]); + free((void*)exceptions[i]); + } + throw (const char*)fullMsg; + } +} + + + +#if DYLD_SHARED_CACHE_SUPPORT + + + +#if __i386__ + #define ARCH_NAME "i386" + #define ARCH_CACHE_MAGIC "dyld_v1 i386" +#elif __x86_64__ + #define ARCH_NAME "x86_64" + #define ARCH_CACHE_MAGIC "dyld_v1 x86_64" + #define ARCH_NAME_H "x86_64h" + #define ARCH_CACHE_MAGIC_H "dyld_v1 x86_64h" +#elif __ARM_ARCH_5TEJ__ + #define ARCH_NAME "armv5" + #define ARCH_CACHE_MAGIC "dyld_v1 armv5" +#elif __ARM_ARCH_6K__ + #define ARCH_NAME "armv6" + #define ARCH_CACHE_MAGIC "dyld_v1 armv6" +#elif __ARM_ARCH_7F__ + #define ARCH_NAME "armv7f" + #define ARCH_CACHE_MAGIC "dyld_v1 armv7f" +#elif __ARM_ARCH_7K__ + #define ARCH_NAME "armv7k" + #define ARCH_CACHE_MAGIC "dyld_v1 armv7k" +#elif __ARM_ARCH_7A__ + #define ARCH_NAME "armv7" + #define ARCH_CACHE_MAGIC "dyld_v1 armv7" +#elif __ARM_ARCH_7S__ + #define ARCH_NAME "armv7s" + #define ARCH_CACHE_MAGIC "dyld_v1 armv7s" +#elif __arm64__ + #define ARCH_NAME "arm64" + #define ARCH_CACHE_MAGIC "dyld_v1 arm64" +#endif + + +static int __attribute__((noinline)) _shared_region_check_np(uint64_t* start_address) +{ + if ( gLinkContext.sharedRegionMode == ImageLoader::kUseSharedRegion ) + return syscall(294, start_address); + return -1; +} + + +static void rebaseChain(uint8_t* pageContent, uint16_t startOffset, uintptr_t slideAmount, const dyld_cache_slide_info2* slideInfo) +{ + const uintptr_t deltaMask = (uintptr_t)(slideInfo->delta_mask); + const uintptr_t valueMask = ~deltaMask; + const uintptr_t valueAdd = (uintptr_t)(slideInfo->value_add); + const unsigned deltaShift = __builtin_ctzll(deltaMask) - 2; + + uint32_t pageOffset = startOffset; + uint32_t delta = 1; + while ( delta != 0 ) { + uint8_t* loc = pageContent + pageOffset; + uintptr_t rawValue = *((uintptr_t*)loc); + delta = (uint32_t)((rawValue & deltaMask) >> deltaShift); + uintptr_t value = (rawValue & valueMask); + if ( value != 0 ) { + value += valueAdd; + value += slideAmount; + } + *((uintptr_t*)loc) = value; + //dyld::log(" pageOffset=0x%03X, loc=%p, org value=0x%08llX, new value=0x%08llX, delta=0x%X\n", pageOffset, loc, (uint64_t)rawValue, (uint64_t)value, delta); + pageOffset += delta; + } +} + + +static void loadAndCheckCodeSignature(int fd, uint32_t count, const shared_file_mapping_np mappings[], + off_t codeSignatureOffset, size_t codeSignatureSize, + const void *firstPages, size_t firstPagesSize) +{ + // register code signature blob for whole dyld cache + fsignatures_t siginfo; + siginfo.fs_file_start = 0; // cache always starts at beginning of file + siginfo.fs_blob_start = (void*)codeSignatureOffset; + siginfo.fs_blob_size = codeSignatureSize; + + int result = fcntl(fd, F_ADDFILESIGS_RETURN, &siginfo); + // don't warn in chrooted case because mapping syscall is about to fail too + if ( result == -1 ) { +#if __IPHONE_OS_VERSION_MIN_REQUIRED + throwf("code signature registration for shared cache failed with errno=%d\n", errno); +#else + if ( gLinkContext.verboseMapping ) + dyld::log("dyld: code signature registration for shared cache failed with errno=%d\n", errno); +#endif + } + uint64_t codeSignedLength = siginfo.fs_file_start; + for (uint32_t i = 0; i < count; ++i) { + if ( (mappings[i].sfm_size > codeSignedLength) || (mappings[i].sfm_file_offset > (codeSignedLength - mappings[i].sfm_size)) ) + throw "dyld shared cache mapping not covered by code signature"; + } + + void *fdata = xmmap(NULL, firstPagesSize, PROT_READ|PROT_EXEC, MAP_PRIVATE, fd, 0); + if ( fdata == MAP_FAILED ) + throwf("mmap() errno=%d validating first page of shared cache", errno); + if ( memcmp(fdata, firstPages, firstPagesSize) != 0 ) + throwf("mmap() page compare failed for shared cache"); + munmap(fdata, firstPagesSize); +} + +static int __attribute__((noinline)) _shared_region_map_and_slide_np(int fd, uint32_t count, const shared_file_mapping_np mappings[], + long slide, void* slideInfo, unsigned long slideInfoSize) +{ + if ( gLinkContext.sharedRegionMode == ImageLoader::kUseSharedRegion ) { + return syscall(438, fd, count, mappings, slide, slideInfo, slideInfoSize); + } + + // remove the shared region sub-map + vm_deallocate(mach_task_self(), (vm_address_t)SHARED_REGION_BASE, SHARED_REGION_SIZE); + + // notify gdb or other lurkers that this process is no longer using the shared region + dyld::gProcessInfo->processDetachedFromSharedRegion = true; + + // map cache just for this process with mmap() + const shared_file_mapping_np* const start = mappings; + const shared_file_mapping_np* const end = &mappings[count]; + for (const shared_file_mapping_np* p = start; p < end; ++p ) { + void* mmapAddress = (void*)(uintptr_t)(p->sfm_address); + size_t size = p->sfm_size; + //dyld::log("dyld: mapping address %p with size 0x%08lX\n", mmapAddress, size); + int protection = 0; + if ( p->sfm_init_prot & VM_PROT_EXECUTE ) + protection |= PROT_EXEC; + if ( p->sfm_init_prot & VM_PROT_READ ) + protection |= PROT_READ; + if ( p->sfm_init_prot & VM_PROT_WRITE ) + protection |= PROT_WRITE; + off_t offset = p->sfm_file_offset; + if ( mmap(mmapAddress, size, protection, MAP_FIXED | MAP_PRIVATE, fd, offset) != mmapAddress ) { + // failed to map some chunk of this shared cache file + // clear shared region + vm_deallocate(mach_task_self(), (vm_address_t)SHARED_REGION_BASE, SHARED_REGION_SIZE); + // go back to not using shared region at all + gLinkContext.sharedRegionMode = ImageLoader::kDontUseSharedRegion; + if ( gLinkContext.verboseMapping ) { + dyld::log("dyld: shared cached region cannot be mapped at address %p with size 0x%08lX\n", + mmapAddress, size); + } + // return failure + return -1; + } + } + + // update all __DATA pages with slide info + const dyld_cache_slide_info* slideInfoHeader = (dyld_cache_slide_info*)slideInfo; + if ( slideInfoHeader->version == 2 ) { + const dyld_cache_slide_info2* slideHeader = (dyld_cache_slide_info2*)slideInfo; + const uint32_t page_size = slideHeader->page_size; + const uint16_t* page_starts = (uint16_t*)((long)(slideInfo) + slideHeader->page_starts_offset); + const uint16_t* page_extras = (uint16_t*)((long)(slideInfo) + slideHeader->page_extras_offset); + const uintptr_t dataPagesStart = mappings[1].sfm_address; + for (int i=0; i < slideHeader->page_starts_count; ++i) { + uint8_t* page = (uint8_t*)(long)(dataPagesStart + (page_size*i)); + uint16_t pageEntry = page_starts[i]; + //dyld::log("page[%d]: page_starts[i]=0x%04X\n", i, pageEntry); + if ( pageEntry == DYLD_CACHE_SLIDE_PAGE_ATTR_NO_REBASE ) + continue; + if ( pageEntry & DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA ) { + uint16_t chainIndex = (pageEntry & 0x3FFF); + bool done = false; + while ( !done ) { + uint16_t info = page_extras[chainIndex]; + uint16_t pageStartOffset = (info & 0x3FFF)*4; + //dyld::log(" chain[%d] pageOffset=0x%03X\n", chainIndex, pageStartOffset); + rebaseChain(page, pageStartOffset, slide, slideHeader); + done = (info & DYLD_CACHE_SLIDE_PAGE_ATTR_END); + ++chainIndex; + } + } + else { + uint32_t pageOffset = pageEntry * 4; + //dyld::log(" start pageOffset=0x%03X\n", pageOffset); + rebaseChain(page, pageOffset, slide, slideHeader); + } + } + } + else if ( slide != 0 ) { + const uintptr_t dataPagesStart = mappings[1].sfm_address; + const uint16_t* toc = (uint16_t*)((long)(slideInfoHeader) + slideInfoHeader->toc_offset); + const uint8_t* entries = (uint8_t*)((long)(slideInfoHeader) + slideInfoHeader->entries_offset); + for(uint32_t i=0; i < slideInfoHeader->toc_count; ++i) { + const uint8_t* entry = &entries[toc[i]*slideInfoHeader->entries_size]; + const uint8_t* page = (uint8_t*)(long)(dataPagesStart + (4096*i)); + //dyld::log("page=%p toc[%d]=%d entries=%p\n", page, i, toc[i], entry); + for(int j=0; j < 128; ++j) { + uint8_t b = entry[j]; + //dyld::log(" entry[%d] = 0x%02X\n", j, b); + if ( b != 0 ) { + for(int k=0; k < 8; ++k) { + if ( b & (1< highAddress ) + highAddress = mappings[i].sfm_address + mappings[i].sfm_size; + } + } +} + +static long pickCacheSlide(uint32_t mappingsCount, shared_file_mapping_np mappings[]) +{ +#if __x86_64__ + // x86_64 has a two memory regions: + // 256MB at 0x00007FFF70000000 + // 1024MB at 0x00007FFF80000000 + // Some old shared caches have r/w region after rx region, so all regions slide within 1GB range + // Newer shared caches have r/w region based at 0x7FFF70000000 and r/o regions at 0x7FFF80000000, so each part has max slide + if ( (mappingsCount >= 3) && (mappings[1].sfm_init_prot == (VM_PROT_READ|VM_PROT_WRITE)) && (mappings[1].sfm_address == 0x00007FFF70000000) ) { + const uint64_t rwSize = mappings[1].sfm_size; + const uint64_t rwSlop = 0x10000000ULL - rwSize; + const uint64_t roSize = (mappings[2].sfm_address + mappings[2].sfm_size) - mappings[0].sfm_address; + const uint64_t roSlop = 0x40000000ULL - roSize; + const uint64_t space = (rwSlop < roSlop) ? rwSlop : roSlop; + + // choose new random slide + long slide = (arc4random() % space) & (-4096); + //dyld::log("rwSlop=0x%0llX, roSlop=0x%0llX\n", rwSlop, roSlop); + //dyld::log("space=0x%0llX, slide=0x%0lX\n", space, slide); + + // update mappings + for(uint32_t i=0; i < mappingsCount; ++i) { + mappings[i].sfm_address += slide; + } + + return slide; + } + // else fall through to handle old style cache +#endif + // get bounds of cache + uint64_t lowAddress; + uint64_t highAddress; + getCacheBounds(mappingsCount, mappings, lowAddress, highAddress); + + // find slop space + const uint64_t space = (SHARED_REGION_BASE + SHARED_REGION_SIZE) - highAddress; + + // choose new random slide +#if __arm__ + // change shared cache slide for 32-bit arm to always be 16k aligned + long slide = ((arc4random() % space) & (-16384)); +#else + long slide = dyld_page_trunc(arc4random() % space); +#endif + //dyld::log("slideSpace=0x%0llX\n", space); + //dyld::log("slide=0x%0lX\n", slide); + + // update mappings + for(uint32_t i=0; i < mappingsCount; ++i) { + mappings[i].sfm_address += slide; + } + + return slide; +} + +static void mapSharedCache() +{ + uint64_t cacheBaseAddress = 0; + // quick check if a cache is already mapped into shared region + if ( _shared_region_check_np(&cacheBaseAddress) == 0 ) { + sSharedCache = (dyld_cache_header*)cacheBaseAddress; + // if we don't understand the currently mapped shared cache, then ignore +#if __x86_64__ + const char* magic = (sHaswell ? ARCH_CACHE_MAGIC_H : ARCH_CACHE_MAGIC); +#else + const char* magic = ARCH_CACHE_MAGIC; +#endif + if ( strcmp(sSharedCache->magic, magic) != 0 ) { + sSharedCache = NULL; + if ( gLinkContext.verboseMapping ) { + dyld::log("dyld: existing shared cached in memory is not compatible\n"); + return; + } + } + // check if cache file is slidable + const dyld_cache_header* header = sSharedCache; + if ( (header->mappingOffset >= 0x48) && (header->slideInfoSize != 0) ) { + // solve for slide by comparing loaded address to address of first region + const uint8_t* loadedAddress = (uint8_t*)sSharedCache; + const dyld_cache_mapping_info* const mappings = (dyld_cache_mapping_info*)(loadedAddress+header->mappingOffset); + const uint8_t* preferedLoadAddress = (uint8_t*)(long)(mappings[0].address); + sSharedCacheSlide = loadedAddress - preferedLoadAddress; + dyld::gProcessInfo->sharedCacheSlide = sSharedCacheSlide; + dyld::gProcessInfo->sharedCacheBaseAddress = cacheBaseAddress; + //dyld::log("sSharedCacheSlide=0x%08lX, loadedAddress=%p, preferedLoadAddress=%p\n", sSharedCacheSlide, loadedAddress, preferedLoadAddress); + } + // if cache has a uuid, copy it + if ( header->mappingOffset >= 0x68 ) { + memcpy(dyld::gProcessInfo->sharedCacheUUID, header->uuid, 16); + } + // verbose logging + if ( gLinkContext.verboseMapping ) { + dyld::log("dyld: re-using existing %s shared cache mapping\n", (header->cacheType == kDyldSharedCacheTypeDevelopment ? "development" : "production")); + } + if (header->mappingOffset >= 0x68) { + dyld_kernel_image_info_t kernelCacheInfo; + memcpy(&kernelCacheInfo.uuid[0], &sSharedCache->uuid[0], sizeof(uuid_t)); + kernelCacheInfo.load_addr = (uint64_t)sSharedCache; + kernelCacheInfo.fsobjid.fid_objno = 0; + kernelCacheInfo.fsobjid.fid_generation = 0; + kernelCacheInfo.fsid.val[0] = 0; + kernelCacheInfo.fsid.val[0] = 0; +#if 0 + task_register_dyld_shared_cache_image_info(mach_task_self(), kernelCacheInfo, false, false); +#endif + } + } + else { +#if __i386__ || __x86_64__ + // Safe Boot should disable dyld shared cache + // if we are in safe-boot mode and the cache was not made during this boot cycle, + // delete the cache file + uint32_t safeBootValue = 0; + size_t safeBootValueSize = sizeof(safeBootValue); + if ( (sysctlbyname("kern.safeboot", &safeBootValue, &safeBootValueSize, NULL, 0) == 0) && (safeBootValue != 0) ) { + // user booted machine in safe-boot mode + struct stat dyldCacheStatInfo; + // Don't use custom DYLD_SHARED_CACHE_DIR if provided, use standard path + if ( my_stat(MACOSX_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME, &dyldCacheStatInfo) == 0 ) { + struct timeval bootTimeValue; + size_t bootTimeValueSize = sizeof(bootTimeValue); + if ( (sysctlbyname("kern.boottime", &bootTimeValue, &bootTimeValueSize, NULL, 0) == 0) && (bootTimeValue.tv_sec != 0) ) { + // if the cache file was created before this boot, then throw it away and let it rebuild itself + if ( dyldCacheStatInfo.st_mtime < bootTimeValue.tv_sec ) { + ::unlink(MACOSX_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME); + gLinkContext.sharedRegionMode = ImageLoader::kDontUseSharedRegion; + return; + } + } + } + } +#endif + // map in shared cache to shared region + int fd = openSharedCacheFile(); + if ( fd != -1 ) { + uint8_t firstPages[8192]; + if ( ::read(fd, firstPages, 8192) == 8192 ) { + dyld_cache_header* header = (dyld_cache_header*)firstPages; + #if __x86_64__ + const char* magic = (sHaswell ? ARCH_CACHE_MAGIC_H : ARCH_CACHE_MAGIC); + #else + const char* magic = ARCH_CACHE_MAGIC; + #endif + if ( strcmp(header->magic, magic) == 0 ) { + const dyld_cache_mapping_info* const fileMappingsStart = (dyld_cache_mapping_info*)&firstPages[header->mappingOffset]; + const dyld_cache_mapping_info* const fileMappingsEnd = &fileMappingsStart[header->mappingCount]; + #if __IPHONE_OS_VERSION_MIN_REQUIRED + if ( (header->mappingCount != 3) + || (header->mappingOffset > 256) + || (fileMappingsStart[0].fileOffset != 0) + || (fileMappingsStart[0].address != SHARED_REGION_BASE) + || ((fileMappingsStart[0].address + fileMappingsStart[0].size) > fileMappingsStart[1].address) + || ((fileMappingsStart[1].address + fileMappingsStart[1].size) > fileMappingsStart[2].address) + || ((fileMappingsStart[0].fileOffset + fileMappingsStart[0].size) != fileMappingsStart[1].fileOffset) + || ((fileMappingsStart[1].fileOffset + fileMappingsStart[1].size) != fileMappingsStart[2].fileOffset) ) + throw "dyld shared cache file is invalid"; + #endif + shared_file_mapping_np mappings[header->mappingCount]; + unsigned int mappingCount = header->mappingCount; + int readWriteMappingIndex = -1; + int readOnlyMappingIndex = -1; + // validate that the cache file has not been truncated + bool goodCache = false; + struct stat stat_buf; + if ( fstat(fd, &stat_buf) == 0 ) { + goodCache = true; + int i=0; + for (const dyld_cache_mapping_info* p = fileMappingsStart; p < fileMappingsEnd; ++p, ++i) { + mappings[i].sfm_address = p->address; + mappings[i].sfm_size = p->size; + mappings[i].sfm_file_offset = p->fileOffset; + mappings[i].sfm_max_prot = p->maxProt; + mappings[i].sfm_init_prot = p->initProt; + // rdar://problem/5694507 old update_dyld_shared_cache tool could make a cache file + // that is not page aligned, but otherwise ok. + if ( p->fileOffset+p->size > (uint64_t)(stat_buf.st_size+4095 & (-4096)) ) { + dyld::log("dyld: shared cached file is corrupt: %s" DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME "\n", sSharedCacheDir); + goodCache = false; + } + if ( (mappings[i].sfm_init_prot & (VM_PROT_READ|VM_PROT_WRITE)) == (VM_PROT_READ|VM_PROT_WRITE) ) { + readWriteMappingIndex = i; + } + if ( mappings[i].sfm_init_prot == VM_PROT_READ ) { + readOnlyMappingIndex = i; + } + } + // if shared cache is code signed, add a mapping for the code signature + uint64_t signatureSize = header->codeSignatureSize; + // zero size in header means signature runs to end-of-file + if ( signatureSize == 0 ) + signatureSize = stat_buf.st_size - header->codeSignatureOffset; + if ( signatureSize != 0 ) { +#if __arm__ || __arm64__ + size_t alignedSignatureSize = (signatureSize+16383) & (-16384); +#else + size_t alignedSignatureSize = (signatureSize+4095) & (-4096); +#endif + // validate code signature covers entire shared cache + loadAndCheckCodeSignature(fd, mappingCount, mappings, header->codeSignatureOffset, alignedSignatureSize, firstPages, sizeof(firstPages)); + } +#if __IPHONE_OS_VERSION_MIN_REQUIRED + else { + throw "dyld shared cache file not code signed"; + } +#endif + } +#if __MAC_OS_X_VERSION_MIN_REQUIRED + // sanity check that /usr/lib/libSystem.B.dylib stat() info matches cache + if ( header->imagesCount * sizeof(dyld_cache_image_info) + header->imagesOffset < 8192 ) { + bool foundLibSystem = false; + if ( my_stat("/usr/lib/libSystem.B.dylib", &stat_buf) == 0 ) { + const dyld_cache_image_info* images = (dyld_cache_image_info*)&firstPages[header->imagesOffset]; + const dyld_cache_image_info* const imagesEnd = &images[header->imagesCount]; + for (const dyld_cache_image_info* p = images; p < imagesEnd; ++p) { + if ( ((time_t)p->modTime == stat_buf.st_mtime) && ((ino_t)p->inode == stat_buf.st_ino) ) { + foundLibSystem = true; + break; + } + } + } + if ( !sSharedCacheIgnoreInodeAndTimeStamp && !foundLibSystem ) { + dyld::log("dyld: shared cached file was built against a different libSystem.dylib, ignoring cache.\n" + "to update dyld shared cache run: 'sudo update_dyld_shared_cache' then reboot.\n"); + goodCache = false; + } + } +#endif +#if __IPHONE_OS_VERSION_MIN_REQUIRED + { + uint64_t lowAddress; + uint64_t highAddress; + getCacheBounds(mappingCount, mappings, lowAddress, highAddress); + if ( (highAddress-lowAddress) > SHARED_REGION_SIZE ) + throw "dyld shared cache is too big to fit in shared region"; + } +#endif + + if ( goodCache && (readWriteMappingIndex == -1) ) { + dyld::log("dyld: shared cached file is missing read/write mapping: %s" DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME "\n", sSharedCacheDir); + goodCache = false; + } + if ( goodCache && (readOnlyMappingIndex == -1) ) { + dyld::log("dyld: shared cached file is missing read-only mapping: %s" DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME "\n", sSharedCacheDir); + goodCache = false; + } + if ( goodCache ) { + long cacheSlide = 0; + void* slideInfo = (void*)(long)(mappings[readOnlyMappingIndex].sfm_address + (header->slideInfoOffset - mappings[readOnlyMappingIndex].sfm_file_offset));; + uint64_t slideInfoSize = header->slideInfoSize; + // check if shared cache contains slid info + if ( slideInfoSize != 0 ) { + // don't slide shared cache if ASLR disabled (main executable didn't slide) + if ( sMainExecutable->isPositionIndependentExecutable() && (sMainExecutable->getSlide() == 0) ) { + cacheSlide = 0; + } + else { + // generate random slide amount + cacheSlide = pickCacheSlide(mappingCount, mappings); + } + + slideInfo = (void*)((uint8_t*)slideInfo + cacheSlide); + // add VM_PROT_SLIDE bit to __DATA area of cache + mappings[readWriteMappingIndex].sfm_max_prot |= VM_PROT_SLIDE; + mappings[readWriteMappingIndex].sfm_init_prot |= VM_PROT_SLIDE; + } + if ( gLinkContext.verboseMapping ) { + dyld::log("dyld: calling _shared_region_map_and_slide_np() with regions:\n"); + for (int i=0; i < mappingCount; ++i) { + dyld::log(" address=0x%08llX, size=0x%08llX, fileOffset=0x%08llX\n", mappings[i].sfm_address, mappings[i].sfm_size, mappings[i].sfm_file_offset); + } + } + + if (_shared_region_map_and_slide_np(fd, mappingCount, mappings, cacheSlide, slideInfo, slideInfoSize) == 0) { + // successfully mapped cache into shared region + sSharedCache = (dyld_cache_header*)mappings[0].sfm_address; + sSharedCacheSlide = cacheSlide; + dyld::gProcessInfo->sharedCacheSlide = cacheSlide; + dyld::gProcessInfo->sharedCacheBaseAddress = mappings[0].sfm_address; + //dyld::log("sSharedCache=%p sSharedCacheSlide=0x%08lX\n", sSharedCache, sSharedCacheSlide); + // if cache has a uuid, copy it + if ( header->mappingOffset >= 0x68 ) { + const bool privateSharedCache = gLinkContext.sharedRegionMode == ImageLoader::kUsePrivateSharedRegion; + memcpy(dyld::gProcessInfo->sharedCacheUUID, header->uuid, 16); + dyld_kernel_image_info_t kernelCacheInfo; + memcpy(&kernelCacheInfo.uuid[0], &sSharedCache->uuid[0], sizeof(uuid_t)); + kernelCacheInfo.load_addr = (uint64_t)sSharedCache; + kernelCacheInfo.fsobjid.fid_objno = 0; + kernelCacheInfo.fsobjid.fid_generation = 0; + kernelCacheInfo.fsid.val[0] = 0; + kernelCacheInfo.fsid.val[0] = 0; + if (privateSharedCache) { + kernelCacheInfo.fsobjid = *(fsobj_id_t*)(&stat_buf.st_ino); + struct statfs statfs_buf; + if ( fstatfs(fd, &statfs_buf) == 0 ) { + kernelCacheInfo.fsid = statfs_buf.f_fsid; + } + } +#if 0 + task_register_dyld_shared_cache_image_info(mach_task_self(), kernelCacheInfo, false, privateSharedCache); +#endif + } + } + else { +#if __IPHONE_OS_VERSION_MIN_REQUIRED + throwf("dyld shared cache could not be mapped. errno=%d, slide=0x%08lX, slideInfo=%p, slideInfoSize=0x%08llX, mappingCount=%u, " + "address/size/off/init/max [0]=0x%0llX/0x%0llX/0x%0llX/0x%02X/0x%02X, [1]=0x%0llX/0x%0llX/0x%0llX/0x%02X/0x%02X, [2]=0x%0llX/0x%0llX/0x%0llX/0x%02X/0x%02X", + errno, cacheSlide, slideInfo, slideInfoSize, mappingCount, + mappings[0].sfm_address, mappings[0].sfm_size, mappings[0].sfm_file_offset, mappings[0].sfm_init_prot, mappings[0].sfm_max_prot, + mappings[1].sfm_address, mappings[1].sfm_size, mappings[1].sfm_file_offset, mappings[1].sfm_init_prot, mappings[1].sfm_max_prot, + mappings[2].sfm_address, mappings[2].sfm_size, mappings[2].sfm_file_offset, mappings[2].sfm_init_prot, mappings[2].sfm_max_prot); +#endif + if ( gLinkContext.verboseMapping ) + dyld::log("dyld: shared cached file could not be mapped\n"); + } + } + } + else { + if ( gLinkContext.verboseMapping ) + dyld::log("dyld: shared cached file is invalid\n"); + } + } + else { + if ( gLinkContext.verboseMapping ) + dyld::log("dyld: shared cached file cannot be read\n"); + } + close(fd); + } + else { + if ( gLinkContext.verboseMapping ) + dyld::log("dyld: shared cached file cannot be opened\n"); + } + } + + // remember if dyld loaded at same address as when cache built + if ( sSharedCache != NULL ) { + gLinkContext.dyldLoadedAtSameAddressNeededBySharedCache = ((uintptr_t)(sSharedCache->dyldBaseAddress) == (uintptr_t)&_mh_dylinker_header); + } + + // tell gdb where the shared cache is + if ( sSharedCache != NULL ) { + const dyld_cache_mapping_info* const start = (dyld_cache_mapping_info*)((uint8_t*)sSharedCache + sSharedCache->mappingOffset); + dyld_shared_cache_ranges.sharedRegionsCount = sSharedCache->mappingCount; + // only room to tell gdb about first four regions + if ( dyld_shared_cache_ranges.sharedRegionsCount > 4 ) + dyld_shared_cache_ranges.sharedRegionsCount = 4; + const dyld_cache_mapping_info* const end = &start[dyld_shared_cache_ranges.sharedRegionsCount]; + int index = 0; + for (const dyld_cache_mapping_info* p = start; p < end; ++p, ++index ) { + dyld_shared_cache_ranges.ranges[index].start = p->address+sSharedCacheSlide; + dyld_shared_cache_ranges.ranges[index].length = p->size; + if ( gLinkContext.verboseMapping ) { + dyld::log(" 0x%08llX->0x%08llX %s%s%s init=%x, max=%x\n", + p->address+sSharedCacheSlide, p->address+sSharedCacheSlide+p->size-1, + ((p->initProt & VM_PROT_READ) ? "read " : ""), + ((p->initProt & VM_PROT_WRITE) ? "write " : ""), + ((p->initProt & VM_PROT_EXECUTE) ? "execute " : ""), p->initProt, p->maxProt); + } + #if __i386__ + // If a non-writable and executable region is found in the R/W shared region, then this is __IMPORT segments + // This is an old cache. Make writable. dyld no longer supports turn W on and off as it binds + if ( (p->initProt == (VM_PROT_READ|VM_PROT_EXECUTE)) && ((p->address & 0xF0000000) == 0xA0000000) ) { + if ( p->size != 0 ) { + vm_prot_t prot = VM_PROT_EXECUTE | PROT_READ | VM_PROT_WRITE; + vm_protect(mach_task_self(), p->address, p->size, false, prot); + if ( gLinkContext.verboseMapping ) { + dyld::log("%18s at 0x%08llX->0x%08llX altered permissions to %c%c%c\n", "", p->address, + p->address+p->size-1, + (prot & PROT_READ) ? 'r' : '.', (prot & PROT_WRITE) ? 'w' : '.', (prot & PROT_EXEC) ? 'x' : '.' ); + } + } + } + #endif + } + if ( gLinkContext.verboseMapping ) { + // list the code blob + dyld_cache_header* header = (dyld_cache_header*)sSharedCache; + uint64_t signatureSize = header->codeSignatureSize; + // zero size in header means signature runs to end-of-file + if ( signatureSize == 0 ) { + struct stat stat_buf; + // FIXME: need size of cache file actually used + if ( my_stat(IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME, &stat_buf) == 0 ) + signatureSize = stat_buf.st_size - header->codeSignatureOffset; + } + if ( signatureSize != 0 ) { + const dyld_cache_mapping_info* const last = &start[dyld_shared_cache_ranges.sharedRegionsCount-1]; + uint64_t codeBlobStart = last->address + last->size; + dyld::log(" 0x%08llX->0x%08llX (code signature)\n", codeBlobStart, codeBlobStart+signatureSize); + } + } + #if SUPPORT_ACCELERATE_TABLES + if ( !dylibsCanOverrideCache() && !sDisableAcceleratorTables && (sSharedCache->mappingOffset > 0x80) && (sSharedCache->accelerateInfoAddr != 0) ) { + sAllCacheImagesProxy = ImageLoaderMegaDylib::makeImageLoaderMegaDylib(sSharedCache, sSharedCacheSlide, gLinkContext); + } + #endif + } +} +#endif // #if DYLD_SHARED_CACHE_SUPPORT + + + +// create when NSLinkModule is called for a second time on a bundle +ImageLoader* cloneImage(ImageLoader* image) +{ + // open file (automagically closed when this function exits) + FileOpener file(image->getPath()); + + struct stat stat_buf; + if ( fstat(file.getFileDescriptor(), &stat_buf) == -1) + throw "stat error"; + + dyld::LoadContext context; + context.useSearchPaths = false; + context.useFallbackPaths = false; + context.useLdLibraryPath = false; + context.implicitRPath = false; + context.matchByInstallName = false; + context.dontLoad = false; + context.mustBeBundle = true; + context.mustBeDylib = false; + context.canBePIE = false; + context.origin = NULL; + context.rpath = NULL; + return loadPhase6(file.getFileDescriptor(), stat_buf, image->getPath(), context); +} + + +ImageLoader* loadFromMemory(const uint8_t* mem, uint64_t len, const char* moduleName) +{ + // if fat wrapper, find usable sub-file + const fat_header* memStartAsFat = (fat_header*)mem; + uint64_t fileOffset = 0; + uint64_t fileLength = len; + if ( memStartAsFat->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) { + if ( fatFindBest(memStartAsFat, &fileOffset, &fileLength) ) { + mem = &mem[fileOffset]; + len = fileLength; + } + else { + throw "no matching architecture in universal wrapper"; + } + } + + // try each loader + if ( isCompatibleMachO(mem, moduleName) ) { + ImageLoader* image = ImageLoaderMachO::instantiateFromMemory(moduleName, (macho_header*)mem, len, gLinkContext); + // don't add bundles to global list, they can be loaded but not linked. When linked it will be added to list + if ( ! image->isBundle() ) + addImage(image); + return image; + } + + // try other file formats here... + + // throw error about what was found + switch (*(uint32_t*)mem) { + case MH_MAGIC: + case MH_CIGAM: + case MH_MAGIC_64: + case MH_CIGAM_64: + throw "mach-o, but wrong architecture"; + default: + throwf("unknown file type, first eight bytes: 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X", + mem[0], mem[1], mem[2], mem[3], mem[4], mem[5], mem[6],mem[7]); + } +} + + +void registerAddCallback(ImageCallback func) +{ + // now add to list to get notified when any more images are added + sAddImageCallbacks.push_back(func); + + // call callback with all existing images + for (std::vector::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) { + ImageLoader* image = *it; + if ( image->getState() >= dyld_image_state_bound && image->getState() < dyld_image_state_terminated ) + (*func)(image->machHeader(), image->getSlide()); + } +#if SUPPORT_ACCELERATE_TABLES + if ( sAllCacheImagesProxy != NULL ) { + dyld_image_info infos[allImagesCount()+1]; + unsigned cacheCount = sAllCacheImagesProxy->appendImagesToNotify(dyld_image_state_bound, true, infos); + for (unsigned i=0; i < cacheCount; ++i) { + (*func)(infos[i].imageLoadAddress, sSharedCacheSlide); + } + } +#endif +} + +void registerRemoveCallback(ImageCallback func) +{ + // ignore calls to register a notification during a notification + if ( sRemoveImageCallbacksInUse ) + return; + sRemoveImageCallbacks.push_back(func); +} + +void clearErrorMessage() +{ + error_string[0] = '\0'; +} + +void setErrorMessage(const char* message) +{ + // save off error message in global buffer for CrashReporter to find + strlcpy(error_string, message, sizeof(error_string)); +} + +const char* getErrorMessage() +{ + return error_string; +} + +void halt(const char* message) +{ + dyld::log("dyld: %s\n", message); + setErrorMessage(message); + dyld::gProcessInfo->errorMessage = error_string; + if ( !gLinkContext.startedInitializingMainExecutable ) + dyld::gProcessInfo->terminationFlags = 1; + else + dyld::gProcessInfo->terminationFlags = 0; +#if 0 + char payloadBuffer[EXIT_REASON_PAYLOAD_MAX_LEN]; + dyld_abort_payload* payload = (dyld_abort_payload*)payloadBuffer; + payload->version = 1; + payload->flags = gLinkContext.startedInitializingMainExecutable ? 0 : 1; + payload->targetDylibPathOffset = 0; + payload->clientPathOffset = 0; + payload->symbolOffset = 0; + int payloadSize = sizeof(dyld_abort_payload); + + if ( dyld::gProcessInfo->errorTargetDylibPath != NULL ) { + payload->targetDylibPathOffset = payloadSize; + payloadSize += strlcpy(&payloadBuffer[payloadSize], dyld::gProcessInfo->errorTargetDylibPath, sizeof(payloadBuffer)-payloadSize) + 1; + } + if ( dyld::gProcessInfo->errorClientOfDylibPath != NULL ) { + payload->clientPathOffset = payloadSize; + payloadSize += strlcpy(&payloadBuffer[payloadSize], dyld::gProcessInfo->errorClientOfDylibPath, sizeof(payloadBuffer)-payloadSize) + 1; + } + if ( dyld::gProcessInfo->errorSymbol != NULL ) { + payload->symbolOffset = payloadSize; + payloadSize += strlcpy(&payloadBuffer[payloadSize], dyld::gProcessInfo->errorSymbol, sizeof(payloadBuffer)-payloadSize) + 1; + } + char truncMessage[EXIT_REASON_USER_DESC_MAX_LEN]; + strlcpy(truncMessage, message, EXIT_REASON_USER_DESC_MAX_LEN); + abort_with_payload(OS_REASON_DYLD, dyld::gProcessInfo->errorKind ? dyld::gProcessInfo->errorKind : DYLD_EXIT_REASON_OTHER, payloadBuffer, payloadSize, truncMessage, 0); +#endif + abort(); +} + +static void setErrorStrings(unsigned errorCode, const char* errorClientOfDylibPath, + const char* errorTargetDylibPath, const char* errorSymbol) +{ + dyld::gProcessInfo->errorKind = errorCode; + dyld::gProcessInfo->errorClientOfDylibPath = errorClientOfDylibPath; + dyld::gProcessInfo->errorTargetDylibPath = errorTargetDylibPath; + dyld::gProcessInfo->errorSymbol = errorSymbol; +} + + +uintptr_t bindLazySymbol(const mach_header* mh, uintptr_t* lazyPointer) +{ + uintptr_t result = 0; + // acquire read-lock on dyld's data structures +#if 0 // rdar://problem/3811777 turn off locking until deadlock is resolved + if ( gLibSystemHelpers != NULL ) + (*gLibSystemHelpers->lockForReading)(); +#endif + // lookup and bind lazy pointer and get target address + try { + ImageLoader* target; + #if __i386__ + // fast stubs pass NULL for mh and image is instead found via the location of stub (aka lazyPointer) + if ( mh == NULL ) + target = dyld::findImageContainingAddress(lazyPointer); + else + target = dyld::findImageByMachHeader(mh); + #else + // note, target should always be mach-o, because only mach-o lazy handler wired up to this + target = dyld::findImageByMachHeader(mh); + #endif + if ( target == NULL ) + throwf("image not found for lazy pointer at %p", lazyPointer); + result = target->doBindLazySymbol(lazyPointer, gLinkContext); + } + catch (const char* message) { + dyld::log("dyld: lazy symbol binding failed: %s\n", message); + halt(message); + } + // release read-lock on dyld's data structures +#if 0 + if ( gLibSystemHelpers != NULL ) + (*gLibSystemHelpers->unlockForReading)(); +#endif + // return target address to glue which jumps to it with real parameters restored + return result; +} + + +uintptr_t fastBindLazySymbol(ImageLoader** imageLoaderCache, uintptr_t lazyBindingInfoOffset) +{ + uintptr_t result = 0; + // get image + if ( *imageLoaderCache == NULL ) { + // save in cache + *imageLoaderCache = dyld::findMappedRange((uintptr_t)imageLoaderCache); + if ( *imageLoaderCache == NULL ) { +#if SUPPORT_ACCELERATE_TABLES + if ( sAllCacheImagesProxy != NULL ) { + const mach_header* mh; + const char* path; + unsigned index; + if ( sAllCacheImagesProxy->addressInCache(imageLoaderCache, &mh, &path, &index) ) { + result = sAllCacheImagesProxy->bindLazy(lazyBindingInfoOffset, gLinkContext, mh, index); + if ( result == 0 ) { + halt("dyld: lazy symbol binding failed for image in dyld shared\n"); + } + return result; + } + } +#endif + const char* message = "fast lazy binding from unknown image"; + dyld::log("dyld: %s\n", message); + halt(message); + } + } + + // bind lazy pointer and return it + try { + result = (*imageLoaderCache)->doBindFastLazySymbol((uint32_t)lazyBindingInfoOffset, gLinkContext, + (dyld::gLibSystemHelpers != NULL) ? dyld::gLibSystemHelpers->acquireGlobalDyldLock : NULL, + (dyld::gLibSystemHelpers != NULL) ? dyld::gLibSystemHelpers->releaseGlobalDyldLock : NULL); + } + catch (const char* message) { + dyld::log("dyld: lazy symbol binding failed: %s\n", message); + halt(message); + } + + // return target address to glue which jumps to it with real parameters restored + return result; +} + + + +void registerUndefinedHandler(UndefinedHandler handler) +{ + sUndefinedHandler = handler; +} + +static void undefinedHandler(const char* symboName) +{ + if ( sUndefinedHandler != NULL ) { + (*sUndefinedHandler)(symboName); + } +} + +static bool findExportedSymbol(const char* name, bool onlyInCoalesced, const ImageLoader::Symbol** sym, const ImageLoader** image) +{ + // search all images in order + const ImageLoader* firstWeakImage = NULL; + const ImageLoader::Symbol* firstWeakSym = NULL; + const size_t imageCount = sAllImages.size(); + for(size_t i=0; i < imageCount; ++i) { + ImageLoader* anImage = sAllImages[i]; + // the use of inserted libraries alters search order + // so that inserted libraries are found before the main executable + if ( sInsertedDylibCount > 0 ) { + if ( i < sInsertedDylibCount ) + anImage = sAllImages[i+1]; + else if ( i == sInsertedDylibCount ) + anImage = sAllImages[0]; + } + if ( ! anImage->hasHiddenExports() && (!onlyInCoalesced || anImage->hasCoalescedExports()) ) { + *sym = anImage->findExportedSymbol(name, false, image); + if ( *sym != NULL ) { + // if weak definition found, record first one found + if ( ((*image)->getExportedSymbolInfo(*sym) & ImageLoader::kWeakDefinition) != 0 ) { + if ( firstWeakImage == NULL ) { + firstWeakImage = *image; + firstWeakSym = *sym; + } + } + else { + // found non-weak, so immediately return with it + return true; + } + } + } + } + if ( firstWeakSym != NULL ) { + // found a weak definition, but no non-weak, so return first weak found + *sym = firstWeakSym; + *image = firstWeakImage; + return true; + } +#if SUPPORT_ACCELERATE_TABLES + if ( sAllCacheImagesProxy != NULL ) { + if ( sAllCacheImagesProxy->flatFindSymbol(name, onlyInCoalesced, sym, image) ) + return true; + } +#endif + + return false; +} + +bool flatFindExportedSymbol(const char* name, const ImageLoader::Symbol** sym, const ImageLoader** image) +{ + return findExportedSymbol(name, false, sym, image); +} + +bool findCoalescedExportedSymbol(const char* name, const ImageLoader::Symbol** sym, const ImageLoader** image) +{ + return findExportedSymbol(name, true, sym, image); +} + + +bool flatFindExportedSymbolWithHint(const char* name, const char* librarySubstring, const ImageLoader::Symbol** sym, const ImageLoader** image) +{ + // search all images in order + const size_t imageCount = sAllImages.size(); + for(size_t i=0; i < imageCount; ++i){ + ImageLoader* anImage = sAllImages[i]; + // only look at images whose paths contain the hint string (NULL hint string is wildcard) + if ( ! anImage->isBundle() && ((librarySubstring==NULL) || (strstr(anImage->getPath(), librarySubstring) != NULL)) ) { + *sym = anImage->findExportedSymbol(name, false, image); + if ( *sym != NULL ) { + return true; + } + } + } + return false; +} + + +unsigned int getCoalescedImages(ImageLoader* images[], unsigned imageIndex[]) +{ + unsigned int count = 0; + for (std::vector::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) { + ImageLoader* image = *it; + if ( image->participatesInCoalescing() ) { + images[count] = *it; + imageIndex[count] = 0; + ++count; + } + } +#if SUPPORT_ACCELERATE_TABLES + if ( sAllCacheImagesProxy != NULL ) { + sAllCacheImagesProxy->appendImagesNeedingCoalescing(images, imageIndex, count); + } +#endif + return count; +} + + +static ImageLoader::MappedRegion* getMappedRegions(ImageLoader::MappedRegion* regions) +{ + ImageLoader::MappedRegion* end = regions; + for (std::vector::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) { + (*it)->getMappedRegions(end); + } + return end; +} + +void registerImageStateSingleChangeHandler(dyld_image_states state, dyld_image_state_change_handler handler) +{ + // mark the image that the handler is in as never-unload because dyld has a reference into it + ImageLoader* handlerImage = findImageContainingAddress((void*)handler); + if ( handlerImage != NULL ) + handlerImage->setNeverUnload(); + + // add to list of handlers + std::vector* handlers = stateToHandlers(state, sSingleHandlers); + if ( handlers != NULL ) { + // need updateAllImages() to be last in dyld_image_state_mapped list + // so that if ObjC adds a handler that prevents a load, it happens before the gdb list is updated + if ( state == dyld_image_state_mapped ) + handlers->insert(handlers->begin(), handler); + else + handlers->push_back(handler); + + // call callback with all existing images + for (std::vector::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) { + ImageLoader* image = *it; + dyld_image_info info; + info.imageLoadAddress = image->machHeader(); + info.imageFilePath = image->getRealPath(); + info.imageFileModDate = image->lastModified(); + // should only call handler if state == image->state + if ( image->getState() == state ) + (*handler)(state, 1, &info); + // ignore returned string, too late to do anything + } + } +} + +void registerImageStateBatchChangeHandler(dyld_image_states state, dyld_image_state_change_handler handler) +{ + // mark the image that the handler is in as never-unload because dyld has a reference into it + ImageLoader* handlerImage = findImageContainingAddress((void*)handler); + if ( handlerImage != NULL ) + handlerImage->setNeverUnload(); + + // add to list of handlers + std::vector* handlers = stateToHandlers(state, sBatchHandlers); + if ( handlers != NULL ) { + // insert at front, so that gdb handler is always last + handlers->insert(handlers->begin(), handler); + + // call callback with all existing images + try { + notifyBatchPartial(state, true, handler, false, false); + } + catch (const char* msg) { + // ignore request to abort during registration + } + } +} + + +void registerObjCNotifiers(_dyld_objc_notify_mapped mapped, _dyld_objc_notify_init init, _dyld_objc_notify_unmapped unmapped) +{ + // record functions to call + sNotifyObjCMapped = mapped; + sNotifyObjCInit = init; + sNotifyObjCUnmapped = unmapped; + + // call 'mapped' function with all images mapped so far + try { + notifyBatchPartial(dyld_image_state_bound, true, NULL, false, true); + } + catch (const char* msg) { + // ignore request to abort during registration + } +} + +bool sharedCacheUUID(uuid_t uuid) +{ +#if DYLD_SHARED_CACHE_SUPPORT + if ( sSharedCache == NULL ) + return false; + + memcpy(uuid, sSharedCache->uuid, 16); + return true; +#else + return false; +#endif +} + +#if SUPPORT_ACCELERATE_TABLES + +bool dlopenFromCache(const char* path, int mode, void** handle) +{ + if ( sAllCacheImagesProxy == NULL ) + return false; + bool result = sAllCacheImagesProxy->dlopenFromCache(gLinkContext, path, mode, handle); + if ( !result && (strchr(path, '/') == NULL) ) { + // POSIX says you can call dlopen() with a leaf name (e.g. dlopen("libz.dylb")) + char fallbackPath[PATH_MAX]; + strcpy(fallbackPath, "/usr/lib/"); + strlcat(fallbackPath, path, PATH_MAX); + result = sAllCacheImagesProxy->dlopenFromCache(gLinkContext, fallbackPath, mode, handle); + } + return result; +} + +bool makeCacheHandle(ImageLoader* image, unsigned cacheIndex, int mode, void** result) +{ + if ( sAllCacheImagesProxy == NULL ) + return false; + return sAllCacheImagesProxy->makeCacheHandle(gLinkContext, cacheIndex, mode, result); +} + +bool isCacheHandle(void* handle) +{ + if ( sAllCacheImagesProxy == NULL ) + return false; + return sAllCacheImagesProxy->isCacheHandle(handle, NULL, NULL); +} + +bool isPathInCache(const char* path) +{ + if ( sAllCacheImagesProxy == NULL ) + return false; + unsigned index; + return sAllCacheImagesProxy->hasDylib(path, &index); +} + +const char* getPathFromIndex(unsigned cacheIndex) +{ + if ( sAllCacheImagesProxy == NULL ) + return NULL; + return sAllCacheImagesProxy->getIndexedPath(cacheIndex); +} + +void* dlsymFromCache(void* handle, const char* symName, unsigned index) +{ + if ( sAllCacheImagesProxy == NULL ) + return NULL; + return sAllCacheImagesProxy->dlsymFromCache(gLinkContext, handle, symName, index); +} + +bool addressInCache(const void* address, const mach_header** mh, const char** path, unsigned* index) +{ + if ( sAllCacheImagesProxy == NULL ) + return false; + unsigned ignore; + return sAllCacheImagesProxy->addressInCache(address, mh, path, index ? index : &ignore); +} + +bool findUnwindSections(const void* addr, dyld_unwind_sections* info) +{ + if ( sAllCacheImagesProxy == NULL ) + return false; + return sAllCacheImagesProxy->findUnwindSections(addr, info); +} + +bool dladdrFromCache(const void* address, Dl_info* info) +{ + if ( sAllCacheImagesProxy == NULL ) + return false; + return sAllCacheImagesProxy->dladdrFromCache(address, info); +} +#endif + +static ImageLoader* libraryLocator(const char* libraryName, bool search, const char* origin, const ImageLoader::RPathChain* rpaths, unsigned& cacheIndex) +{ + dyld::LoadContext context; + context.useSearchPaths = search; + context.useFallbackPaths = search; + context.useLdLibraryPath = false; + context.implicitRPath = false; + context.matchByInstallName = false; + context.dontLoad = false; + context.mustBeBundle = false; + context.mustBeDylib = true; + context.canBePIE = false; + context.origin = origin; + context.rpath = rpaths; + return load(libraryName, context, cacheIndex); +} + +static const char* basename(const char* path) +{ + const char* last = path; + for (const char* s = path; *s != '\0'; s++) { + if (*s == '/') + last = s+1; + } + return last; +} + +static void setContext(const macho_header* mainExecutableMH, int argc, const char* argv[], const char* envp[], const char* apple[]) +{ + gLinkContext.loadLibrary = &libraryLocator; + gLinkContext.terminationRecorder = &terminationRecorder; + gLinkContext.flatExportFinder = &flatFindExportedSymbol; + gLinkContext.coalescedExportFinder = &findCoalescedExportedSymbol; + gLinkContext.getCoalescedImages = &getCoalescedImages; + gLinkContext.undefinedHandler = &undefinedHandler; + gLinkContext.getAllMappedRegions = &getMappedRegions; + gLinkContext.bindingHandler = NULL; + gLinkContext.notifySingle = ¬ifySingle; + gLinkContext.notifyBatch = ¬ifyBatch; + gLinkContext.removeImage = &removeImage; + gLinkContext.registerDOFs = ®isterDOFs; + gLinkContext.clearAllDepths = &clearAllDepths; + gLinkContext.printAllDepths = &printAllDepths; + gLinkContext.imageCount = &imageCount; + gLinkContext.setNewProgramVars = &setNewProgramVars; +#if DYLD_SHARED_CACHE_SUPPORT + gLinkContext.inSharedCache = &inSharedCache; +#endif + gLinkContext.setErrorStrings = &setErrorStrings; +#if SUPPORT_OLD_CRT_INITIALIZATION + gLinkContext.setRunInitialzersOldWay= &setRunInitialzersOldWay; +#endif + gLinkContext.findImageContainingAddress = &findImageContainingAddress; + gLinkContext.addDynamicReference = &addDynamicReference; +#if SUPPORT_ACCELERATE_TABLES + gLinkContext.notifySingleFromCache = ¬ifySingleFromCache; + gLinkContext.getPreInitNotifyHandler= &getPreInitNotifyHandler; + gLinkContext.getBoundBatchHandler = &getBoundBatchHandler; +#endif + gLinkContext.bindingOptions = ImageLoader::kBindingNone; + gLinkContext.argc = argc; + gLinkContext.argv = argv; + gLinkContext.envp = envp; + gLinkContext.apple = apple; + gLinkContext.progname = (argv[0] != NULL) ? basename(argv[0]) : ""; + gLinkContext.programVars.mh = mainExecutableMH; + gLinkContext.programVars.NXArgcPtr = &gLinkContext.argc; + gLinkContext.programVars.NXArgvPtr = &gLinkContext.argv; + gLinkContext.programVars.environPtr = &gLinkContext.envp; + gLinkContext.programVars.__prognamePtr=&gLinkContext.progname; + gLinkContext.mainExecutable = NULL; + gLinkContext.imageSuffix = NULL; + gLinkContext.dynamicInterposeArray = NULL; + gLinkContext.dynamicInterposeCount = 0; + gLinkContext.prebindUsage = ImageLoader::kUseAllPrebinding; +#if TARGET_IPHONE_SIMULATOR + gLinkContext.sharedRegionMode = ImageLoader::kDontUseSharedRegion; +#else + gLinkContext.sharedRegionMode = ImageLoader::kUseSharedRegion; +#endif +} + + + +// +// Look for a special segment in the mach header. +// Its presences means that the binary wants to have DYLD ignore +// DYLD_ environment variables. +// +static bool hasRestrictedSegment(const macho_header* mh) +{ + const uint32_t cmd_count = mh->ncmds; + const struct load_command* const cmds = (struct load_command*)(((char*)mh)+sizeof(macho_header)); + const struct load_command* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd) { + case LC_SEGMENT_COMMAND: + { + const struct macho_segment_command* seg = (struct macho_segment_command*)cmd; + + //dyld::log("seg name: %s\n", seg->segname); + if (strcmp(seg->segname, "__RESTRICT") == 0) { + const struct macho_section* const sectionsStart = (struct macho_section*)((char*)seg + sizeof(struct macho_segment_command)); + const struct macho_section* const sectionsEnd = §ionsStart[seg->nsects]; + for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) { + if (strcmp(sect->sectname, "__restrict") == 0) + return true; + } + } + } + break; + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + + return false; +} + +#if __IPHONE_OS_VERSION_MIN_REQUIRED +static bool isFairPlayEncrypted(const macho_header* mh) +{ + const uint32_t cmd_count = mh->ncmds; + const struct load_command* const cmds = (struct load_command*)(((char*)mh)+sizeof(macho_header)); + const struct load_command* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + if ( cmd->cmd == LC_ENCRYPT_COMMAND ) { + const encryption_info_command* enc = (encryption_info_command*)cmd; + return (enc->cryptid != 0); + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + + return false; +} +#endif + +#if SUPPORT_VERSIONED_PATHS + +static bool readFirstPage(const char* dylibPath, uint8_t firstPage[4096]) +{ + firstPage[0] = 0; + // open file (automagically closed when this function exits) + FileOpener file(dylibPath); + + if ( file.getFileDescriptor() == -1 ) + return false; + + if ( pread(file.getFileDescriptor(), firstPage, 4096, 0) != 4096 ) + return false; + + // if fat wrapper, find usable sub-file + const fat_header* fileStartAsFat = (fat_header*)firstPage; + if ( fileStartAsFat->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) { + uint64_t fileOffset; + uint64_t fileLength; + if ( fatFindBest(fileStartAsFat, &fileOffset, &fileLength) ) { + if ( pread(file.getFileDescriptor(), firstPage, 4096, fileOffset) != 4096 ) + return false; + } + else { + return false; + } + } + + return true; +} + +// +// Peeks at a dylib file and returns its current_version and install_name. +// Returns false on error. +// +static bool getDylibVersionAndInstallname(const char* dylibPath, uint32_t* version, char* installName) +{ + uint8_t firstPage[4096]; + const macho_header* mh = (macho_header*)firstPage; + if ( !readFirstPage(dylibPath, firstPage) ) { + #if DYLD_SHARED_CACHE_SUPPORT + // If file cannot be read, check to see if path is in shared cache + const macho_header* mhInCache; + const char* pathInCache; + long slideInCache; + if ( !findInSharedCacheImage(dylibPath, true, NULL, &mhInCache, &pathInCache, &slideInCache) ) + return false; + mh = mhInCache; + #else + return false; + #endif + } + + // check mach-o header + if ( mh->magic != sMainExecutableMachHeader->magic ) + return false; + if ( mh->cputype != sMainExecutableMachHeader->cputype ) + return false; + + // scan load commands for LC_ID_DYLIB + const uint32_t cmd_count = mh->ncmds; + const struct load_command* const cmds = (struct load_command*)(((char*)mh)+sizeof(macho_header)); + const struct load_command* const cmdsReadEnd = (struct load_command*)(((char*)mh)+4096); + const struct load_command* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd) { + case LC_ID_DYLIB: + { + const struct dylib_command* id = (struct dylib_command*)cmd; + *version = id->dylib.current_version; + if ( installName != NULL ) + strlcpy(installName, (char *)id + id->dylib.name.offset, PATH_MAX); + return true; + } + break; + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + if ( cmd > cmdsReadEnd ) + return false; + } + + return false; +} +#endif // SUPPORT_VERSIONED_PATHS + + +#if 0 +static void printAllImages() +{ + dyld::log("printAllImages()\n"); + for (std::vector::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) { + ImageLoader* image = *it; + dyld_image_states imageState = image->getState(); + dyld::log(" state=%d, dlopen-count=%d, never-unload=%d, in-use=%d, name=%s\n", + imageState, image->dlopenCount(), image->neverUnload(), image->isMarkedInUse(), image->getShortName()); + } +} +#endif + +void link(ImageLoader* image, bool forceLazysBound, bool neverUnload, const ImageLoader::RPathChain& loaderRPaths, unsigned cacheIndex) +{ + // add to list of known images. This did not happen at creation time for bundles + if ( image->isBundle() && !image->isLinked() ) + addImage(image); + + // we detect root images as those not linked in yet + if ( !image->isLinked() ) + addRootImage(image); + + // process images + try { + const char* path = image->getPath(); +#if SUPPORT_ACCELERATE_TABLES + if ( image == sAllCacheImagesProxy ) + path = sAllCacheImagesProxy->getIndexedPath(cacheIndex); +#endif + image->link(gLinkContext, forceLazysBound, false, neverUnload, loaderRPaths, path); + } + catch (const char* msg) { + garbageCollectImages(); + throw; + } +} + + +void runInitializers(ImageLoader* image) +{ + // do bottom up initialization + ImageLoader::InitializerTimingList initializerTimes[allImagesCount()]; + initializerTimes[0].count = 0; + image->runInitializers(gLinkContext, initializerTimes[0]); +} + +// This function is called at the end of dlclose() when the reference count goes to zero. +// The dylib being unloaded may have brought in other dependent dylibs when it was loaded. +// Those dependent dylibs need to be unloaded, but only if they are not referenced by +// something else. We use a standard mark and sweep garbage collection. +// +// The tricky part is that when a dylib is unloaded it may have a termination function that +// can run and itself call dlclose() on yet another dylib. The problem is that this +// sort of gabage collection is not re-entrant. Instead a terminator's call to dlclose() +// which calls garbageCollectImages() will just set a flag to re-do the garbage collection +// when the current pass is done. +// +// Also note that this is done within the dyld global lock, so it is always single threaded. +// +void garbageCollectImages() +{ + static bool sDoingGC = false; + static bool sRedo = false; + + if ( sDoingGC ) { + // GC is currently being run, just set a flag to have it run again. + sRedo = true; + return; + } + + sDoingGC = true; + do { + sRedo = false; + + // mark phase: mark all images not-in-use + for (std::vector::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) { + ImageLoader* image = *it; + //dyld::log("gc: neverUnload=%d name=%s\n", image->neverUnload(), image->getShortName()); + image->markNotUsed(); + } + + // sweep phase: mark as in-use, images reachable from never-unload or in-use image + for (std::vector::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) { + ImageLoader* image = *it; + if ( (image->dlopenCount() != 0) || image->neverUnload() || (image == sMainExecutable) ) { + OSSpinLockLock(&sDynamicReferencesLock); + image->markedUsedRecursive(sDynamicReferences); + OSSpinLockUnlock(&sDynamicReferencesLock); + } + } + + // collect phase: build array of images not marked in-use + ImageLoader* deadImages[sAllImages.size()]; + unsigned deadCount = 0; + int maxRangeCount = 0; + for (std::vector::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) { + ImageLoader* image = *it; + if ( ! image->isMarkedInUse() ) { + deadImages[deadCount++] = image; + if (gLogAPIs) dyld::log("dlclose(), found unused image %p %s\n", image, image->getShortName()); + maxRangeCount += image->segmentCount(); + } + } + + // collect phase: run termination routines for images not marked in-use + __cxa_range_t ranges[maxRangeCount]; + int rangeCount = 0; + for (unsigned i=0; i < deadCount; ++i) { + ImageLoader* image = deadImages[i]; + for (unsigned int j=0; j < image->segmentCount(); ++j) { + if ( !image->segExecutable(j) ) + continue; + if ( rangeCount < maxRangeCount ) { + ranges[rangeCount].addr = (const void*)image->segActualLoadAddress(j); + ranges[rangeCount].length = image->segSize(j); + ++rangeCount; + } + } + try { + runImageStaticTerminators(image); + } + catch (const char* msg) { + dyld::warn("problem running terminators for image: %s\n", msg); + } + } + + // dyld should call __cxa_finalize_ranges() + if ( (rangeCount > 0) && (gLibSystemHelpers != NULL) && (gLibSystemHelpers->version >= 13) ) + (*gLibSystemHelpers->cxa_finalize_ranges)(ranges, rangeCount); + + // collect phase: delete all images which are not marked in-use + bool mightBeMore; + do { + mightBeMore = false; + for (std::vector::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) { + ImageLoader* image = *it; + if ( ! image->isMarkedInUse() ) { + try { + if (gLogAPIs) dyld::log("dlclose(), deleting %p %s\n", image, image->getShortName()); + removeImage(image); + ImageLoader::deleteImage(image); + mightBeMore = true; + break; // interator in invalidated by this removal + } + catch (const char* msg) { + dyld::warn("problem deleting image: %s\n", msg); + } + } + } + } while ( mightBeMore ); + } while (sRedo); + sDoingGC = false; + + //printAllImages(); + +} + + +static void preflight_finally(ImageLoader* image) +{ + if ( image->isBundle() ) { + removeImageFromAllImages(image->machHeader()); + ImageLoader::deleteImage(image); + } + sBundleBeingLoaded = NULL; + dyld::garbageCollectImages(); +} + + +void preflight(ImageLoader* image, const ImageLoader::RPathChain& loaderRPaths, unsigned cacheIndex) +{ + try { + if ( image->isBundle() ) + sBundleBeingLoaded = image; // hack + const char* path = image->getPath(); +#if SUPPORT_ACCELERATE_TABLES + if ( image == sAllCacheImagesProxy ) + path = sAllCacheImagesProxy->getIndexedPath(cacheIndex); +#endif + image->link(gLinkContext, false, true, false, loaderRPaths, path); + } + catch (const char* msg) { + preflight_finally(image); + throw; + } + preflight_finally(image); +} + +static void loadInsertedDylib(const char* path) +{ + ImageLoader* image = NULL; + unsigned cacheIndex; + try { + LoadContext context; + context.useSearchPaths = false; + context.useFallbackPaths = false; + context.useLdLibraryPath = false; + context.implicitRPath = false; + context.matchByInstallName = false; + context.dontLoad = false; + context.mustBeBundle = false; + context.mustBeDylib = true; + context.canBePIE = false; + context.origin = NULL; // can't use @loader_path with DYLD_INSERT_LIBRARIES + context.rpath = NULL; + image = load(path, context, cacheIndex); + } + catch (const char* msg) { +#if TARGET_IPHONE_SIMULATOR + dyld::log("dyld: warning: could not load inserted library '%s' because %s\n", path, msg); +#else +#if __MAC_OS_X_VERSION_MIN_REQUIRED + if ( gLinkContext.processUsingLibraryValidation ) + dyld::log("dyld: warning: could not load inserted library '%s' into library validated process because %s\n", path, msg); + else +#endif + halt(dyld::mkstringf("could not load inserted library '%s' because %s\n", path, msg)); +#endif + } + catch (...) { + halt(dyld::mkstringf("could not load inserted library '%s'\n", path)); + } +} + + +// +// Sets: +// sEnvMode +// gLinkContext.requireCodeSignature +// gLinkContext.processIsRestricted // Mac OS X only +// gLinkContext.processUsingLibraryValidation // Mac OS X only +// +static void configureProcessRestrictions(const macho_header* mainExecutableMH) +{ + uint32_t flags; +#if TARGET_IPHONE_SIMULATOR + sEnvMode = envAll; + gLinkContext.requireCodeSignature = true; +#elif __IPHONE_OS_VERSION_MIN_REQUIRED + sEnvMode = envNone; + gLinkContext.requireCodeSignature = true; + if ( csops(0, CS_OPS_STATUS, &flags, sizeof(flags)) != -1 ) { + if ( flags & CS_ENFORCEMENT ) { + if ( flags & CS_GET_TASK_ALLOW ) { + // Xcode built app for Debug allowed to use DYLD_* variables + sEnvMode = envAll; + } + else { + // Development kernel can use DYLD_PRINT_* variables on any FairPlay encrypted app + uint32_t secureValue = 0; + size_t secureValueSize = sizeof(secureValue); + if ( (sysctlbyname("kern.secure_kernel", &secureValue, &secureValueSize, NULL, 0) == 0) && (secureValue == 0) && isFairPlayEncrypted(mainExecutableMH) ) { + sEnvMode = envPrintOnly; + } + } + } + else { + // Development kernel can run unsigned code + sEnvMode = envAll; + gLinkContext.requireCodeSignature = false; + } + } + if ( issetugid() ) { + sEnvMode = envNone; + } +#elif __MAC_OS_X_VERSION_MIN_REQUIRED + sEnvMode = envAll; + gLinkContext.requireCodeSignature = false; + gLinkContext.processIsRestricted = false; + gLinkContext.processUsingLibraryValidation = false; + // any processes with setuid or setgid bit set or with __RESTRICT segment is restricted + if ( issetugid() || hasRestrictedSegment(mainExecutableMH) ) { + gLinkContext.processIsRestricted = true; + } + if ( csops(0, CS_OPS_STATUS, &flags, sizeof(flags)) != -1 ) { + // On OS X CS_RESTRICT means the program was signed with entitlements + if ( ((flags & CS_RESTRICT) == CS_RESTRICT) && (csr_check(CSR_ALLOW_TASK_FOR_PID) != 0) ) { + gLinkContext.processIsRestricted = true; + } + // Library Validation loosens searching but requires everything to be code signed + if ( flags & CS_REQUIRE_LV ) { + gLinkContext.processIsRestricted = false; + //gLinkContext.requireCodeSignature = true; + gLinkContext.processUsingLibraryValidation = true; + } + } +#endif +} + + +bool processIsRestricted() +{ +#if __MAC_OS_X_VERSION_MIN_REQUIRED + return gLinkContext.processIsRestricted; +#else + return false; +#endif +} + + +// Add dyld to uuidArray to enable symbolication of stackshots +static void addDyldImageToUUIDList() +{ + const struct macho_header* mh = (macho_header*)&__dso_handle; + const uint32_t cmd_count = mh->ncmds; + const struct load_command* const cmds = (struct load_command*)((char*)mh + sizeof(macho_header)); + const struct load_command* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd) { + case LC_UUID: { + uuid_command* uc = (uuid_command*)cmd; + dyld_uuid_info info; + info.imageLoadAddress = (mach_header*)mh; + memcpy(info.imageUUID, uc->uuid, 16); + addNonSharedCacheImageUUID(info); + return; + } + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } +} + +void notifyKernelAboutDyld() +{ + const struct macho_header* mh = (macho_header*)&__dso_handle; + const uint32_t cmd_count = mh->ncmds; + const struct load_command* const cmds = (struct load_command*)((char*)mh + sizeof(macho_header)); + const struct load_command* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd) { + case LC_UUID: { + // Add dyld to the kernel image info + uuid_command* uc = (uuid_command*)cmd; + dyld_kernel_image_info_t kernelInfo; + memcpy(kernelInfo.uuid, uc->uuid, 16); + kernelInfo.load_addr = (uint64_t)mh; + kernelInfo.fsobjid.fid_objno = 0; + kernelInfo.fsobjid.fid_generation = 0; + kernelInfo.fsid.val[0] = 0; + kernelInfo.fsid.val[1] = 0; +#if 0 + task_register_dyld_image_infos(mach_task_self(), &kernelInfo, 1); +#endif + return; + } + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } +} + +#if __MAC_OS_X_VERSION_MIN_REQUIRED +typedef int (*open_proc_t)(const char*, int, int); +typedef int (*fcntl_proc_t)(int, int, void*); +typedef int (*ioctl_proc_t)(int, unsigned long, void*); +static void* getProcessInfo() { return dyld::gProcessInfo; } +static SyscallHelpers sSysCalls = { + 7, + // added in version 1 + (open_proc_t)&open, + &close, + &pread, + &write, + &mmap, + &munmap, + &madvise, + &stat, + (fcntl_proc_t)&fcntl, + (ioctl_proc_t)&ioctl, + &issetugid, + &getcwd, + &realpath, + &vm_allocate, + &vm_deallocate, + &vm_protect, + &vlog, + &vwarn, + &pthread_mutex_lock, + &pthread_mutex_unlock, + &mach_thread_self, + &mach_port_deallocate, + &task_self_trap, + &mach_timebase_info, + &OSAtomicCompareAndSwapPtrBarrier, + &OSMemoryBarrier, + &getProcessInfo, + &__error, + &mach_absolute_time, + // added in version 2 + &thread_switch, + // added in version 3 + &opendir, + &readdir_r, + &closedir, + // added in version 4 + NULL, //&coresymbolication_load_notifier, + NULL, //&coresymbolication_unload_notifier, + // Added in version 5 + &proc_regionfilename, + &getpid, + &mach_port_insert_right, + &mach_port_allocate, + &mach_msg, + // Added in version 6 + NULL, //&abort_with_payload, + // Added in version 7 + NULL, //&task_register_dyld_image_infos, + NULL, //&task_unregister_dyld_image_infos, + NULL, //&task_get_dyld_image_infos, + NULL, //&task_register_dyld_shared_cache_image_info, + NULL, //&task_register_dyld_set_dyld_state, + NULL, //&task_register_dyld_get_process_state +}; + +__attribute__((noinline)) +static const char* useSimulatorDyld(int fd, const macho_header* mainExecutableMH, const char* dyldPath, + int argc, const char* argv[], const char* envp[], const char* apple[], + uintptr_t* startGlue, uintptr_t* mainAddr) +{ + *startGlue = 0; + *mainAddr = 0; + + // simulator does not support restricted processes + uint32_t flags; + if ( csops(0, CS_OPS_STATUS, &flags, sizeof(flags)) == -1 ) + return "csops() failed"; + if ( (flags & CS_RESTRICT) == CS_RESTRICT ) + return "dyld_sim cannot be loaded in a restricted process"; + if ( issetugid() ) + return "dyld_sim cannot be loaded in a setuid process"; + if ( hasRestrictedSegment(mainExecutableMH) ) + return "dyld_sim cannot be loaded in a restricted process"; + + // get file size of dyld_sim + struct stat sb; + if ( fstat(fd, &sb) == -1 ) + return "stat(dyld_sim) failed"; + + // read first page of dyld_sim file + uint8_t firstPage[4096]; + if ( pread(fd, firstPage, 4096, 0) != 4096 ) + return "pread(dyld_sim) failed"; + + // if fat file, pick matching slice + uint64_t fileOffset = 0; + uint64_t fileLength = sb.st_size; + const fat_header* fileStartAsFat = (fat_header*)firstPage; + if ( fileStartAsFat->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) { + if ( !fatFindBest(fileStartAsFat, &fileOffset, &fileLength) ) + return "no matching arch in dyld_sim"; + // re-read buffer from start of mach-o slice in fat file + if ( pread(fd, firstPage, 4096, fileOffset) != 4096 ) + return "pread(dyld_sim) failed"; + } + else if ( !isCompatibleMachO(firstPage, dyldPath) ) { + return "dyld_sim not compatible mach-o"; + } + + // calculate total size of dyld segments + const macho_header* mh = (const macho_header*)firstPage; + struct macho_segment_command* lastSeg = NULL; + struct macho_segment_command* firstSeg = NULL; + uintptr_t mappingSize = 0; + uintptr_t preferredLoadAddress = 0; + const uint32_t cmd_count = mh->ncmds; + if ( mh->sizeofcmds > 4096 ) + return "dyld_sim load commands to large"; + if ( (sizeof(macho_header) + mh->sizeofcmds) > 4096 ) + return "dyld_sim load commands to large"; + const struct load_command* const cmds = (struct load_command*)(((char*)mh)+sizeof(macho_header)); + const struct load_command* const endCmds = (struct load_command*)(((char*)mh) + sizeof(macho_header) + mh->sizeofcmds); + const struct load_command* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + uint32_t cmdLength = cmd->cmdsize; + if ( cmdLength < 8 ) + return "dyld_sim load command too small"; + const struct load_command* const nextCmd = (const struct load_command*)(((char*)cmd)+cmdLength); + if ( (nextCmd > endCmds) || (nextCmd < cmd) ) + return "dyld_sim load command too large"; + switch (cmd->cmd) { + case LC_SEGMENT_COMMAND: + { + struct macho_segment_command* seg = (struct macho_segment_command*)cmd; + if ( seg->vmaddr + seg->vmsize < seg->vmaddr ) + return "dyld_sim seg wraps address space"; + if ( seg->vmsize < seg->filesize ) + return "dyld_sim seg vmsize too small"; + if ( (seg->fileoff + seg->filesize) < seg->fileoff ) + return "dyld_sim seg size wraps address space"; + if ( lastSeg == NULL ) { + // first segment must be __TEXT and start at beginning of file/slice + firstSeg = seg; + if ( strcmp(seg->segname, "__TEXT") != 0 ) + return "dyld_sim first segment not __TEXT"; + if ( seg->fileoff != 0 ) + return "dyld_sim first segment not at file offset zero"; + if ( seg->filesize < (sizeof(macho_header) + mh->sizeofcmds) ) + return "dyld_sim first segment smaller than load commands"; + preferredLoadAddress = seg->vmaddr; + } + else { + // other sements must be continguous with previous segment and not executable + if ( lastSeg->fileoff + lastSeg->filesize != seg->fileoff ) + return "dyld_sim segments not contiguous"; + if ( lastSeg->vmaddr + lastSeg->vmsize != seg->vmaddr ) + return "dyld_sim segments not address contiguous"; + if ( (seg->initprot & VM_PROT_EXECUTE) != 0 ) + return "dyld_sim non-first segment is executable"; + } + mappingSize += seg->vmsize; + lastSeg = seg; + } + break; + case LC_SEGMENT_COMMAND_WRONG: + return "dyld_sim wrong load segment load command"; + } + cmd = nextCmd; + } + // last segment must be named __LINKEDIT and not writable + if ( strcmp(lastSeg->segname, "__LINKEDIT") != 0 ) + return "dyld_sim last segment not __LINKEDIT"; + if ( lastSeg->initprot & VM_PROT_WRITE ) + return "dyld_sim __LINKEDIT segment writable"; + + // reserve space, then mmap each segment + vm_address_t loadAddress = 0; + if ( ::vm_allocate(mach_task_self(), &loadAddress, mappingSize, VM_FLAGS_ANYWHERE) != 0 ) + return "dyld_sim cannot allocate space"; + cmd = cmds; + struct linkedit_data_command* codeSigCmd = NULL; + struct source_version_command* dyldVersionCmd = NULL; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd) { + case LC_SEGMENT_COMMAND: + { + struct macho_segment_command* seg = (struct macho_segment_command*)cmd; + uintptr_t requestedLoadAddress = seg->vmaddr - preferredLoadAddress + loadAddress; + void* segAddress = ::mmap((void*)requestedLoadAddress, seg->filesize, seg->initprot, MAP_FIXED | MAP_PRIVATE, fd, fileOffset + seg->fileoff); + //dyld::log("dyld_sim %s mapped at %p\n", seg->segname, segAddress); + if ( segAddress == (void*)(-1) ) + return "dyld_sim mmap() of segment failed"; + if ( ((uintptr_t)segAddress < loadAddress) || ((uintptr_t)segAddress+seg->filesize > loadAddress+mappingSize) ) + return "dyld_sim mmap() to wrong location"; + } + break; + case LC_CODE_SIGNATURE: + codeSigCmd = (struct linkedit_data_command*)cmd; + break; + case LC_SOURCE_VERSION: + dyldVersionCmd = (struct source_version_command*)cmd; + break; + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + + // must have code signature which is contained within LINKEDIT segment + if ( codeSigCmd == NULL ) + return "dyld_sim not code signed"; + if ( codeSigCmd->dataoff < lastSeg->fileoff ) + return "dyld_sim code signature not in __LINKEDIT"; + if ( (codeSigCmd->dataoff + codeSigCmd->datasize) < codeSigCmd->dataoff ) + return "dyld_sim code signature size wraps"; + if ( (codeSigCmd->dataoff + codeSigCmd->datasize) > (lastSeg->fileoff + lastSeg->filesize) ) + return "dyld_sim code signature extends beyond __LINKEDIT"; + + fsignatures_t siginfo; + siginfo.fs_file_start=fileOffset; // start of mach-o slice in fat file + siginfo.fs_blob_start=(void*)(long)(codeSigCmd->dataoff); // start of code-signature in mach-o file + siginfo.fs_blob_size=codeSigCmd->datasize; // size of code-signature + int result = fcntl(fd, F_ADDFILESIGS_FOR_DYLD_SIM, &siginfo); + if ( result == -1 ) { + return mkstringf("dyld_sim fcntl(F_ADDFILESIGS_FOR_DYLD_SIM) failed with errno=%d", errno); + } + close(fd); + // file range covered by code signature must extend up to code signature itself + if ( siginfo.fs_file_start < codeSigCmd->dataoff ) + return mkstringf("dyld_sim code signature does not cover all of dyld_sim. Signature covers up to 0x%08lX. Signature starts at 0x%08X", (unsigned long)siginfo.fs_file_start, codeSigCmd->dataoff); + + + // walk newly mapped dyld_sim __TEXT load commands to find entry point + uintptr_t entry = 0; + cmd = (struct load_command*)(((char*)loadAddress)+sizeof(macho_header)); + const uint32_t count = ((macho_header*)(loadAddress))->ncmds; + for (uint32_t i = 0; i < count; ++i) { + if (cmd->cmd == LC_UNIXTHREAD) { + #if __i386__ + const i386_thread_state_t* registers = (i386_thread_state_t*)(((char*)cmd) + 16); + // entry point must be in first segment + if ( registers->__eip < firstSeg->vmaddr ) + return "dyld_sim entry point not in __TEXT segment"; + if ( registers->__eip > (firstSeg->vmaddr + firstSeg->vmsize) ) + return "dyld_sim entry point not in __TEXT segment"; + entry = (registers->__eip + loadAddress - preferredLoadAddress); + #elif __x86_64__ + const x86_thread_state64_t* registers = (x86_thread_state64_t*)(((char*)cmd) + 16); + // entry point must be in first segment + if ( registers->__rip < firstSeg->vmaddr ) + return "dyld_sim entry point not in __TEXT segment"; + if ( registers->__rip > (firstSeg->vmaddr + firstSeg->vmsize) ) + return "dyld_sim entry point not in __TEXT segment"; + entry = (registers->__rip + loadAddress - preferredLoadAddress); + #endif + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + + // notify debugger that dyld_sim is loaded + dyld_image_info info; + info.imageLoadAddress = (mach_header*)loadAddress; + info.imageFilePath = strdup(dyldPath); + info.imageFileModDate = sb.st_mtime; + addImagesToAllImages(1, &info); + dyld::gProcessInfo->notification(dyld_image_adding, 1, &info); + + const char** appleParams = apple; + // jump into new simulator dyld + typedef uintptr_t (*sim_entry_proc_t)(int argc, const char* argv[], const char* envp[], const char* apple[], + const macho_header* mainExecutableMH, const macho_header* dyldMH, uintptr_t dyldSlide, + const dyld::SyscallHelpers* vtable, uintptr_t* startGlue); + sim_entry_proc_t newDyld = (sim_entry_proc_t)entry; + *mainAddr = (*newDyld)(argc, argv, envp, appleParams, mainExecutableMH, (macho_header*)loadAddress, + loadAddress - preferredLoadAddress, + &sSysCalls, startGlue); + return NULL; +} +#endif + + +// +// Entry point for dyld. The kernel loads dyld and jumps to __dyld_start which +// sets up some registers and call this function. +// +// Returns address of main() in target program which __dyld_start jumps to +// +uintptr_t +_main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide, + int argc, const char* argv[], const char* envp[], const char* apple[], + uintptr_t* startGlue) +{ + uintptr_t result = 0; + sMainExecutableMachHeader = mainExecutableMH; +#if __MAC_OS_X_VERSION_MIN_REQUIRED + // if this is host dyld, check to see if iOS simulator is being run + const char* rootPath = _simple_getenv(envp, "DYLD_ROOT_PATH"); + if ( rootPath != NULL ) { + // Add dyld to the kernel image info before we jump to the sim + notifyKernelAboutDyld(); + + // look to see if simulator has its own dyld + char simDyldPath[PATH_MAX]; + strlcpy(simDyldPath, rootPath, PATH_MAX); + strlcat(simDyldPath, "/usr/lib/dyld_sim", PATH_MAX); + int fd = my_open(simDyldPath, O_RDONLY, 0); + if ( fd != -1 ) { + const char* errMessage = useSimulatorDyld(fd, mainExecutableMH, simDyldPath, argc, argv, envp, apple, startGlue, &result); + if ( errMessage != NULL ) + halt(errMessage); + return result; + } + } +#endif + + CRSetCrashLogMessage("dyld: launch started"); + + setContext(mainExecutableMH, argc, argv, envp, apple); + + // Pickup the pointer to the exec path. + sExecPath = _simple_getenv(apple, "executable_path"); + + // Remove interim apple[0] transition code from dyld + if (!sExecPath) sExecPath = apple[0]; + + if ( sExecPath[0] != '/' ) { + // have relative path, use cwd to make absolute + char cwdbuff[MAXPATHLEN]; + if ( getcwd(cwdbuff, MAXPATHLEN) != NULL ) { + // maybe use static buffer to avoid calling malloc so early... + char* s = new char[strlen(cwdbuff) + strlen(sExecPath) + 2]; + strcpy(s, cwdbuff); + strcat(s, "/"); + strcat(s, sExecPath); + sExecPath = s; + } + } + // Remember short name of process for later logging + sExecShortName = ::strrchr(sExecPath, '/'); + if ( sExecShortName != NULL ) + ++sExecShortName; + else + sExecShortName = sExecPath; + + configureProcessRestrictions(mainExecutableMH); + +#if __MAC_OS_X_VERSION_MIN_REQUIRED + if ( gLinkContext.processIsRestricted ) { + pruneEnvironmentVariables(envp, &apple); + // set again because envp and apple may have changed or moved + setContext(mainExecutableMH, argc, argv, envp, apple); + } + else +#endif + { + checkEnvironmentVariables(envp); + defaultUninitializedFallbackPaths(envp); + } + if ( sEnv.DYLD_PRINT_OPTS ) + printOptions(argv); + if ( sEnv.DYLD_PRINT_ENV ) + printEnvironmentVariables(envp); + getHostInfo(mainExecutableMH, mainExecutableSlide); + // install gdb notifier + stateToHandlers(dyld_image_state_dependents_mapped, sBatchHandlers)->push_back(notifyGDB); + stateToHandlers(dyld_image_state_mapped, sSingleHandlers)->push_back(updateAllImages); + // make initial allocations large enough that it is unlikely to need to be re-alloced + sImageRoots.reserve(16); + sAddImageCallbacks.reserve(4); + sRemoveImageCallbacks.reserve(4); + sImageFilesNeedingTermination.reserve(16); + sImageFilesNeedingDOFUnregistration.reserve(8); + +#if !TARGET_IPHONE_SIMULATOR +#ifdef WAIT_FOR_SYSTEM_ORDER_HANDSHAKE + // Add gating mechanism to dyld support system order file generation process + WAIT_FOR_SYSTEM_ORDER_HANDSHAKE(dyld::gProcessInfo->systemOrderFlag); +#endif +#endif + + + try { + // add dyld itself to UUID list + addDyldImageToUUIDList(); + notifyKernelAboutDyld(); + +#if SUPPORT_ACCELERATE_TABLES + bool mainExcutableAlreadyRebased = false; + +reloadAllImages: +#endif + + CRSetCrashLogMessage(sLoadingCrashMessage); + // instantiate ImageLoader for main executable + sMainExecutable = instantiateFromLoadedImage(mainExecutableMH, mainExecutableSlide, sExecPath); + gLinkContext.mainExecutable = sMainExecutable; + gLinkContext.mainExecutableCodeSigned = hasCodeSignatureLoadCommand(mainExecutableMH); + +#if TARGET_IPHONE_SIMULATOR + // check main executable is not too new for this OS + { + if ( ! isSimulatorBinary((uint8_t*)mainExecutableMH, sExecPath) ) { + throwf("program was built for a platform that is not supported by this runtime"); + } + uint32_t mainMinOS = sMainExecutable->minOSVersion(); + + // dyld is always built for the current OS, so we can get the current OS version + // from the load command in dyld itself. + uint32_t dyldMinOS = ImageLoaderMachO::minOSVersion((const mach_header*)&__dso_handle); + if ( mainMinOS > dyldMinOS ) { + #if TARGET_OS_WATCH + throwf("app was built for watchOS %d.%d which is newer than this simulator %d.%d", + mainMinOS >> 16, ((mainMinOS >> 8) & 0xFF), + dyldMinOS >> 16, ((dyldMinOS >> 8) & 0xFF)); + #elif TARGET_OS_TV + throwf("app was built for tvOS %d.%d which is newer than this simulator %d.%d", + mainMinOS >> 16, ((mainMinOS >> 8) & 0xFF), + dyldMinOS >> 16, ((dyldMinOS >> 8) & 0xFF)); + #else + throwf("app was built for iOS %d.%d which is newer than this simulator %d.%d", + mainMinOS >> 16, ((mainMinOS >> 8) & 0xFF), + dyldMinOS >> 16, ((dyldMinOS >> 8) & 0xFF)); + #endif + } + } +#endif + + + #if __MAC_OS_X_VERSION_MIN_REQUIRED + // be less strict about old mach-o binaries + uint32_t mainSDK = sMainExecutable->sdkVersion(); + gLinkContext.strictMachORequired = (mainSDK >= DYLD_MACOSX_VERSION_10_12) || gLinkContext.processUsingLibraryValidation; + #else + // simulators, iOS, tvOS, and watchOS are always strict + gLinkContext.strictMachORequired = true; + #endif + + // load shared cache + checkSharedRegionDisable(); + #if DYLD_SHARED_CACHE_SUPPORT + if ( gLinkContext.sharedRegionMode != ImageLoader::kDontUseSharedRegion ) { + mapSharedCache(); + } else { + dyld_kernel_image_info_t kernelCacheInfo; + bzero(&kernelCacheInfo.uuid[0], sizeof(uuid_t)); + kernelCacheInfo.load_addr = 0; + kernelCacheInfo.fsobjid.fid_objno = 0; + kernelCacheInfo.fsobjid.fid_generation = 0; + kernelCacheInfo.fsid.val[0] = 0; + kernelCacheInfo.fsid.val[0] = 0; +#if 0 + task_register_dyld_shared_cache_image_info(mach_task_self(), kernelCacheInfo, true, false); +#endif + } + #endif + + #if SUPPORT_ACCELERATE_TABLES + sAllImages.reserve((sAllCacheImagesProxy != NULL) ? 16 : INITIAL_IMAGE_COUNT); + #else + sAllImages.reserve(INITIAL_IMAGE_COUNT); + #endif + + // Now that shared cache is loaded, setup an versioned dylib overrides + #if SUPPORT_VERSIONED_PATHS + checkVersionedPaths(); + #endif + + + // dyld_all_image_infos image list does not contain dyld + // add it as dyldPath field in dyld_all_image_infos + // for simulator, dyld_sim is in image list, need host dyld added +#if TARGET_IPHONE_SIMULATOR + // get path of host dyld from table of syscall vectors in host dyld + void* addressInDyld = gSyscallHelpers; +#else + // get path of dyld itself + void* addressInDyld = (void*)&__dso_handle; +#endif + char dyldPathBuffer[MAXPATHLEN+1]; + int len = proc_regionfilename(getpid(), (uint64_t)(long)addressInDyld, dyldPathBuffer, MAXPATHLEN); + if ( (len != 0) && (strcmp(dyldPathBuffer, gProcessInfo->dyldPath) != 0) ) { + gProcessInfo->dyldPath = strdup(dyldPathBuffer); + } + + // load any inserted libraries + if ( sEnv.DYLD_INSERT_LIBRARIES != NULL ) { + for (const char* const* lib = sEnv.DYLD_INSERT_LIBRARIES; *lib != NULL; ++lib) + loadInsertedDylib(*lib); + } + // record count of inserted libraries so that a flat search will look at + // inserted libraries, then main, then others. + sInsertedDylibCount = sAllImages.size()-1; + + // link main executable + gLinkContext.linkingMainExecutable = true; +#if SUPPORT_ACCELERATE_TABLES + if ( mainExcutableAlreadyRebased ) { + // previous link() on main executable has already adjusted its internal pointers for ASLR + // work around that by rebasing by inverse amount + sMainExecutable->rebase(gLinkContext, -mainExecutableSlide); + } +#endif + link(sMainExecutable, sEnv.DYLD_BIND_AT_LAUNCH, true, ImageLoader::RPathChain(NULL, NULL), -1); + sMainExecutable->setNeverUnloadRecursive(); + if ( sMainExecutable->forceFlat() ) { + gLinkContext.bindFlat = true; + gLinkContext.prebindUsage = ImageLoader::kUseNoPrebinding; + } + + // link any inserted libraries + // do this after linking main executable so that any dylibs pulled in by inserted + // dylibs (e.g. libSystem) will not be in front of dylibs the program uses + if ( sInsertedDylibCount > 0 ) { + for(unsigned int i=0; i < sInsertedDylibCount; ++i) { + ImageLoader* image = sAllImages[i+1]; + link(image, sEnv.DYLD_BIND_AT_LAUNCH, true, ImageLoader::RPathChain(NULL, NULL), -1); + image->setNeverUnloadRecursive(); + } + // only INSERTED libraries can interpose + // register interposing info after all inserted libraries are bound so chaining works + for(unsigned int i=0; i < sInsertedDylibCount; ++i) { + ImageLoader* image = sAllImages[i+1]; + image->registerInterposing(); + } + } + + // dyld should support interposition even without DYLD_INSERT_LIBRARIES + for (long i=sInsertedDylibCount+1; i < sAllImages.size(); ++i) { + ImageLoader* image = sAllImages[i]; + if ( image->inSharedCache() ) + continue; + image->registerInterposing(); + } + #if SUPPORT_ACCELERATE_TABLES + if ( (sAllCacheImagesProxy != NULL) && ImageLoader::haveInterposingTuples() ) { + // Accelerator tables cannot be used with implicit interposing, so relaunch with accelerator tables disabled + ImageLoader::clearInterposingTuples(); + // unmap all loaded dylibs (but not main executable) + for (long i=1; i < sAllImages.size(); ++i) { + ImageLoader* image = sAllImages[i]; + if ( image == sMainExecutable ) + continue; + if ( image == sAllCacheImagesProxy ) + continue; + image->setCanUnload(); + ImageLoader::deleteImage(image); + } + // note: we don't need to worry about inserted images because if DYLD_INSERT_LIBRARIES was set we would not be using the accelerator table + sAllImages.clear(); + sImageRoots.clear(); + sImageFilesNeedingTermination.clear(); + sImageFilesNeedingDOFUnregistration.clear(); + sAddImageCallbacks.clear(); + sRemoveImageCallbacks.clear(); + sDisableAcceleratorTables = true; + sAllCacheImagesProxy = NULL; + sMappedRangesStart = NULL; + mainExcutableAlreadyRebased = true; + gLinkContext.linkingMainExecutable = false; + resetAllImages(); + goto reloadAllImages; + } + #endif + + // apply interposing to initial set of images + for(int i=0; i < sImageRoots.size(); ++i) { + sImageRoots[i]->applyInterposing(gLinkContext); + } + gLinkContext.linkingMainExecutable = false; + + // do weak binding only after all inserted images linked + sMainExecutable->weakBind(gLinkContext); + + #if DYLD_SHARED_CACHE_SUPPORT + // If cache has branch island dylibs, tell debugger about them + if ( (sSharedCache != NULL) && (sSharedCache->mappingOffset >= 0x78) && (sSharedCache->branchPoolsOffset != 0) ) { + uint32_t count = sSharedCache->branchPoolsCount; + dyld_image_info info[count]; + const uint64_t* poolAddress = (uint64_t*)((char*)sSharedCache + sSharedCache->branchPoolsOffset); + // empty branch pools can be in development cache + if ( ((mach_header*)poolAddress)->magic == sMainExecutableMachHeader->magic ) { + for (int poolIndex=0; poolIndex < count; ++poolIndex) { + uint64_t poolAddr = poolAddress[poolIndex] + sSharedCacheSlide; + info[poolIndex].imageLoadAddress = (mach_header*)(long)poolAddr; + info[poolIndex].imageFilePath = "dyld_shared_cache_branch_islands"; + info[poolIndex].imageFileModDate = 0; + } + // add to all_images list + addImagesToAllImages(count, info); + // tell gdb about new branch island images + gProcessInfo->notification(dyld_image_adding, count, info); + } + } + #endif + + CRSetCrashLogMessage("dyld: launch, running initializers"); + #if SUPPORT_OLD_CRT_INITIALIZATION + // Old way is to run initializers via a callback from crt1.o + if ( ! gRunInitializersOldWay ) + initializeMainExecutable(); + #else + // run all initializers + initializeMainExecutable(); + #endif + // find entry point for main executable + result = (uintptr_t)sMainExecutable->getThreadPC(); + if ( result != 0 ) { + // main executable uses LC_MAIN, needs to return to glue in libdyld.dylib + if ( (gLibSystemHelpers != NULL) && (gLibSystemHelpers->version >= 9) ) + *startGlue = (uintptr_t)gLibSystemHelpers->startGlueToCallExit; + else + halt("libdyld.dylib support not present for LC_MAIN"); + } + else { + // main executable uses LC_UNIXTHREAD, dyld needs to let "start" in program set up for main() + result = (uintptr_t)sMainExecutable->getMain(); + *startGlue = 0; + } + } + catch(const char* message) { + syncAllImages(); + halt(message); + } + catch(...) { + dyld::log("dyld: launch failed\n"); + } + + CRSetCrashLogMessage(NULL); + + return result; +} + + + +} // namespace + + + diff --git a/src/dyld-apple/src/dyld.exp b/src/dyld-apple/src/dyld.exp new file mode 100644 index 000000000..e5f408681 --- /dev/null +++ b/src/dyld-apple/src/dyld.exp @@ -0,0 +1,40 @@ +# +# Copyright (c) 2004-2008 Apple Computer, 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@ +# + +# +# Only the following symbols should be "global". +# gdb and Symbolication lookup these symbols in dyld in order to determine what images the process is using +# + +# gdb and Symbolication look at these to discover a process's loaded images +_dyld_all_image_infos +_dyld_shared_cache_ranges +__dyld_debugger_notification + +# CrashReporter uses this to get message as to why dyld terminated the process +_error_string + +# Used by various tools to see build number of dyld +_dyldVersionString +_dyldVersionNumber + diff --git a/src/dyld-apple/src/dyld.h b/src/dyld-apple/src/dyld.h new file mode 100644 index 000000000..d8bca0d7b --- /dev/null +++ b/src/dyld-apple/src/dyld.h @@ -0,0 +1,142 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2004-2009 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@ + */ + +#include +#include +#include + +#include "ImageLoader.h" +#include "mach-o/dyld_priv.h" + + + +// +// dyld functions available when implementing dyld API's +// +// +namespace dyld { + + struct LoadContext + { + bool useSearchPaths; + bool useFallbackPaths; + bool useLdLibraryPath; + bool implicitRPath; + bool matchByInstallName; + bool dontLoad; + bool mustBeBundle; + bool mustBeDylib; + bool canBePIE; + const char* origin; // path for expanding @loader_path + const ImageLoader::RPathChain* rpath; // paths for expanding @rpath + }; + + + + typedef void (*ImageCallback)(const struct mach_header* mh, intptr_t slide); + typedef void (*UndefinedHandler)(const char* symbolName); + typedef const char* (*ImageLocator)(const char* dllName); + + + extern ImageLoader::LinkContext gLinkContext; + extern struct dyld_all_image_infos* gProcessInfo; + extern bool gLogAPIs; +#if SUPPORT_ACCELERATE_TABLES + extern bool gLogAppAPIs; +#endif +#if DYLD_SHARED_CACHE_SUPPORT + extern bool gSharedCacheOverridden; +#endif + extern const struct LibSystemHelpers* gLibSystemHelpers; +#if SUPPORT_OLD_CRT_INITIALIZATION + extern bool gRunInitializersOldWay; +#endif + extern void registerAddCallback(ImageCallback func); + extern void registerRemoveCallback(ImageCallback func); + extern void registerUndefinedHandler(UndefinedHandler); + extern void initializeMainExecutable(); + extern void preflight(ImageLoader* image, const ImageLoader::RPathChain& loaderRPaths, unsigned cacheIndex); + extern void link(ImageLoader* image, bool forceLazysBound, bool neverUnload, const ImageLoader::RPathChain& loaderRPaths, unsigned cacheIndex); + extern void runInitializers(ImageLoader* image); + extern void runImageStaticTerminators(ImageLoader* image); + extern const char* getExecutablePath(); + extern bool validImage(const ImageLoader*); + extern ImageLoader* getIndexedImage(uint32_t index); + extern uint32_t getImageCount(); + extern ImageLoader* findImageByMachHeader(const struct mach_header* target); + extern ImageLoader* findImageContainingAddress(const void* addr); + extern ImageLoader* findImageContainingSymbol(const void* symbol); + extern ImageLoader* findImageByName(const char* path); + extern ImageLoader* findLoadedImageByInstallPath(const char* path); + extern bool flatFindExportedSymbol(const char* name, const ImageLoader::Symbol** sym, const ImageLoader** image); + extern bool flatFindExportedSymbolWithHint(const char* name, const char* librarySubstring, const ImageLoader::Symbol** sym, const ImageLoader** image); + extern ImageLoader* load(const char* path, const LoadContext& context, unsigned& cacheIndex); + extern ImageLoader* loadFromMemory(const uint8_t* mem, uint64_t len, const char* moduleName); + extern void removeImage(ImageLoader* image); + extern ImageLoader* cloneImage(ImageLoader* image); + extern void forEachImageDo( void (*)(ImageLoader*, void*), void*); + extern uintptr_t _main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide, int argc, const char* argv[], const char* envp[], + const char* apple[], uintptr_t* startGlue) __attribute__((noinline)); // +// +// + +#define __STDC_LIMIT_MACROS +#include +#include +#include +#include +#include +#include + + +#include +#include +#include + +#include +#include +#include +#include // for task_self_trap() + + +#include "mach-o/dyld_images.h" +#include "mach-o/dyld.h" +#include "mach-o/dyld_priv.h" + +#include "ImageLoader.h" +#include "ImageLoaderMachO.h" +#include "dyld.h" +#include "dyldLibSystemInterface.h" + +#undef _POSIX_C_SOURCE +#include "dlfcn.h" + +// from dyldExceptions.c +extern "C" void __Unwind_SjLj_SetThreadKey(pthread_key_t key); + +// from dyld_gdb.cpp +extern void addImagesToAllImages(uint32_t infoCount, const dyld_image_info info[]); +extern uint32_t allImagesCount(); +extern const mach_header* allImagesIndexedMachHeader(uint32_t index); +extern const char* allImagesIndexedPath(uint32_t index); + + +// deprecated APIs are still availble on Mac OS X, but not on iPhone OS +#if __IPHONE_OS_VERSION_MIN_REQUIRED + #define DEPRECATED_APIS_SUPPORTED 0 +#else + #define DEPRECATED_APIS_SUPPORTED 1 +#endif + +static bool sDynamicInterposing = false; + +#if DEPRECATED_APIS_SUPPORTED +static char sLastErrorFilePath[1024]; +static NSLinkEditErrors sLastErrorFileCode; +static int sLastErrorNo; +#endif + +// In 10.3.x and earlier all the NSObjectFileImage API's were implemeneted in libSystem.dylib +// Beginning in 10.4 the NSObjectFileImage API's are implemented in dyld and libSystem just forwards +// This conditional keeps support for old libSystem's which needed some help implementing the API's +#define OLD_LIBSYSTEM_SUPPORT (__i386__) + +// The following functions have no prototype in any header. They are special cases +// where _dyld_func_lookup() is used directly. +static void _dyld_make_delayed_module_initializer_calls(); +static void registerThreadHelpers(const dyld::LibSystemHelpers*); +#if DEPRECATED_APIS_SUPPORTED +static void _dyld_install_handlers(void* undefined, void* multiple, void* linkEdit); +#if OLD_LIBSYSTEM_SUPPORT +static NSModule _dyld_link_module(NSObjectFileImage object_addr, size_t object_size, const char* moduleName, uint32_t options); +#endif +static void _dyld_register_binding_handler(void * (*)(const char *, const char *, void *), ImageLoader::BindingOptions); +static bool NSMakePrivateModulePublic(NSModule module); +static void _dyld_call_module_initializers_for_dylib(const struct mach_header* mh_dylib_header); + +// The following functions are dyld API's, but since dyld links with a static copy of libc.a +// the public name cannot be used. +static void client_dyld_lookup_and_bind(const char* symbolName, void** address, NSModule* module); +static bool client_NSIsSymbolNameDefined(const char* symbolName); +#endif // DEPRECATED_APIS_SUPPORTED +#if SUPPORT_ZERO_COST_EXCEPTIONS +static bool client_dyld_find_unwind_sections(void* addr, dyld_unwind_sections* info); +#endif +#if DEPRECATED_APIS_SUPPORTED +#endif + +static void unimplemented() +{ + dyld::halt("unimplemented dyld function\n"); +} + +struct dyld_func { + const char* name; + void* implementation; +}; + +static struct dyld_func dyld_funcs[] = { + {"__dyld_register_func_for_add_image", (void*)_dyld_register_func_for_add_image }, + {"__dyld_register_func_for_remove_image", (void*)_dyld_register_func_for_remove_image }, + {"__dyld_dladdr", (void*)dladdr }, + {"__dyld_dlclose", (void*)dlclose }, + {"__dyld_dlerror", (void*)dlerror }, + {"__dyld_dlopen", (void*)dlopen }, + {"__dyld_dlsym", (void*)dlsym }, + {"__dyld_dlopen_preflight", (void*)dlopen_preflight }, + {"__dyld_image_count", (void*)_dyld_image_count }, + {"__dyld_get_image_header", (void*)_dyld_get_image_header }, + {"__dyld_get_image_vmaddr_slide", (void*)_dyld_get_image_vmaddr_slide }, + {"__dyld_get_image_name", (void*)_dyld_get_image_name }, + {"__dyld_get_image_slide", (void*)_dyld_get_image_slide }, + {"__dyld__NSGetExecutablePath", (void*)_NSGetExecutablePath }, + + // SPIs + {"__dyld_register_thread_helpers", (void*)registerThreadHelpers }, + {"__dyld_fork_child", (void*)_dyld_fork_child }, + {"__dyld_make_delayed_module_initializer_calls", (void*)_dyld_make_delayed_module_initializer_calls }, + {"__dyld_get_all_image_infos", (void*)_dyld_get_all_image_infos }, +#if SUPPORT_ZERO_COST_EXCEPTIONS + {"__dyld_find_unwind_sections", (void*)client_dyld_find_unwind_sections }, +#endif +#if __i386__ || __x86_64__ || __arm__ || __arm64__ + {"__dyld_fast_stub_entry", (void*)dyld::fastBindLazySymbol }, +#endif + {"__dyld_image_path_containing_address", (void*)dyld_image_path_containing_address }, + {"__dyld_shared_cache_some_image_overridden", (void*)dyld_shared_cache_some_image_overridden }, + {"__dyld_process_is_restricted", (void*)dyld::processIsRestricted }, + {"__dyld_dynamic_interpose", (void*)dyld_dynamic_interpose }, +#if DYLD_SHARED_CACHE_SUPPORT + {"__dyld_shared_cache_file_path", (void*)dyld::getStandardSharedCacheFilePath }, +#endif + {"__dyld_get_image_header_containing_address", (void*)dyld_image_header_containing_address }, + {"__dyld_is_memory_immutable", (void*)_dyld_is_memory_immutable }, + {"__dyld_objc_notify_register", (void*)_dyld_objc_notify_register }, + {"__dyld_get_shared_cache_uuid", (void*)_dyld_get_shared_cache_uuid }, + + + // deprecated +#if DEPRECATED_APIS_SUPPORTED + {"__dyld_lookup_and_bind", (void*)client_dyld_lookup_and_bind }, + {"__dyld_lookup_and_bind_with_hint", (void*)_dyld_lookup_and_bind_with_hint }, + {"__dyld_lookup_and_bind_fully", (void*)_dyld_lookup_and_bind_fully }, + {"__dyld_install_handlers", (void*)_dyld_install_handlers }, + {"__dyld_link_edit_error", (void*)NSLinkEditError }, + {"__dyld_unlink_module", (void*)NSUnLinkModule }, + {"__dyld_bind_objc_module", (void*)_dyld_bind_objc_module }, + {"__dyld_bind_fully_image_containing_address", (void*)_dyld_bind_fully_image_containing_address }, + {"__dyld_image_containing_address", (void*)_dyld_image_containing_address }, + {"__dyld_register_binding_handler", (void*)_dyld_register_binding_handler }, + {"__dyld_NSNameOfSymbol", (void*)NSNameOfSymbol }, + {"__dyld_NSAddressOfSymbol", (void*)NSAddressOfSymbol }, + {"__dyld_NSModuleForSymbol", (void*)NSModuleForSymbol }, + {"__dyld_NSLookupAndBindSymbol", (void*)NSLookupAndBindSymbol }, + {"__dyld_NSLookupAndBindSymbolWithHint", (void*)NSLookupAndBindSymbolWithHint }, + {"__dyld_NSLookupSymbolInModule", (void*)NSLookupSymbolInModule}, + {"__dyld_NSLookupSymbolInImage", (void*)NSLookupSymbolInImage}, + {"__dyld_NSMakePrivateModulePublic", (void*)NSMakePrivateModulePublic}, + {"__dyld_NSIsSymbolNameDefined", (void*)client_NSIsSymbolNameDefined}, + {"__dyld_NSIsSymbolNameDefinedWithHint", (void*)NSIsSymbolNameDefinedWithHint }, + {"__dyld_NSIsSymbolNameDefinedInImage", (void*)NSIsSymbolNameDefinedInImage}, + {"__dyld_NSNameOfModule", (void*)NSNameOfModule }, + {"__dyld_NSLibraryNameForModule", (void*)NSLibraryNameForModule }, + {"__dyld_NSAddLibrary", (void*)NSAddLibrary }, + {"__dyld_NSAddLibraryWithSearching", (void*)NSAddLibraryWithSearching }, + {"__dyld_NSAddImage", (void*)NSAddImage }, + {"__dyld_launched_prebound", (void*)_dyld_launched_prebound }, + {"__dyld_all_twolevel_modules_prebound", (void*)_dyld_all_twolevel_modules_prebound }, + {"__dyld_call_module_initializers_for_dylib", (void*)_dyld_call_module_initializers_for_dylib }, + {"__dyld_NSCreateObjectFileImageFromFile", (void*)NSCreateObjectFileImageFromFile }, + {"__dyld_NSCreateObjectFileImageFromMemory", (void*)NSCreateObjectFileImageFromMemory }, + {"__dyld_NSDestroyObjectFileImage", (void*)NSDestroyObjectFileImage }, + {"__dyld_NSLinkModule", (void*)NSLinkModule }, + {"__dyld_NSHasModInitObjectFileImage", (void*)NSHasModInitObjectFileImage }, + {"__dyld_NSSymbolDefinitionCountInObjectFileImage", (void*)NSSymbolDefinitionCountInObjectFileImage }, + {"__dyld_NSSymbolDefinitionNameInObjectFileImage", (void*)NSSymbolDefinitionNameInObjectFileImage }, + {"__dyld_NSIsSymbolDefinedInObjectFileImage", (void*)NSIsSymbolDefinedInObjectFileImage }, + {"__dyld_NSSymbolReferenceNameInObjectFileImage", (void*)NSSymbolReferenceNameInObjectFileImage }, + {"__dyld_NSSymbolReferenceCountInObjectFileImage", (void*)NSSymbolReferenceCountInObjectFileImage }, + {"__dyld_NSGetSectionDataInObjectFileImage", (void*)NSGetSectionDataInObjectFileImage }, +#if OLD_LIBSYSTEM_SUPPORT + {"__dyld_link_module", (void*)_dyld_link_module }, +#endif +#endif //DEPRECATED_APIS_SUPPORTED + + {NULL, 0} +}; + + + +#if DEPRECATED_APIS_SUPPORTED + +static void dyldAPIhalt(const char* apiName, const char* errorMsg) +{ + dyld::log("dyld: %s() error\n", apiName); + dyld::halt(errorMsg); +} + +// dyld's abstract type NSSymbol is implemented as const ImageLoader::Symbol* +inline NSSymbol SymbolToNSSymbol(const ImageLoader::Symbol* sym) +{ + return (NSSymbol)sym; +} +inline const ImageLoader::Symbol* NSSymbolToSymbol(NSSymbol sym) +{ + return (const ImageLoader::Symbol*)sym; +} + +// dyld's abstract type NSModule is implemented as ImageLoader* +inline NSModule ImageLoaderToNSModule(const ImageLoader* image) +{ + return (NSModule)image; +} +inline ImageLoader* NSModuleToImageLoader(NSModule module) +{ + ImageLoader* image = (ImageLoader*)module; + if ( dyld::validImage(image) ) + return image; + return NULL; +} + +// actual definition for opaque type +struct __NSObjectFileImage +{ + ImageLoader* image; + const void* imageBaseAddress; // not used with OFI created from files + size_t imageLength; // not used with OFI created from files +}; + + +VECTOR_NEVER_DESTRUCTED(NSObjectFileImage); +static std::vector sObjectFileImages; + + + +// +// __NSObjectFileImage are deleted in NSDestroyObjectFileImage() +// The contained image is delete in one of two places: +// NSUnLinkModule deletes the image if there is no __NSObjectFileImage with a reference to it +// NSDestroyObjectFileImage deletes the image if image is not in list of valid images +// + + + +static void setLastError(NSLinkEditErrors code, int errnum, const char* file, const char* message) +{ + dyld::setErrorMessage(message); + strncpy(sLastErrorFilePath, file, 1024); + sLastErrorFilePath[1023] = '\0'; + sLastErrorFileCode = code; + sLastErrorNo = errnum; +} + +#endif // DEPRECATED_APIS_SUPPORTED + +/* + *_dyld_NSGetExecutablePath is the dyld side of _NSGetExecutablePath which + * copies the path of the executable into the buffer and returns 0 if the path + * was successfully copied in the provided buffer. If the buffer is not large + * enough, -1 is returned and the expected buffer size is copied in *bufsize. + * Note that _NSGetExecutablePath will return "a path" to the executable not a + * "real path" to the executable. That is the path may be a symbolic link and + * not the real file. And with deep directories the total bufsize needed could + * be more than MAXPATHLEN. + */ +int _NSGetExecutablePath(char* buf, uint32_t *bufsize) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s(...)\n", __func__); + const char* exePath = dyld::getExecutablePath(); + if(*bufsize < strlen(exePath) + 1){ + *bufsize = (uint32_t)(strlen(exePath) + 1); + return -1; + } + strcpy(buf, exePath); + return 0; +} + +uint32_t _dyld_image_count(void) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s()\n", __func__); + return allImagesCount(); +} + +const struct mach_header* _dyld_get_image_header(uint32_t image_index) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s(%u)\n", __func__, image_index); + return allImagesIndexedMachHeader(image_index); +} + +intptr_t _dyld_get_image_vmaddr_slide(uint32_t image_index) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s(%u)\n", __func__, image_index); + const struct mach_header* mh = allImagesIndexedMachHeader(image_index); + if ( mh != NULL ) + return ImageLoaderMachO::computeSlide(mh); + else + return 0; +} + +intptr_t _dyld_get_image_slide(const struct mach_header* mh) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s(%p)\n", __func__, mh); + return ImageLoaderMachO::computeSlide(mh); +} + + +const char* _dyld_get_image_name(uint32_t image_index) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s(%u)\n", __func__, image_index); + return allImagesIndexedPath(image_index); +} + +const struct mach_header * dyld_image_header_containing_address(const void* address) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s(%p)\n", __func__, address); +#if SUPPORT_ACCELERATE_TABLES + const mach_header* mh; + const char* path; + if ( dyld::addressInCache(address, &mh, &path) ) + return mh; +#endif + ImageLoader* image = dyld::findImageContainingAddress(address); + if ( image != NULL ) + return image->machHeader(); + return NULL; +} + + +void _dyld_register_func_for_add_image(void (*func)(const struct mach_header *mh, intptr_t vmaddr_slide)) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s(%p)\n", __func__, (void *)func); + dyld::registerAddCallback(func); +} + +void _dyld_register_func_for_remove_image(void (*func)(const struct mach_header *mh, intptr_t vmaddr_slide)) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s(%p)\n", __func__, (void *)func); + dyld::registerRemoveCallback(func); +} + + + +// called by crt before main() by programs linked with 10.4 or earlier crt1.o +static void _dyld_make_delayed_module_initializer_calls() +{ + if ( dyld::gLogAPIs ) + dyld::log("%s()\n", __func__); + +#if SUPPORT_OLD_CRT_INITIALIZATION + if ( dyld::gRunInitializersOldWay ) + dyld::initializeMainExecutable(); +#endif +} + + + +#if DEPRECATED_APIS_SUPPORTED + +// +// _dyld_call_module_initializers_for_dylib() is the dyld side of +// __initialize_Cplusplus() which is in dylib1.o. +// It is intended to only be called inside -init rouintes. +// -init routines are called before module initializers (what C++ +// initializers use). Calling __initialize_Cplusplus() in a -init +// routine causes the module initializers for an image to be called +// which then allows C++ to be used inside a -init routine +// +static void _dyld_call_module_initializers_for_dylib(const struct mach_header* mh_dylib_header) +{ + if ( dyld::gLogAPIs ) + dyld::log("__initialize_Cplusplus()\n"); + + // for now, do nothing... +} + + +void _dyld_lookup_and_bind_fully(const char* symbolName, void** address, NSModule* module) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s(\"%s\", %p, %p)\n", __func__, symbolName, address, module); + ImageLoader* image; + const ImageLoader::Symbol* sym; + dyld::clearErrorMessage(); + if ( dyld::flatFindExportedSymbol(symbolName, &sym, (const ImageLoader**)&image) ) { + try { + image->bindAllLazyPointers(dyld::gLinkContext, true); + if ( address != NULL) + *address = (void*)image->getExportedSymbolAddress(sym, dyld::gLinkContext); + if ( module != NULL) + *module = ImageLoaderToNSModule(image); + } + catch (const char* msg) { + dyldAPIhalt(__func__, msg); + } + } + else { + // on failure to find symbol return NULLs + if ( address != NULL) + *address = NULL; + if ( module != NULL) + *module = NULL; + } +} + +// Note: This cannot have public name because dyld is built with a static copy of libc.a +// which calls dyld_lookup_and_bind() and expects to find dyld's symbols not host process +static void client_dyld_lookup_and_bind(const char* symbolName, void** address, NSModule* module) +{ + if ( dyld::gLogAPIs ) + dyld::log("_dyld_lookup_and_bind(\"%s\", %p, %p)\n", symbolName, address, module); + const ImageLoader* image; + const ImageLoader::Symbol* sym; + if ( dyld::flatFindExportedSymbol(symbolName, &sym, &image) ) { + if ( address != NULL) + *address = (void*)image->getExportedSymbolAddress(sym, dyld::gLinkContext); + if ( module != NULL) + *module = ImageLoaderToNSModule(image); + } + else { + // on failure to find symbol return NULLs + if ( address != NULL) + *address = NULL; + if ( module != NULL) + *module = NULL; + } +} + +void _dyld_lookup_and_bind_with_hint(const char* symbolName, const char* library_name_hint, void** address, NSModule* module) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s(\"%s\", \"%s\", %p, %p)\n", __func__, symbolName, library_name_hint, address, module); + const ImageLoader* image; + const ImageLoader::Symbol* sym; + // Look for library whose path contains the hint. If that fails search everywhere + if ( dyld::flatFindExportedSymbolWithHint(symbolName, library_name_hint, &sym, &image) + || dyld::flatFindExportedSymbol(symbolName, &sym, &image) ) { + if ( address != NULL) + *address = (void*)image->getExportedSymbolAddress(sym, dyld::gLinkContext); + if ( module != NULL) + *module = ImageLoaderToNSModule(image); + } + else { + // on failure to find symbol return NULLs + if ( address != NULL) + *address = NULL; + if ( module != NULL) + *module = NULL; + } +} + + +NSSymbol NSLookupAndBindSymbol(const char *symbolName) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s(\"%s\")\n", __func__, symbolName); + const ImageLoader* image; + const ImageLoader::Symbol* sym; + if ( dyld::flatFindExportedSymbol(symbolName, &sym, &image) ) { + return SymbolToNSSymbol(sym); + } + // return NULL on failure + return NULL; +} + +NSSymbol NSLookupAndBindSymbolWithHint(const char* symbolName, const char* libraryNameHint) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s(\"%s\", \"%s\")\n", __func__, symbolName, libraryNameHint); + const ImageLoader* image; + const ImageLoader::Symbol* sym; + bool found = dyld::flatFindExportedSymbolWithHint(symbolName, libraryNameHint, &sym, &image); + if ( ! found ) { + // hint failed, do slow search of all images + found = dyld::flatFindExportedSymbol(symbolName, &sym, &image); + } + if ( found ) + return SymbolToNSSymbol(sym); + + // return NULL on failure and log + if ( dyld::gLogAPIs ) + dyld::log("%s(\"%s\", \"%s\") => NULL \n", __func__, symbolName, libraryNameHint); + return NULL; +} + + + + +static __attribute__((noinline)) +const struct mach_header* addImage(void* callerAddress, const char* path, bool search, bool dontLoad, bool matchInstallName, bool abortOnError) +{ + ImageLoader* image = NULL; + std::vector rpathsFromCallerImage; + try { + dyld::clearErrorMessage(); + ImageLoader* callerImage = dyld::findImageContainingAddress(callerAddress); + // like dlopen, use rpath from caller image and from main executable + if ( callerImage != NULL ) + callerImage->getRPaths(dyld::gLinkContext, rpathsFromCallerImage); + ImageLoader::RPathChain callersRPaths(NULL, &rpathsFromCallerImage); + if ( callerImage != dyld::mainExecutable() ) { + dyld::mainExecutable()->getRPaths(dyld::gLinkContext, rpathsFromCallerImage); + } + dyld::LoadContext context; + context.useSearchPaths = search; + context.useFallbackPaths = search; + context.useLdLibraryPath = false; + context.implicitRPath = false; + context.matchByInstallName = matchInstallName; + context.dontLoad = dontLoad; + context.mustBeBundle = false; + context.mustBeDylib = true; + context.canBePIE = false; + context.origin = callerImage != NULL ? callerImage->getPath() : NULL; // caller's image's path + context.rpath = &callersRPaths; // rpaths from caller and main executable + + unsigned cacheIndex; + image = load(path, context, cacheIndex); + if ( image != NULL ) { + if ( context.matchByInstallName ) + image->setMatchInstallPath(true); + dyld::link(image, false, false, callersRPaths, cacheIndex); + dyld::runInitializers(image); + // images added with NSAddImage() can never be unloaded + image->setNeverUnload(); + } + } + catch (const char* msg) { + dyld::garbageCollectImages(); + if ( abortOnError) { + char pathMsg[strlen(msg)+strlen(path)+4]; + strcpy(pathMsg, msg); + strcat(pathMsg, " "); + strcat(pathMsg, path); + dyldAPIhalt("NSAddImage", pathMsg); + } + // not halting, so set error state for NSLinkEditError to find + setLastError(NSLinkEditOtherError, 0, path, msg); + free((void*)msg); // our free() will do nothing if msg is a string literal + image = NULL; + } + // free rpaths (getRPaths() malloc'ed each string) + for(std::vector::iterator it=rpathsFromCallerImage.begin(); it != rpathsFromCallerImage.end(); ++it) { + const char* str = *it; + free((void*)str); + } + if ( image == NULL ) + return NULL; + else + return image->machHeader(); +} + + +const struct mach_header* NSAddImage(const char* path, uint32_t options) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s(\"%s\", 0x%08X)\n", __func__, path, options); + const bool dontLoad = ( (options & NSADDIMAGE_OPTION_RETURN_ONLY_IF_LOADED) != 0 ); + const bool search = ( (options & NSADDIMAGE_OPTION_WITH_SEARCHING) != 0 ); + const bool matchInstallName = ( (options & NSADDIMAGE_OPTION_MATCH_FILENAME_BY_INSTALLNAME) != 0 ); + const bool abortOnError = ( (options & (NSADDIMAGE_OPTION_RETURN_ON_ERROR|NSADDIMAGE_OPTION_RETURN_ONLY_IF_LOADED)) == 0 ); + void* callerAddress = __builtin_return_address(1); // note layers: 1: real client, 0: libSystem glue + return addImage(callerAddress, path, search, dontLoad, matchInstallName, abortOnError); +} + +bool NSAddLibrary(const char* path) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s(\"%s\")\n", __func__, path); + void* callerAddress = __builtin_return_address(1); // note layers: 1: real client, 0: libSystem glue + return (addImage(callerAddress, path, false, false, false, false) != NULL); +} + +bool NSAddLibraryWithSearching(const char* path) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s(\"%s\")\n", __func__, path); + void* callerAddress = __builtin_return_address(1); // note layers: 1: real client, 0: libSystem glue + return (addImage(callerAddress, path, true, false, false, false) != NULL); +} + + + +//#define NSADDIMAGE_OPTION_NONE 0x0 +//#define NSADDIMAGE_OPTION_RETURN_ON_ERROR 0x1 +//#define NSADDIMAGE_OPTION_MATCH_FILENAME_BY_INSTALLNAME 0x8 + +bool NSIsSymbolNameDefinedInImage(const struct mach_header* mh, const char* symbolName) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s(%p, \"%s\")\n", __func__, (void *)mh, symbolName); + ImageLoader* image = dyld::findImageByMachHeader(mh); + if ( image != NULL ) { + if ( image->findExportedSymbol(symbolName, true, NULL) != NULL) + return true; + } + return false; +} + + +NSSymbol NSLookupSymbolInImage(const struct mach_header* mh, const char* symbolName, uint32_t options) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s(%p, \"%s\", 0x%08X)\n", __func__, mh, symbolName, options); + const ImageLoader::Symbol* symbol = NULL; + dyld::clearErrorMessage(); + ImageLoader* image = dyld::findImageByMachHeader(mh); + if ( image != NULL ) { + try { + if ( options & NSLOOKUPSYMBOLINIMAGE_OPTION_BIND_FULLY ) { + image->bindAllLazyPointers(dyld::gLinkContext, true); + } + else if ( options & NSLOOKUPSYMBOLINIMAGE_OPTION_BIND_NOW ) { + image->bindAllLazyPointers(dyld::gLinkContext, false); + } + } + catch (const char* msg) { + if ( (options & NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR) == 0 ) { + dyldAPIhalt(__func__, msg); + } + } + symbol = image->findExportedSymbol(symbolName, true, NULL); + } + if ( dyld::gLogAPIs && (symbol == NULL) ) + dyld::log("%s(%p, \"%s\", 0x%08X) ==> NULL\n", __func__, mh, symbolName, options); + return SymbolToNSSymbol(symbol); +} + + +// Note: This cannot have public name because dyld is built with a static copy of libc.a +// which calls NSIsSymbolNameDefined() and expects to find dyld's symbols not host process +static bool client_NSIsSymbolNameDefined(const char* symbolName) +{ + if ( dyld::gLogAPIs ) + dyld::log("NSIsSymbolNameDefined(\"%s\")\n", symbolName); + const ImageLoader* image; + const ImageLoader::Symbol* sym; + return dyld::flatFindExportedSymbol(symbolName, &sym, &image); +} + +bool NSIsSymbolNameDefinedWithHint(const char* symbolName, const char* libraryNameHint) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s(\"%s\", \"%s\")\n", __func__, symbolName, libraryNameHint); + const ImageLoader* image; + const ImageLoader::Symbol* sym; + bool found = dyld::flatFindExportedSymbolWithHint(symbolName, libraryNameHint, &sym, &image); + if ( ! found ) { + // hint failed, do slow search of all images + found = dyld::flatFindExportedSymbol(symbolName, &sym, &image); + } + if ( !found && dyld::gLogAPIs ) + dyld::log("%s(\"%s\", \"%s\") => false \n", __func__, symbolName, libraryNameHint); + return found; +} + +const char* NSNameOfSymbol(NSSymbol symbol) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s(%p)\n", __func__, (void *)symbol); + const char* result = NULL; + ImageLoader* image = dyld::findImageContainingSymbol(symbol); + if ( image != NULL ) + result = image->getExportedSymbolName(NSSymbolToSymbol(symbol)); + return result; +} + +void* NSAddressOfSymbol(NSSymbol symbol) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s(%p)\n", __func__, (void *)symbol); + if ( symbol == NULL ) + return NULL; + void* result = NULL; + ImageLoader* image = dyld::findImageContainingSymbol(symbol); + if ( image != NULL ) + result = (void*)image->getExportedSymbolAddress(NSSymbolToSymbol(symbol), dyld::gLinkContext); + return result; +} + +NSModule NSModuleForSymbol(NSSymbol symbol) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s(%p)\n", __func__, (void *)symbol); + NSModule result = NULL; + ImageLoader* image = dyld::findImageContainingSymbol(symbol); + if ( image != NULL ) + result = ImageLoaderToNSModule(image); + return result; +} + + + + +bool _dyld_all_twolevel_modules_prebound(void) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s()\n", __func__); + return FALSE; +} + +void _dyld_bind_objc_module(const void *objc_module) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s(%p)\n", __func__, objc_module); + // do nothing, with new dyld everything already bound +} + + +bool _dyld_bind_fully_image_containing_address(const void* address) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s(%p)\n", __func__, address); + dyld::clearErrorMessage(); + ImageLoader* image = dyld::findImageContainingAddress(address); + if ( image != NULL ) { + try { + image->bindAllLazyPointers(dyld::gLinkContext, true); + return true; + } + catch (const char* msg) { + dyldAPIhalt(__func__, msg); + } + } + return false; +} + +bool _dyld_image_containing_address(const void* address) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s(%p)\n", __func__, address); + ImageLoader *imageLoader = dyld::findImageContainingAddress(address); + return (NULL != imageLoader); +} + +static NSObjectFileImage createObjectImageFile(ImageLoader* image, const void* address = NULL, size_t len=0) +{ + NSObjectFileImage result = new __NSObjectFileImage(); + result->image = image; + result->imageBaseAddress = address; + result->imageLength = len; + sObjectFileImages.push_back(result); + return result; +} + +NSObjectFileImageReturnCode NSCreateObjectFileImageFromFile(const char* pathName, NSObjectFileImage *objectFileImage) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s(\"%s\", ...)\n", __func__, pathName); + try { + void* callerAddress = __builtin_return_address(1); // note layers: 1: real client, 0: libSystem glue + ImageLoader* callerImage = dyld::findImageContainingAddress(callerAddress); + + dyld::LoadContext context; + context.useSearchPaths = false; + context.useFallbackPaths = false; + context.useLdLibraryPath = false; + context.implicitRPath = false; + context.matchByInstallName = false; + context.dontLoad = false; + context.mustBeBundle = true; + context.mustBeDylib = false; + context.canBePIE = false; + context.origin = callerImage != NULL ? callerImage->getPath() : NULL; // caller's image's path + context.rpath = NULL; // support not yet implemented + + unsigned cacheIndex; + ImageLoader* image = dyld::load(pathName, context, cacheIndex); + // Note: We DO NOT link the image! NSLinkModule will do that + if ( image != NULL ) { + if ( !image->isBundle() ) { + // the image must have been already loaded (since context.mustBeBundle will prevent it from being loaded) + return NSObjectFileImageInappropriateFile; + } + *objectFileImage = createObjectImageFile(image); + return NSObjectFileImageSuccess; + } + } + catch (const char* msg) { + //dyld::log("dyld: NSCreateObjectFileImageFromFile() error: %s\n", msg); + dyld::garbageCollectImages(); + free((void*)msg); + return NSObjectFileImageInappropriateFile; + } + return NSObjectFileImageFailure; +} + + +NSObjectFileImageReturnCode NSCreateObjectFileImageFromMemory(const void* address, size_t size, NSObjectFileImage *objectFileImage) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s(%p, %lu, %p)\n", __func__, address, size, objectFileImage); + + try { + ImageLoader* image = dyld::loadFromMemory((const uint8_t*)address, size, NULL); + if ( ! image->isBundle() ) { + // this API can only be used with bundles... + dyld::garbageCollectImages(); + return NSObjectFileImageInappropriateFile; + } + // Note: We DO NOT link the image! NSLinkModule will do that + if ( image != NULL ) { + *objectFileImage = createObjectImageFile(image, address, size); + return NSObjectFileImageSuccess; + } + } + catch (const char* msg) { + free((void*)msg); + dyld::garbageCollectImages(); + //dyld::log("dyld: NSCreateObjectFileImageFromMemory() error: %s\n", msg); + } + return NSObjectFileImageFailure; +} + +static bool validOFI(NSObjectFileImage objectFileImage) +{ + const int ofiCount = sObjectFileImages.size(); + for (int i=0; i < ofiCount; ++i) { + if ( sObjectFileImages[i] == objectFileImage ) + return true; + } + return false; +} + +bool NSDestroyObjectFileImage(NSObjectFileImage objectFileImage) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s(%p)\n", __func__, objectFileImage); + + if ( validOFI(objectFileImage) ) { + // a failure during NSLinkModule will delete the image + if ( objectFileImage->image != NULL ) { + // if the image has never been linked or has been unlinked, the image is not in the list of valid images + // and we should delete it + bool linkedImage = dyld::validImage(objectFileImage->image); + if ( ! linkedImage ) { + ImageLoader::deleteImage(objectFileImage->image); + objectFileImage->image = NULL; + } + } + + // remove from list of ofi's + for (std::vector::iterator it=sObjectFileImages.begin(); it != sObjectFileImages.end(); it++) { + if ( *it == objectFileImage ) { + sObjectFileImages.erase(it); + break; + } + } + + // if object was created from a memory, release that memory + // NOTE: this is the way dyld has always done this. NSCreateObjectFileImageFromMemory() hands over ownership of the memory to dyld + if ( objectFileImage->imageBaseAddress != NULL ) { + bool freed = false; + if ( (dyld::gLibSystemHelpers != NULL) && (dyld::gLibSystemHelpers->version >= 6) ) { + size_t sz = (*dyld::gLibSystemHelpers->malloc_size)(objectFileImage->imageBaseAddress); + if ( sz != 0 ) { + (*dyld::gLibSystemHelpers->free)((void*)(objectFileImage->imageBaseAddress)); + freed = true; + } + } + if ( ! freed ) + vm_deallocate(mach_task_self(), (vm_address_t)objectFileImage->imageBaseAddress, objectFileImage->imageLength); + } + + // free ofi object + delete objectFileImage; + + return true; + } + return false; +} + +bool NSHasModInitObjectFileImage(NSObjectFileImage objectFileImage) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s(%p)\n", __func__, objectFileImage); + return objectFileImage->image->needsInitialization(); +} + +uint32_t NSSymbolDefinitionCountInObjectFileImage(NSObjectFileImage objectFileImage) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s(%p)\n", __func__, objectFileImage); + return objectFileImage->image->getExportedSymbolCount(); +} + +const char* NSSymbolDefinitionNameInObjectFileImage(NSObjectFileImage objectFileImage, uint32_t ordinal) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s(%p,%d)\n", __func__, objectFileImage, ordinal); + const ImageLoader::Symbol* sym = objectFileImage->image->getIndexedExportedSymbol(ordinal); + return objectFileImage->image->getExportedSymbolName(sym); +} + +uint32_t NSSymbolReferenceCountInObjectFileImage(NSObjectFileImage objectFileImage) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s(%p)\n", __func__, objectFileImage); + return objectFileImage->image->getImportedSymbolCount(); +} + +const char * NSSymbolReferenceNameInObjectFileImage(NSObjectFileImage objectFileImage, uint32_t ordinal, + bool* tentative_definition) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s(%p,%d)\n", __func__, objectFileImage, ordinal); + const ImageLoader::Symbol* sym = objectFileImage->image->getIndexedImportedSymbol(ordinal); + if ( tentative_definition != NULL ) { + ImageLoader::ReferenceFlags flags = objectFileImage->image->getImportedSymbolInfo(sym); + if ( (flags & ImageLoader::kTentativeDefinition) != 0 ) + *tentative_definition = true; + else + *tentative_definition = false; + } + return objectFileImage->image->getImportedSymbolName(sym); +} + +void* NSGetSectionDataInObjectFileImage(NSObjectFileImage objectFileImage, + const char* segmentName, const char* sectionName, unsigned long* size) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s(%p,%s, %s)\n", __func__, objectFileImage, segmentName, sectionName); + + void* start; + size_t length; + if ( objectFileImage->image->getSectionContent(segmentName, sectionName, &start, &length) ) { + if ( size != NULL ) + *size = length; + return start; + } + return NULL; +} + + + +bool NSIsSymbolDefinedInObjectFileImage(NSObjectFileImage objectFileImage, const char* symbolName) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s(%p,%s)\n", __func__, objectFileImage, symbolName); + const ImageLoader::Symbol* sym = objectFileImage->image->findExportedSymbol(symbolName, true, NULL); + return ( sym != NULL ); +} + + + +NSModule NSLinkModule(NSObjectFileImage objectFileImage, const char* moduleName, uint32_t options) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s(%p, \"%s\", 0x%08X)\n", __func__, objectFileImage, moduleName, options); + + dyld::clearErrorMessage(); + try { + if ( (options & NSLINKMODULE_OPTION_CAN_UNLOAD) != 0 ) + objectFileImage->image->setCanUnload(); + + // NSLinkModule allows a bundle to be link multpile times + // each link causes the bundle to be copied to a new address + if ( objectFileImage->image->isLinked() ) { + // already linked, so clone a new one and link it + objectFileImage->image = dyld::cloneImage(objectFileImage->image); + } + + // for memory based images, set moduleName as the name anyone calling _dyld_get_image_name() will see + if ( objectFileImage->image->getPath() == NULL ) { + objectFileImage->image->setPath(moduleName); + // dyld has NULL paths in image info array + dyld_image_info info; + info.imageLoadAddress = objectFileImage->image->machHeader(); + info.imageFilePath = moduleName; + info.imageFileModDate = 0; + addImagesToAllImages(1, &info); + } + + // support private bundles + if ( (options & NSLINKMODULE_OPTION_PRIVATE) != 0 ) + objectFileImage->image->setHideExports(); + + // set up linking options + bool forceLazysBound = ( (options & NSLINKMODULE_OPTION_BINDNOW) != 0 ); + + // load libraries, rebase, bind, to make this image usable + dyld::link(objectFileImage->image, forceLazysBound, false, ImageLoader::RPathChain(NULL,NULL), UINT32_MAX); + + // bump reference count to keep this bundle from being garbage collected + objectFileImage->image->incrementDlopenReferenceCount(); + + // run initializers unless magic flag says not to + if ( (options & NSLINKMODULE_OPTION_DONT_CALL_MOD_INIT_ROUTINES) == 0 ) + dyld::runInitializers(objectFileImage->image); + + return ImageLoaderToNSModule(objectFileImage->image); + } + catch (const char* msg) { + dyld::garbageCollectImages(); + if ( (options & NSLINKMODULE_OPTION_RETURN_ON_ERROR) == 0 ) + dyldAPIhalt(__func__, msg); + // not halting, so set error state for NSLinkEditError to find + setLastError(NSLinkEditOtherError, 0, moduleName, msg); + // dyld::link() deleted the image so lose our reference + objectFileImage->image = NULL; + free((void*)msg); + return NULL; + } +} + + +#if OLD_LIBSYSTEM_SUPPORT +// This is for compatibility with old libSystems (libdyld.a) which process ObjectFileImages outside dyld +static NSModule _dyld_link_module(NSObjectFileImage object_addr, size_t object_size, const char* moduleName, uint32_t options) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s(%p, \"%s\", 0x%08X)\n", "NSLinkModule", object_addr, moduleName, options); // note name/args translation + ImageLoader* image = NULL; + dyld::clearErrorMessage(); + try { + const char* imageName = moduleName; + image = dyld::loadFromMemory((const uint8_t*)object_addr, object_size, imageName); + + if ( image != NULL ) { + // support private bundles + if ( (options & NSLINKMODULE_OPTION_PRIVATE) != 0 ) + image->setHideExports(); + + // set up linking options + bool forceLazysBound = ( (options & NSLINKMODULE_OPTION_BINDNOW) != 0 ); + + // load libraries, rebase, bind, to make this image usable + dyld::link(image, forceLazysBound, false, ImageLoader::RPathChain(NULL,NULL), UINT32_MAX); + + // run initializers unless magic flag says not to + if ( (options & NSLINKMODULE_OPTION_DONT_CALL_MOD_INIT_ROUTINES) == 0 ) + dyld::runInitializers(image); + } + } + catch (const char* msg) { + if ( (options & NSLINKMODULE_OPTION_RETURN_ON_ERROR) == 0 ) + dyldAPIhalt("NSLinkModule", msg); + // not halting, so set error state for NSLinkEditError to find + setLastError(NSLinkEditOtherError, 0, moduleName, msg); + // if image was created for this bundle, destroy it + if ( image != NULL ) { + dyld::removeImage(image); + ImageLoader::deleteImage(image); + } + image = NULL; + free((void*)msg); + } + return ImageLoaderToNSModule(image); +} +#endif + +NSSymbol NSLookupSymbolInModule(NSModule module, const char* symbolName) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s(%p, \"%s\")\n", __func__, (void *)module, symbolName); + ImageLoader* image = NSModuleToImageLoader(module); + if ( image == NULL ) + return NULL; + return SymbolToNSSymbol(image->findExportedSymbol(symbolName, false, NULL)); +} + +const char* NSNameOfModule(NSModule module) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s(%p)\n", __func__, module); + ImageLoader* image = NSModuleToImageLoader(module); + if ( image == NULL ) + return NULL; + return image->getPath(); +} + +const char* NSLibraryNameForModule(NSModule module) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s(%p)\n", __func__, module); + ImageLoader* image = NSModuleToImageLoader(module); + if ( image == NULL ) + return NULL; + return image->getPath(); +} + +bool NSUnLinkModule(NSModule module, uint32_t options) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s(%p, 0x%08X)\n", __func__, module, options); + if ( module == NULL ) + return false; + ImageLoader* image = NSModuleToImageLoader(module); + if ( image == NULL ) + return false; + dyld::runImageStaticTerminators(image); + if ( (dyld::gLibSystemHelpers != NULL) && (dyld::gLibSystemHelpers->version >= 13) ) { + __cxa_range_t ranges[image->segmentCount()]; + int rangeCount = 0; + for (unsigned int j=0; j < image->segmentCount(); ++j) { + if ( !image->segExecutable(j) ) + continue; + ranges[rangeCount].addr = (const void*)image->segActualLoadAddress(j); + ranges[rangeCount].length = image->segSize(j); + ++rangeCount; + } + (*dyld::gLibSystemHelpers->cxa_finalize_ranges)(ranges, rangeCount); + } + dyld::removeImage(image); + + if ( (options & NSUNLINKMODULE_OPTION_KEEP_MEMORY_MAPPED) != 0 ) + image->setLeaveMapped(); + + // TODO: NSUNLINKMODULE_OPTION_RESET_LAZY_REFERENCES + + // Only delete image if there is no ofi referencing it + // That means the ofi was destroyed after linking, so no one is left to delete this image + const int ofiCount = sObjectFileImages.size(); + bool found = false; + for (int i=0; i < ofiCount; ++i) { + NSObjectFileImage ofi = sObjectFileImages[i]; + if ( ofi->image == image ) + found = true; + } + if ( !found ) + ImageLoader::deleteImage(image); + + return true; +} + +// internal name and parameters do not match public name and parameters... +static void _dyld_install_handlers(void* undefined, void* multiple, void* linkEdit) +{ + if ( dyld::gLogAPIs ) + dyld::log("NSLinkEditErrorHandlers()\n"); + + dyld::registerUndefinedHandler((dyld::UndefinedHandler)undefined); + // no support for multiple or linkedit handlers +} + + + + +void NSLinkEditError(NSLinkEditErrors* c, int* errorNumber, const char** fileName, const char** errorString) +{ + // FIXME FIXME + *c = sLastErrorFileCode; + *errorNumber = sLastErrorNo; + *fileName = sLastErrorFilePath; + *errorString = dyld::getErrorMessage(); +} + + + +static void _dyld_register_binding_handler(void * (*bindingHandler)(const char *, const char *, void *), ImageLoader::BindingOptions bindingOptions) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s()\n", __func__); + dyld::gLinkContext.bindingHandler = bindingHandler; + dyld::gLinkContext.bindingOptions = bindingOptions; +} + +#endif //DEPRECATED_APIS_SUPPORTED + + +// Call by fork() in libSystem after the kernel trap is done on the child side +void _dyld_fork_child() +{ + if ( dyld::gLogAPIs ) + dyld::log("%s()\n", __func__); + // The implementation of fork() in libSystem knows to reset the variable mach_task_self_ + // in libSystem for the child of a fork. But dyld is built with a static copy + // of libc.a and has its own copy of mach_task_self_ which we reset here. + // + // In mach_init.h mach_task_self() is #defined to mach_task_self_ and + // in mach_init() mach_task_self_ is initialized to task_self_trap(). + // + extern mach_port_t mach_task_self_; + mach_task_self_ = task_self_trap(); + + // If dyld is sending load/unload notices to CoreSymbolication, the shared memory + // page is not copied on fork. + // NULL the CoreSymbolication shared memory pointer to prevent a crash. + dyld::gProcessInfo->coreSymbolicationShmPage = NULL; + // for safety, make sure child starts with clean systemOrderFlag + dyld::gProcessInfo->systemOrderFlag = 0; +} + + +#if DEPRECATED_APIS_SUPPORTED +// returns true if prebinding was used in main executable +bool _dyld_launched_prebound() +{ + if ( dyld::gLogAPIs ) + dyld::log("%s()\n", __func__); + + // ����if we deprecate prebinding, we may want to consider always returning true or false here + return dyld::mainExecutablePrebound(); +} + + +// +// _dyld_NSMakePrivateModulePublic() is the dyld side of the hack +// NSMakePrivateModulePublic() needed for the dlopen() to turn it's +// RTLD_LOCAL handles into RTLD_GLOBAL. It just simply turns off the private +// flag on the image for this module. If the module was found and it was +// private then everything worked and TRUE is returned else FALSE is returned. +// +static bool NSMakePrivateModulePublic(NSModule module) +{ + ImageLoader* image = NSModuleToImageLoader(module); + if ( image != NULL ) { + if ( image->hasHiddenExports() ) { + image->setHideExports(false); + return true; + } + } + return false; +} + +#endif // DEPRECATED_APIS_SUPPORTED + +int _dyld_func_lookup(const char* name, void** address) +{ + for (const dyld_func* p = dyld_funcs; p->name != NULL; ++p) { + if ( strcmp(p->name, name) == 0 ) { + if( p->implementation == unimplemented ) + dyld::log("unimplemented dyld function: %s\n", p->name); + *address = p->implementation; + return true; + } + } + *address = 0; + return false; +} + + +static void registerThreadHelpers(const dyld::LibSystemHelpers* helpers) +{ + dyld::gLibSystemHelpers = helpers; + +#if !SUPPORT_ZERO_COST_EXCEPTIONS + if ( helpers->version >= 5 ) { + // create key use by dyld exception handling + pthread_key_t key; + int result = helpers->pthread_key_create(&key, NULL); + if ( result == 0 ) + __Unwind_SjLj_SetThreadKey(key); + } +#endif +} + + +static void dlerrorClear() +{ + if ( dyld::gLibSystemHelpers != NULL ) { + // dlerror buffer leak + // dlerrorClear() should not force allocation, but zero it if already allocated + if ( dyld::gLibSystemHelpers->version >= 10 ) { + if ( ! (*dyld::gLibSystemHelpers->hasPerThreadBufferFor_dlerror)() ) + return; + } + + // first char of buffer is flag whether string (starting at second char) is valid + char* buffer = (*dyld::gLibSystemHelpers->getThreadBufferFor_dlerror)(2); + buffer[0] = '\0'; + buffer[1] = '\0'; + } +} + +static void dlerrorSet(const char* msg) +{ + if ( dyld::gLibSystemHelpers != NULL ) { + // first char of buffer is flag whether string (starting at second char) is valid + char* buffer = (*dyld::gLibSystemHelpers->getThreadBufferFor_dlerror)(strlen(msg)+2); + buffer[0] = '\1'; + strcpy(&buffer[1], msg); + } +} + + +bool dlopen_preflight(const char* path) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s(%s)\n", __func__, path); + + dlerrorClear(); + + CRSetCrashLogMessage("dyld: in dlopen_preflight()"); + + const bool leafName = (strchr(path, '/') == NULL); + const bool absolutePath = (path[0] == '/'); +#if __IPHONE_OS_VERSION_MIN_REQUIRED + char canonicalPath[PATH_MAX]; + // dlopen() not opening frameworks from shared cache with // or ./ in path + if ( !leafName ) { + // make path canonical if it contains a // or ./ + if ( (strstr(path, "//") != NULL) || (strstr(path, "./") != NULL) ) { + const char* lastSlash = strrchr(path, '/'); + char dirPath[PATH_MAX]; + if ( strlcpy(dirPath, path, sizeof(dirPath)) < sizeof(dirPath) ) { + dirPath[lastSlash-path] = '\0'; + if ( realpath(dirPath, canonicalPath) ) { + strlcat(canonicalPath, "/", sizeof(canonicalPath)); + if ( strlcat(canonicalPath, lastSlash+1, sizeof(canonicalPath)) < sizeof(canonicalPath) ) { + // if all fit in buffer, use new canonical path + path = canonicalPath; + } + } + } + } + } +#endif +#if SUPPORT_ACCELERATE_TABLES + if ( dyld::isPathInCache(path) ) + return true; +#endif + +#if DYLD_SHARED_CACHE_SUPPORT + // dlopen_preflight() on image in shared cache leaves it loaded but not objc initialized + // if requested path is to something in the dyld shared cache, always succeed + if ( dyld::inSharedCache(path) ) + return true; +#endif + + bool result = false; + std::vector rpathsFromCallerImage; + try { + void* callerAddress = __builtin_return_address(1); // note layers: 1: real client, 0: libSystem glue + ImageLoader* callerImage = dyld::findImageContainingAddress(callerAddress); + // for dlopen, use rpath from caller image and from main executable + if ( callerImage != NULL ) + callerImage->getRPaths(dyld::gLinkContext, rpathsFromCallerImage); + ImageLoader::RPathChain callersRPaths(NULL, &rpathsFromCallerImage); + if ( callerImage != dyld::mainExecutable() ) { + dyld::mainExecutable()->getRPaths(dyld::gLinkContext, rpathsFromCallerImage); + } + + ImageLoader* image = NULL; + dyld::LoadContext context; + context.useSearchPaths = true; + context.useFallbackPaths= leafName; // a partial path implies don't use fallback paths + context.useLdLibraryPath= leafName; // a leafname implies should search + context.implicitRPath = !absolutePath; // a non-absolute path implies try rpath searching + context.matchByInstallName = true; + context.dontLoad = false; + context.mustBeBundle = false; + context.mustBeDylib = false; + context.canBePIE = true; + context.origin = callerImage != NULL ? callerImage->getPath() : NULL; // caller's image's path + context.rpath = &callersRPaths; // rpaths from caller and main executable + + unsigned cacheIndex; + image = load(path, context, cacheIndex); + if ( image != NULL ) { + dyld::preflight(image, callersRPaths, cacheIndex); // image object deleted by dyld::preflight() + result = true; + } + } + catch (const char* msg) { + const char* str = dyld::mkstringf("dlopen_preflight(%s): %s", path, msg); + dlerrorSet(str); + free((void*)str); + free((void*)msg); // our free() will do nothing if msg is a string literal + } + // free rpaths (getRPaths() malloc'ed each string) + for(std::vector::iterator it=rpathsFromCallerImage.begin(); it != rpathsFromCallerImage.end(); ++it) { + const char* str = *it; + free((void*)str); + } + CRSetCrashLogMessage(NULL); + return result; +} + +#if SUPPORT_ACCELERATE_TABLES +bool static callerIsNonOSApp(void* callerAddress, const char** shortName) +{ + *shortName = NULL; + const mach_header* unusedMh; + const char* unusedPath; + unsigned unusedIndex; + // any address in shared cache is not from app + if ( dyld::addressInCache(callerAddress, &unusedMh, &unusedPath, &unusedIndex) ) + return false; + + ImageLoader* callerImage = dyld::findImageContainingAddress(callerAddress); + if ( callerImage == NULL ) + return false; + + *shortName = callerImage->getShortName(); + return ( strncmp(callerImage->getPath(), "/var/containers/", 16) == 0 ); +} +#endif + +void* dlopen(const char* path, int mode) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s(%s, 0x%08X)\n", __func__, ((path==NULL) ? "NULL" : path), mode); + +#if SUPPORT_ACCELERATE_TABLES + if ( dyld::gLogAppAPIs ) { + void* callerAddress = __builtin_return_address(1); // note layers: 1: real client, 0: libSystem glue + const char* shortName; + if ( callerIsNonOSApp(callerAddress, &shortName) ) { + dyld::log("%s: %s(%s, 0x%08X)\n", shortName, __func__, ((path==NULL) ? "NULL" : path), mode); + } + } +#endif + + dlerrorClear(); + + // passing NULL for path means return magic object + if ( path == NULL ) { + // RTLD_FIRST means any dlsym() calls on the handle should only search that handle and not subsequent images + if ( (mode & RTLD_FIRST) != 0 ) + return RTLD_MAIN_ONLY; + else + return RTLD_DEFAULT; + } + + // acquire global dyld lock (dlopen is special - libSystem glue does not do locking) + bool lockHeld = false; + if ( (dyld::gLibSystemHelpers != NULL) && (dyld::gLibSystemHelpers->version >= 4) ) { + dyld::gLibSystemHelpers->acquireGlobalDyldLock(); + CRSetCrashLogMessage("dyld: in dlopen()"); + lockHeld = true; + } + + void* result = NULL; + const bool leafName = (strchr(path, '/') == NULL); + const bool absolutePath = (path[0] == '/'); +#if __IPHONE_OS_VERSION_MIN_REQUIRED + char canonicalPath[PATH_MAX]; + // dlopen() not opening frameworks from shared cache with // or ./ in path + if ( !leafName ) { + // make path canonical if it contains a // or ./ + if ( (strstr(path, "//") != NULL) || (strstr(path, "./") != NULL) ) { + const char* lastSlash = strrchr(path, '/'); + char dirPath[PATH_MAX]; + if ( strlcpy(dirPath, path, sizeof(dirPath)) < sizeof(dirPath) ) { + dirPath[lastSlash-path] = '\0'; + if ( realpath(dirPath, canonicalPath) ) { + strlcat(canonicalPath, "/", sizeof(canonicalPath)); + if ( strlcat(canonicalPath, lastSlash+1, sizeof(canonicalPath)) < sizeof(canonicalPath) ) { + // if all fit in buffer, use new canonical path + path = canonicalPath; + } + } + } + } + } +#endif +#if SUPPORT_ACCELERATE_TABLES + if ( dyld::dlopenFromCache(path, mode, &result) ) { + // Note: dlopenFromCache() releases the lock + if ( dyld::gLogAPIs ) + dyld::log(" %s(%s) ==> %p\n", __func__, path, result); + return result; + } +#endif + + ImageLoader* image = NULL; + std::vector rpathsFromCallerImage; + ImageLoader::RPathChain callersRPaths(NULL, &rpathsFromCallerImage); + try { + void* callerAddress = __builtin_return_address(1); // note layers: 1: real client, 0: libSystem glue + ImageLoader* callerImage = dyld::findImageContainingAddress(callerAddress); + if ( (mode & RTLD_NOLOAD) == 0 ) { + // for dlopen, use rpath from caller image and from main executable + if ( callerImage != NULL ) + callerImage->getRPaths(dyld::gLinkContext, rpathsFromCallerImage); + if ( callerImage != dyld::mainExecutable() ) + dyld::mainExecutable()->getRPaths(dyld::gLinkContext, rpathsFromCallerImage); + } + + dyld::LoadContext context; + context.useSearchPaths = true; + context.useFallbackPaths= leafName; // a partial path means no fallback paths + context.useLdLibraryPath= leafName; // a leafname implies should search + context.implicitRPath = !absolutePath; // a non-absolute path implies try rpath searching + context.matchByInstallName = true; + context.dontLoad = ( (mode & RTLD_NOLOAD) != 0 ); + context.mustBeBundle = false; + context.mustBeDylib = false; + context.canBePIE = true; + context.origin = callerImage != NULL ? callerImage->getPath() : NULL; // caller's image's path + context.rpath = &callersRPaths; // rpaths from caller and main executable + + unsigned cacheIndex; + image = load(path, context, cacheIndex); +#if SUPPORT_ACCELERATE_TABLES + if ( (image != NULL) && (cacheIndex != UINT32_MAX) ) { + if ( dyld::makeCacheHandle(image, cacheIndex, mode, &result) ) { + if ( dyld::gLogAPIs ) + dyld::log(" %s(%s) ==> %p\n", __func__, path, result); + if ( lockHeld ) + dyld::gLibSystemHelpers->releaseGlobalDyldLock(); + return result; + } + } +#endif + if ( image != NULL ) { + // bump reference count. Do this before link() so that if an initializer calls dlopen and fails + // this image is not garbage collected + image->incrementDlopenReferenceCount(); + // link in all dependents + if ( (mode & RTLD_NOLOAD) == 0 ) { + bool alreadyLinked = image->isLinked(); + bool forceLazysBound = ( (mode & RTLD_NOW) != 0 ); + dyld::link(image, forceLazysBound, false, callersRPaths, cacheIndex); + if ( ! alreadyLinked ) { + // only hide exports if image is not already in use + if ( (mode & RTLD_LOCAL) != 0 ) + image->setHideExports(true); + } + } + + // RTLD_NODELETE means don't unmap image even after dlclosed. This is what dlcompat did on Mac OS X 10.3 + // On other *nix OS's, it means dlclose() should do nothing, but the handle should be invalidated. + // The subtle differences are: + // 1) if the image has any termination routines, whether they are run during dlclose or when the process terminates + // 2) If someone does a supsequent dlopen() on the same image, whether the same address should be used. + if ( (mode & RTLD_NODELETE) != 0 ) + image->setLeaveMapped(); + + // release global dyld lock early, this enables initializers to do threaded operations + if ( lockHeld ) { + CRSetCrashLogMessage(NULL); + dyld::gLibSystemHelpers->releaseGlobalDyldLock(); + lockHeld = false; + } + + // RTLD_NOLOAD means dlopen should fail unless path is already loaded. + // don't run initializers when RTLD_NOLOAD is set. This only matters if dlopen() is + // called from within an initializer because it can cause initializers to run + // out of order. Most uses of RTLD_NOLOAD are "probes". If they want initialzers + // to run, then don't use RTLD_NOLOAD. + if ( (mode & RTLD_NOLOAD) == 0 ) { + // run initializers + dyld::runInitializers(image); + } + + // RTLD_FIRST means any dlsym() calls on the handle should only search that handle and not subsequent images + // this is tracked by setting the low bit of the handle, which is usually zero by malloc alignment + if ( (mode & RTLD_FIRST) != 0 ) + result = (void*)(((uintptr_t)image)|1); + else + result = image; + } + } + catch (const char* msg) { + if ( image != NULL ) { + // load() succeeded but, link() failed + // back down reference count and do GC + image->decrementDlopenReferenceCount(); + if ( image->dlopenCount() == 0 ) + dyld::garbageCollectImages(); + } + const char* str = dyld::mkstringf("dlopen(%s, %d): %s", path, mode, msg); + if ( dyld::gLogAPIs ) + dyld::log(" %s() failed, error: '%s'\n", __func__, str); + dlerrorSet(str); + free((void*)str); + free((void*)msg); // our free() will do nothing if msg is a string literal + result = NULL; + } + // free rpaths (getRPaths() malloc'ed each string) + for(std::vector::iterator it=rpathsFromCallerImage.begin(); it != rpathsFromCallerImage.end(); ++it) { + const char* str = *it; + free((void*)str); + } + + // when context.dontLoad is set, load() returns NULL instead of throwing an exception + if ( (mode & RTLD_NOLOAD) && (result == NULL) ) { + dlerrorSet("image not already loaded"); + } + + if ( lockHeld ) { + CRSetCrashLogMessage(NULL); + dyld::gLibSystemHelpers->releaseGlobalDyldLock(); + } + if ( dyld::gLogAPIs && (result != NULL) ) + dyld::log(" %s(%s) ==> %p\n", __func__, path, result); + return result; +} + + + +int dlclose(void* handle) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s(%p)\n", __func__, handle); + + // silently accept magic handles for main executable + if ( handle == RTLD_MAIN_ONLY ) + return 0; + if ( handle == RTLD_DEFAULT ) + return 0; + + ImageLoader* image = (ImageLoader*)(((uintptr_t)handle) & (-4)); // clear mode bits + if ( dyld::validImage(image) ) { + dlerrorClear(); + // decrement use count + if ( image->decrementDlopenReferenceCount() ) { + dlerrorSet("dlclose() called too many times"); + return -1; + } + // remove image if reference count went to zero + if ( image->dlopenCount() == 0 ) + dyld::garbageCollectImages(); + return 0; + } + else { + dlerrorSet("invalid handle passed to dlclose()"); + return -1; + } +} + + + +int dladdr(const void* address, Dl_info* info) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s(%p, %p)\n", __func__, address, info); + + CRSetCrashLogMessage("dyld: in dladdr()"); +#if SUPPORT_ACCELERATE_TABLES + if ( dyld::dladdrFromCache(address, info) ) { + CRSetCrashLogMessage(NULL); + return 1; // success + } +#endif + + ImageLoader* image = dyld::findImageContainingAddress(address); + if ( image != NULL ) { + info->dli_fname = image->getRealPath(); + info->dli_fbase = (void*)image->machHeader(); + if ( address == info->dli_fbase ) { + // special case lookup of header + info->dli_sname = "__dso_handle"; + info->dli_saddr = info->dli_fbase; + CRSetCrashLogMessage(NULL); + return 1; // success + } + // find closest symbol in the image + info->dli_sname = image->findClosestSymbol(address, (const void**)&info->dli_saddr); + // never return the mach_header symbol + if ( info->dli_saddr == info->dli_fbase ) { + info->dli_sname = NULL; + info->dli_saddr = NULL; + CRSetCrashLogMessage(NULL); + return 1; // success + } + if ( info->dli_sname != NULL ) { + if ( info->dli_sname[0] == '_' ) + info->dli_sname = info->dli_sname +1; // strip off leading underscore + //dyld::log("dladdr(%p) => %p %s\n", address, info->dli_saddr, info->dli_sname); + CRSetCrashLogMessage(NULL); + return 1; // success + } + info->dli_sname = NULL; + info->dli_saddr = NULL; + CRSetCrashLogMessage(NULL); + return 1; // success + } + CRSetCrashLogMessage(NULL); + return 0; // failure +} + + +char* dlerror() +{ + if ( dyld::gLogAPIs ) + dyld::log("%s()\n", __func__); + + if ( dyld::gLibSystemHelpers != NULL ) { + // if using newer libdyld.dylib and buffer if buffer not yet allocated, return NULL + if ( dyld::gLibSystemHelpers->version >= 10 ) { + if ( ! (*dyld::gLibSystemHelpers->hasPerThreadBufferFor_dlerror)() ) + return NULL; + } + + // first char of buffer is flag whether string (starting at second char) is valid + char* buffer = (*dyld::gLibSystemHelpers->getThreadBufferFor_dlerror)(2); + if ( buffer[0] != '\0' ) { // if valid buffer + buffer[0] = '\0'; // mark invalid, so next call to dlerror returns NULL + return &buffer[1]; // return message + } + } + return NULL; +} + +void* dlsym(void* handle, const char* symbolName) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s(%p, %s)\n", __func__, handle, symbolName); + +#if SUPPORT_ACCELERATE_TABLES + if ( dyld::gLogAppAPIs ) { + void* callerAddress = __builtin_return_address(1); // note layers: 1: real client, 0: libSystem glue + const char* shortName; + if ( callerIsNonOSApp(callerAddress, &shortName) ) { + dyld::log("%s: %s(%p, %s)\n", shortName, __func__, handle, symbolName); + } + } +#endif + + CRSetCrashLogMessage("dyld: in dlsym()"); + dlerrorClear(); + + const ImageLoader* image; + const ImageLoader::Symbol* sym; + void* result; + + // dlsym() assumes symbolName passed in is same as in C source code + // dyld assumes all symbol names have an underscore prefix + char underscoredName[strlen(symbolName)+2]; + underscoredName[0] = '_'; + strcpy(&underscoredName[1], symbolName); + + // magic "search all" handle + if ( handle == RTLD_DEFAULT ) { + if ( dyld::flatFindExportedSymbol(underscoredName, &sym, &image) ) { + CRSetCrashLogMessage(NULL); + result = (void*)image->getExportedSymbolAddress(sym, dyld::gLinkContext, NULL, false, underscoredName); + if ( dyld::gLogAPIs ) + dyld::log(" %s(RTLD_DEFAULT, %s) ==> %p\n", __func__, symbolName, result); + return result; + } + const char* str = dyld::mkstringf("dlsym(RTLD_DEFAULT, %s): symbol not found", symbolName); + dlerrorSet(str); + free((void*)str); + CRSetCrashLogMessage(NULL); + if ( dyld::gLogAPIs ) + dyld::log(" %s(RTLD_DEFAULT, %s) ==> NULL\n", __func__, symbolName); + return NULL; + } + + // magic "search only main executable" handle + else if ( handle == RTLD_MAIN_ONLY ) { + image = dyld::mainExecutable(); + sym = image->findExportedSymbol(underscoredName, true, &image); // search RTLD_FIRST way + if ( sym != NULL ) { + CRSetCrashLogMessage(NULL); + result = (void*)image->getExportedSymbolAddress(sym, dyld::gLinkContext, NULL, false, underscoredName); + if ( dyld::gLogAPIs ) + dyld::log(" %s(RTLD_MAIN_ONLY, %s) ==> %p\n", __func__, symbolName, result); + return result; + } + const char* str = dyld::mkstringf("dlsym(RTLD_MAIN_ONLY, %s): symbol not found", symbolName); + dlerrorSet(str); + free((void*)str); + CRSetCrashLogMessage(NULL); + if ( dyld::gLogAPIs ) + dyld::log(" %s(RTLD_MAIN_ONLY, %s) ==> NULL\n", __func__, symbolName); + return NULL; + } + + // magic "search what I would see" handle + else if ( handle == RTLD_NEXT ) { + void* callerAddress = __builtin_return_address(1); // note layers: 1: real client, 0: libSystem glue +#if SUPPORT_ACCELERATE_TABLES + const mach_header* mh; + const char* path; + unsigned index; + if ( dyld::addressInCache(callerAddress, &mh, &path, &index) ) { + // if dylib in cache is calling dlsym(RTLD_NEXT,xxx) handle search differently + result = dyld::dlsymFromCache(RTLD_NEXT, underscoredName, index); + if ( dyld::gLogAPIs ) + dyld::log(" %s(RTLD_NEXT, %s) ==> %p\n", __func__, symbolName, result); + return result; + } +#endif + ImageLoader* callerImage = dyld::findImageContainingAddress(callerAddress); + sym = callerImage->findExportedSymbolInDependentImages(underscoredName, dyld::gLinkContext, &image); // don't search image, but do search what it links against + if ( sym != NULL ) { + CRSetCrashLogMessage(NULL); + result = (void*)image->getExportedSymbolAddress(sym, dyld::gLinkContext , callerImage, false, underscoredName); + if ( dyld::gLogAPIs ) + dyld::log(" %s(RTLD_NEXT, %s) ==> %p\n", __func__, symbolName, result); + return result; + } + const char* str = dyld::mkstringf("dlsym(RTLD_NEXT, %s): symbol not found", symbolName); + dlerrorSet(str); + free((void*)str); + CRSetCrashLogMessage(NULL); + if ( dyld::gLogAPIs ) + dyld::log(" %s(RTLD_NEXT, %s) ==> NULL\n", __func__, symbolName); + return NULL; + } + // magic "search me, then what I would see" handle + else if ( handle == RTLD_SELF ) { + void* callerAddress = __builtin_return_address(1); // note layers: 1: real client, 0: libSystem glue +#if SUPPORT_ACCELERATE_TABLES + const mach_header* mh; + const char* path; + unsigned index; + if ( dyld::addressInCache(callerAddress, &mh, &path, &index) ) { + // if dylib in cache is calling dlsym(RTLD_SELF,xxx) handle search differently + result = dyld::dlsymFromCache(RTLD_SELF, underscoredName, index); + if ( dyld::gLogAPIs ) + dyld::log(" %s(RTLD_SELF, %s) ==> %p\n", __func__, symbolName, result); + return result; + } +#endif + ImageLoader* callerImage = dyld::findImageContainingAddress(callerAddress); + sym = callerImage->findExportedSymbolInImageOrDependentImages(underscoredName, dyld::gLinkContext, &image); // search image and what it links against + if ( sym != NULL ) { + CRSetCrashLogMessage(NULL); + result = (void*)image->getExportedSymbolAddress(sym, dyld::gLinkContext, callerImage, false, underscoredName); + if ( dyld::gLogAPIs ) + dyld::log(" %s(RTLD_SELF, %s) ==> %p\n", __func__, symbolName, result); + return result; + } + const char* str = dyld::mkstringf("dlsym(RTLD_SELF, %s): symbol not found", symbolName); + dlerrorSet(str); + free((void*)str); + CRSetCrashLogMessage(NULL); + if ( dyld::gLogAPIs ) + dyld::log(" %s(RTLD_SELF, %s) ==> NULL\n", __func__, symbolName); + return NULL; + } +#if SUPPORT_ACCELERATE_TABLES + // check for mega dylib handle + else if ( dyld::isCacheHandle(handle) ) { + result = dyld::dlsymFromCache(handle, underscoredName, 0); + if ( dyld::gLogAPIs ) + dyld::log(" %s(%p, %s) ==> %p\n", __func__, handle, symbolName, result); + return result; + } +#endif + // real handle + image = (ImageLoader*)(((uintptr_t)handle) & (-4)); // clear mode bits + if ( dyld::validImage(image) ) { + if ( (((uintptr_t)handle) & 1) != 0 ) + sym = image->findExportedSymbol(underscoredName, true, &image); // search RTLD_FIRST way + else + sym = image->findExportedSymbolInImageOrDependentImages(underscoredName, dyld::gLinkContext, &image); // search image and what it links against + + if ( sym != NULL ) { + CRSetCrashLogMessage(NULL); + ImageLoader* callerImage = NULL; + if ( sDynamicInterposing ) { + // only take time to look up caller, if dynamic interposing in use + void* callerAddress = __builtin_return_address(1); // note layers: 1: real client, 0: libSystem glue + callerImage = dyld::findImageContainingAddress(callerAddress); + } + result = (void*)image->getExportedSymbolAddress(sym, dyld::gLinkContext, callerImage, false, underscoredName); + if ( dyld::gLogAPIs ) + dyld::log(" %s(%p, %s) ==> %p\n", __func__, handle, symbolName, result); + return result; + } + const char* str = dyld::mkstringf("dlsym(%p, %s): symbol not found", handle, symbolName); + dlerrorSet(str); + free((void*)str); + } + else { + dlerrorSet("invalid handle passed to dlsym()"); + } + CRSetCrashLogMessage(NULL); + if ( dyld::gLogAPIs ) + dyld::log(" %s(%p, %s) ==> NULL\n", __func__, handle, symbolName); + return NULL; +} + + + + + + + + + + +const struct dyld_all_image_infos* _dyld_get_all_image_infos() +{ + return dyld::gProcessInfo; +} + + +#if SUPPORT_ZERO_COST_EXCEPTIONS +static bool client_dyld_find_unwind_sections(void* addr, dyld_unwind_sections* info) +{ + //if ( dyld::gLogAPIs ) + // dyld::log("%s(%p, %p)\n", __func__, addr, info); + +#if SUPPORT_ACCELERATE_TABLES + if ( dyld::findUnwindSections(addr, info) ) + return true; +#endif + ImageLoader* image = dyld::findImageContainingAddress(addr); + if ( image != NULL ) { + image->getUnwindInfo(info); + return true; + } + return false; +} +#endif + + +const char* dyld_image_path_containing_address(const void* address) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s(%p)\n", __func__, address); + +#if SUPPORT_ACCELERATE_TABLES + const mach_header* mh; + const char* path; + if ( dyld::addressInCache(address, &mh, &path) ) + return path; +#endif + + ImageLoader* image = dyld::findImageContainingAddress(address); + if ( image != NULL ) + return image->getRealPath(); + return NULL; +} + + + +bool dyld_shared_cache_some_image_overridden() +{ + #if DYLD_SHARED_CACHE_SUPPORT + return dyld::gSharedCacheOverridden; + #else + return true; + #endif +} + + +void dyld_dynamic_interpose(const struct mach_header* mh, const struct dyld_interpose_tuple array[], size_t count) +{ + if ( mh == NULL ) + return; + if ( array == NULL ) + return; + if ( count == 0 ) + return; + ImageLoader* image = dyld::findImageByMachHeader(mh); + if ( image == NULL ) + return; + + // make pass at bound references in this image and update them + dyld::gLinkContext.dynamicInterposeArray = array; + dyld::gLinkContext.dynamicInterposeCount = count; + image->dynamicInterpose(dyld::gLinkContext); + dyld::gLinkContext.dynamicInterposeArray = NULL; + dyld::gLinkContext.dynamicInterposeCount = 0; + + // leave interposing info so any future (lazy) binding will get it too + image->addDynamicInterposingTuples(array, count); + + sDynamicInterposing = true; +} + + +bool _dyld_is_memory_immutable(const void* addr, size_t length) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s(%p, %ld)\n", __func__, addr, length); + + uintptr_t checkStart = (uintptr_t)addr; + uintptr_t checkEnd = checkStart + length; + +#if DYLD_SHARED_CACHE_SUPPORT + // quick check to see if in r/o region of shared cache. If so return true. + if ( dyld_shared_cache_ranges.sharedRegionsCount > 2 ) { + uintptr_t roStart = dyld_shared_cache_ranges.ranges[0].start; + uintptr_t roEnd = roStart + dyld_shared_cache_ranges.ranges[0].length; + if ( (roStart < checkStart) && (checkEnd < roEnd) ) + return true; + } +#endif + + // Otherwise find if addr is in a dyld loaded image + ImageLoader* image = dyld::findImageContainingAddress(addr); + if ( image != NULL ) { + // already checked for r/o portion of cache + if ( image->inSharedCache() ) + return false; + if ( !image->neverUnload() ) + return false; + for (unsigned i=0, e=image->segmentCount(); i < e; ++i) { + if ( (image->segActualLoadAddress(i) < checkStart) && (checkEnd < image->segActualEndAddress(i)) ) { + return !image->segWriteable(i); + } + } + } + return false; +} + + + +void _dyld_objc_notify_register(_dyld_objc_notify_mapped mapped, + _dyld_objc_notify_init init, + _dyld_objc_notify_unmapped unmapped) +{ + dyld::registerObjCNotifiers(mapped, init, unmapped); +} + + +bool _dyld_get_shared_cache_uuid(uuid_t uuid) +{ + return dyld::sharedCacheUUID(uuid); +} diff --git a/src/dyld-apple/src/dyldAPIsInLibSystem.cpp b/src/dyld-apple/src/dyldAPIsInLibSystem.cpp new file mode 100644 index 000000000..46c5c3af3 --- /dev/null +++ b/src/dyld-apple/src/dyldAPIsInLibSystem.cpp @@ -0,0 +1,1807 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2004-2012 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@ + */ + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include "mach-o/dyld_images.h" +#include "mach-o/dyld.h" +#include "mach-o/dyld_priv.h" +#include "dyld_cache_format.h" + +#include "ImageLoader.h" +#include "dyldLock.h" +#include "start_glue.h" + +extern "C" int __cxa_atexit(void (*func)(void *), void *arg, void *dso); +extern "C" void __cxa_finalize(const void *dso); +extern "C" void __cxa_finalize_ranges(const struct __cxa_range_t ranges[], int count); + + +#ifndef LC_VERSION_MIN_MACOSX + #define LC_VERSION_MIN_MACOSX 0x24 + struct version_min_command { + uint32_t cmd; /* LC_VERSION_MIN_MACOSX or + LC_VERSION_MIN_IPHONEOS */ + uint32_t cmdsize; /* sizeof(struct min_version_command) */ + uint32_t version; /* X.Y.Z is encoded in nibbles xxxx.yy.zz */ + uint32_t sdk; /* X.Y.Z is encoded in nibbles xxxx.yy.zz */ + }; +#endif + +#ifndef LC_VERSION_MIN_IPHONEOS + #define LC_VERSION_MIN_IPHONEOS 0x25 +#endif + +#ifndef LC_VERSION_MIN_TVOS + #define LC_VERSION_MIN_TVOS 0x2F +#endif + +#ifndef LC_VERSION_MIN_WATCHOS + #define LC_VERSION_MIN_WATCHOS 0x30 +#endif + + +#ifndef LC_LOAD_UPWARD_DYLIB + #define LC_LOAD_UPWARD_DYLIB (0x23|LC_REQ_DYLD) /* load of dylib whose initializers run later */ +#endif + + +// deprecated APIs are still availble on Mac OS X, but not on iPhone OS +#if __IPHONE_OS_VERSION_MIN_REQUIRED + #define DEPRECATED_APIS_SUPPORTED 0 +#else + #define DEPRECATED_APIS_SUPPORTED 1 +#endif + +/* + * names_match() takes an install_name from an LC_LOAD_DYLIB command and a + * libraryName (which is -lx or -framework Foo argument passed to the static + * link editor for the same library) and determines if they match. This depends + * on conventional use of names including major versioning. + */ +static +bool +names_match( +const char *install_name, +const char* libraryName) +{ + const char *basename; + unsigned long n; + + /* + * Conventional install names have these forms: + * /System/Library/Frameworks/AppKit.framework/Versions/A/Appkit + * /Local/Library/Frameworks/AppKit.framework/Appkit + * /lib/libsys_s.A.dylib + * /usr/lib/libsys_s.dylib + */ + basename = strrchr(install_name, '/'); + if(basename == NULL) + basename = install_name; + else + basename++; + + /* + * By checking the base name matching the library name we take care + * of the -framework cases. + */ + if(strcmp(basename, libraryName) == 0) + return true; + + /* + * Now check the base name for "lib" if so proceed to check for the + * -lx case dealing with a possible .X.dylib and a .dylib extension. + */ + if(strncmp(basename, "lib", 3) ==0){ + n = strlen(libraryName); + if(strncmp(basename+3, libraryName, n) == 0){ + if(strncmp(basename+3+n, ".dylib", 6) == 0) + return true; + if(basename[3+n] == '.' && + basename[3+n+1] != '\0' && + strncmp(basename+3+n+2, ".dylib", 6) == 0) + return true; + } + } + return false; +} + +#if DEPRECATED_APIS_SUPPORTED + +void NSInstallLinkEditErrorHandlers( +const NSLinkEditErrorHandlers* handlers) +{ + DYLD_LOCK_THIS_BLOCK; + typedef void (*ucallback_t)(const char* symbol_name); + typedef NSModule (*mcallback_t)(NSSymbol s, NSModule old, NSModule newhandler); + typedef void (*lcallback_t)(NSLinkEditErrors c, int errorNumber, + const char* fileName, const char* errorString); + static void (*p)(ucallback_t undefined, mcallback_t multiple, lcallback_t linkEdit) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_install_handlers", (void**)&p); + mcallback_t m = handlers->multiple; + p(handlers->undefined, m, handlers->linkEdit); +} + +const char* +NSNameOfModule( +NSModule module) +{ + DYLD_LOCK_THIS_BLOCK; + static const char* (*p)(NSModule module) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_NSNameOfModule", (void**)&p); + return(p(module)); +} + +const char* +NSLibraryNameForModule( +NSModule module) +{ + DYLD_LOCK_THIS_BLOCK; + static const char* (*p)(NSModule module) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_NSLibraryNameForModule", (void**)&p); + return(p(module)); +} + +bool +NSIsSymbolNameDefined( +const char* symbolName) +{ + DYLD_LOCK_THIS_BLOCK; + static bool (*p)(const char* symbolName) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_NSIsSymbolNameDefined", (void**)&p); + return(p(symbolName)); +} + +bool +NSIsSymbolNameDefinedWithHint( +const char* symbolName, +const char* libraryNameHint) +{ + DYLD_LOCK_THIS_BLOCK; + static bool (*p)(const char* symbolName, + const char* libraryNameHint) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_NSIsSymbolNameDefinedWithHint", (void**)&p); + return(p(symbolName, libraryNameHint)); +} + +bool +NSIsSymbolNameDefinedInImage( +const struct mach_header *image, +const char* symbolName) +{ + DYLD_LOCK_THIS_BLOCK; + static bool (*p)(const struct mach_header *image, + const char* symbolName) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_NSIsSymbolNameDefinedInImage", (void**)&p); + return(p(image, symbolName)); +} + +NSSymbol +NSLookupAndBindSymbol( +const char* symbolName) +{ + DYLD_LOCK_THIS_BLOCK; + static NSSymbol (*p)(const char* symbolName) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_NSLookupAndBindSymbol", (void**)&p); + return(p(symbolName)); +} + +NSSymbol +NSLookupAndBindSymbolWithHint( +const char* symbolName, +const char* libraryNameHint) +{ + DYLD_LOCK_THIS_BLOCK; + static NSSymbol (*p)(const char* symbolName, + const char* libraryNameHint) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_NSLookupAndBindSymbolWithHint", (void**)&p); + return(p(symbolName, libraryNameHint)); +} + +NSSymbol +NSLookupSymbolInModule( +NSModule module, +const char* symbolName) +{ + DYLD_LOCK_THIS_BLOCK; + static NSSymbol (*p)(NSModule module, const char* symbolName) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_NSLookupSymbolInModule", (void**)&p); + return(p(module, symbolName)); +} + +NSSymbol +NSLookupSymbolInImage( +const struct mach_header *image, +const char* symbolName, +uint32_t options) +{ + DYLD_LOCK_THIS_BLOCK; + static NSSymbol (*p)(const struct mach_header *image, + const char* symbolName, + uint32_t options) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_NSLookupSymbolInImage", (void**)&p); + return(p(image, symbolName, options)); +} + +const char* +NSNameOfSymbol( +NSSymbol symbol) +{ + DYLD_LOCK_THIS_BLOCK; + static char * (*p)(NSSymbol symbol) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_NSNameOfSymbol",(void**)&p); + return(p(symbol)); +} + +void * +NSAddressOfSymbol( +NSSymbol symbol) +{ + DYLD_LOCK_THIS_BLOCK; + static void * (*p)(NSSymbol symbol) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_NSAddressOfSymbol", (void**)&p); + return(p(symbol)); +} + +NSModule +NSModuleForSymbol( +NSSymbol symbol) +{ + DYLD_LOCK_THIS_BLOCK; + static NSModule (*p)(NSSymbol symbol) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_NSModuleForSymbol", (void**)&p); + return(p(symbol)); +} + +bool +NSAddLibrary( +const char* pathName) +{ + DYLD_LOCK_THIS_BLOCK; + static bool (*p)(const char* pathName) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_NSAddLibrary", (void**)&p); + return(p(pathName)); +} + +bool +NSAddLibraryWithSearching( +const char* pathName) +{ + DYLD_LOCK_THIS_BLOCK; + static bool (*p)(const char* pathName) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_NSAddLibraryWithSearching", (void**)&p); + return(p(pathName)); +} + +const struct mach_header * +NSAddImage( +const char* image_name, +uint32_t options) +{ + DYLD_LOCK_THIS_BLOCK; + static const struct mach_header * (*p)(const char* image_name, + uint32_t options) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_NSAddImage", (void**)&p); + return(p(image_name, options)); +} +#endif // DEPRECATED_APIS_SUPPORTED + +/* + * This routine returns the current version of the named shared library the + * executable it was built with. The libraryName parameter is the same as the + * -lx or -framework Foo argument passed to the static link editor when building + * the executable (with -lx it would be "x" and with -framework Foo it would be + * "Foo"). If this the executable was not built against the specified library + * it returns -1. It should be noted that if this only returns the value the + * current version of the named shared library the executable was built with + * and not a list of current versions that dependent libraries and bundles the + * program is using were built with. + */ +int32_t NSVersionOfLinkTimeLibrary(const char* libraryName) +{ + // Lazily call _NSGetMachExecuteHeader() and cache result +#if __LP64__ + static mach_header_64* mh = NULL; +#else + static mach_header* mh = NULL; +#endif + if ( mh == NULL ) + mh = _NSGetMachExecuteHeader(); +#if __LP64__ + const load_command* lc = (load_command*)((char*)mh + sizeof(mach_header_64)); +#else + const load_command* lc = (load_command*)((char*)mh + sizeof(mach_header)); +#endif + for(uint32_t i = 0; i < mh->ncmds; i++){ + switch ( lc->cmd ) { + case LC_LOAD_DYLIB: + case LC_LOAD_WEAK_DYLIB: + case LC_LOAD_UPWARD_DYLIB: + const dylib_command* dl = (dylib_command *)lc; + const char* install_name = (char*)dl + dl->dylib.name.offset; + if ( names_match(install_name, libraryName) ) + return dl->dylib.current_version; + break; + } + lc = (load_command*)((char*)lc + lc->cmdsize); + } + return (-1); +} + +/* + * This routine returns the current version of the named shared library the + * program it is running against. The libraryName parameter is the same as + * would be static link editor using the -lx or -framework Foo flags (with -lx + * it would be "x" and with -framework Foo it would be "Foo"). If the program + * is not using the specified library it returns -1. + */ +int32_t NSVersionOfRunTimeLibrary(const char* libraryName) +{ + uint32_t n = _dyld_image_count(); + for(uint32_t i = 0; i < n; i++){ + const mach_header* mh = _dyld_get_image_header(i); + if ( mh == NULL ) + continue; + if ( mh->filetype != MH_DYLIB ) + continue; +#if __LP64__ + const load_command* lc = (load_command*)((char*)mh + sizeof(mach_header_64)); +#else + const load_command* lc = (load_command*)((char*)mh + sizeof(mach_header)); +#endif + for(uint32_t j = 0; j < mh->ncmds; j++){ + if ( lc->cmd == LC_ID_DYLIB ) { + const dylib_command* dl = (dylib_command*)lc; + const char* install_name = (char *)dl + dl->dylib.name.offset; + if ( names_match(install_name, libraryName) ) + return dl->dylib.current_version; + } + lc = (load_command*)((char*)lc + lc->cmdsize); + } + } + return (-1); +} + + +#define PACKED_VERSION(major, minor, tiny) ((((major) & 0xffff) << 16) | (((minor) & 0xff) << 8) | ((tiny) & 0xff)) + + +static bool getVersionLoadCommandInfo(const mach_header* mh, uint32_t* loadCommand, uint32_t* minOS, uint32_t* sdk) +{ + const load_command* startCmds = NULL; + if ( mh->magic == MH_MAGIC_64 ) + startCmds = (load_command*)((char *)mh + sizeof(mach_header_64)); + else if ( mh->magic == MH_MAGIC ) + startCmds = (load_command*)((char *)mh + sizeof(mach_header)); + else + return false; // not a mach-o file, or wrong endianness + + const load_command* const cmdsEnd = (load_command*)((char*)startCmds + mh->sizeofcmds); + const load_command* cmd = startCmds; + for(uint32_t i = 0; i < mh->ncmds; ++i) { + const load_command* nextCmd = (load_command*)((char *)cmd + cmd->cmdsize); + if ( (cmd->cmdsize < 8) || (nextCmd > cmdsEnd) || (nextCmd < startCmds)) { + return 0; + } + const version_min_command* versCmd; + switch ( cmd->cmd ) { + case LC_VERSION_MIN_IPHONEOS: + case LC_VERSION_MIN_MACOSX: + case LC_VERSION_MIN_TVOS: + case LC_VERSION_MIN_WATCHOS: + versCmd = (version_min_command*)cmd; + *loadCommand = versCmd->cmd; + *minOS = versCmd->version; + *sdk = versCmd->sdk; + return true; + } + cmd = nextCmd; + } + return false; +} + +#if !__WATCH_OS_VERSION_MIN_REQUIRED && !__TV_OS_VERSION_MIN_REQUIRED +static uint32_t deriveSDKVersFromDylibs(const mach_header* mh) +{ + const load_command* startCmds = NULL; + if ( mh->magic == MH_MAGIC_64 ) + startCmds = (load_command*)((char *)mh + sizeof(mach_header_64)); + else if ( mh->magic == MH_MAGIC ) + startCmds = (load_command*)((char *)mh + sizeof(mach_header)); + else + return 0; // not a mach-o file, or wrong endianness + + const load_command* const cmdsEnd = (load_command*)((char*)startCmds + mh->sizeofcmds); + const dylib_command* dylibCmd; + const load_command* cmd = startCmds; + const char* dylibName; + #if __IPHONE_OS_VERSION_MIN_REQUIRED + uint32_t foundationVers = 0; + #else + uint32_t libSystemVers = 0; + #endif + for(uint32_t i = 0; i < mh->ncmds; ++i) { + const load_command* nextCmd = (load_command*)((char *)cmd + cmd->cmdsize); + // sanity check size of command + if ( (cmd->cmdsize < 8) || (nextCmd > cmdsEnd) || (nextCmd < startCmds)) { + return 0; + } + switch ( cmd->cmd ) { + case LC_LOAD_DYLIB: + case LC_LOAD_WEAK_DYLIB: + case LC_LOAD_UPWARD_DYLIB: + dylibCmd = (dylib_command*)cmd; + // sanity check dylib command layout + if ( dylibCmd->dylib.name.offset > cmd->cmdsize ) + return 0; + dylibName = (char*)dylibCmd + dylibCmd->dylib.name.offset; + #if __IPHONE_OS_VERSION_MIN_REQUIRED + if ( strcmp(dylibName, "/System/Library/Frameworks/Foundation.framework/Foundation") == 0 ) + foundationVers = dylibCmd->dylib.current_version; + #else + if ( strcmp(dylibName, "/usr/lib/libSystem.B.dylib") == 0 ) + libSystemVers = dylibCmd->dylib.current_version; + #endif + break; + } + cmd = nextCmd; + } + + struct DylibToOSMapping { + uint32_t dylibVersion; + uint32_t osVersion; + }; + + #if __IPHONE_OS_VERSION_MIN_REQUIRED + static const DylibToOSMapping foundationMapping[] = { + { PACKED_VERSION(678,24,0), DYLD_IOS_VERSION_2_0 }, + { PACKED_VERSION(678,26,0), DYLD_IOS_VERSION_2_1 }, + { PACKED_VERSION(678,29,0), DYLD_IOS_VERSION_2_2 }, + { PACKED_VERSION(678,47,0), DYLD_IOS_VERSION_3_0 }, + { PACKED_VERSION(678,51,0), DYLD_IOS_VERSION_3_1 }, + { PACKED_VERSION(678,60,0), DYLD_IOS_VERSION_3_2 }, + { PACKED_VERSION(751,32,0), DYLD_IOS_VERSION_4_0 }, + { PACKED_VERSION(751,37,0), DYLD_IOS_VERSION_4_1 }, + { PACKED_VERSION(751,49,0), DYLD_IOS_VERSION_4_2 }, + { PACKED_VERSION(751,58,0), DYLD_IOS_VERSION_4_3 }, + { PACKED_VERSION(881,0,0), DYLD_IOS_VERSION_5_0 }, + { PACKED_VERSION(890,1,0), DYLD_IOS_VERSION_5_1 }, + { PACKED_VERSION(992,0,0), DYLD_IOS_VERSION_6_0 }, + { PACKED_VERSION(993,0,0), DYLD_IOS_VERSION_6_1 }, + { PACKED_VERSION(1038,14,0),DYLD_IOS_VERSION_7_0 }, + { PACKED_VERSION(0,0,0), DYLD_IOS_VERSION_7_0 } + // We don't need to expand this table because all recent + // binaries have LC_VERSION_MIN_ load command. + }; + + if ( foundationVers != 0 ) { + uint32_t lastOsVersion = 0; + for (const DylibToOSMapping* p=foundationMapping; ; ++p) { + if ( p->dylibVersion == 0 ) + return p->osVersion; + if ( foundationVers < p->dylibVersion ) + return lastOsVersion; + lastOsVersion = p->osVersion; + } + } + + #else + // Note: versions are for the GM release. The last entry should + // always be zero. At the start of the next major version, + // a new last entry needs to be added and the previous zero + // updated to the GM dylib version. + static const DylibToOSMapping libSystemMapping[] = { + { PACKED_VERSION(88,1,3), DYLD_MACOSX_VERSION_10_4 }, + { PACKED_VERSION(111,0,0), DYLD_MACOSX_VERSION_10_5 }, + { PACKED_VERSION(123,0,0), DYLD_MACOSX_VERSION_10_6 }, + { PACKED_VERSION(159,0,0), DYLD_MACOSX_VERSION_10_7 }, + { PACKED_VERSION(169,3,0), DYLD_MACOSX_VERSION_10_8 }, + { PACKED_VERSION(1197,0,0), DYLD_MACOSX_VERSION_10_9 }, + { PACKED_VERSION(0,0,0), DYLD_MACOSX_VERSION_10_9 } + // We don't need to expand this table because all recent + // binaries have LC_VERSION_MIN_ load command. + }; + + if ( libSystemVers != 0 ) { + uint32_t lastOsVersion = 0; + for (const DylibToOSMapping* p=libSystemMapping; ; ++p) { + if ( p->dylibVersion == 0 ) + return p->osVersion; + if ( libSystemVers < p->dylibVersion ) + return lastOsVersion; + lastOsVersion = p->osVersion; + } + } + #endif + return 0; +} +#endif + + +#if __WATCH_OS_VERSION_MIN_REQUIRED +static uint32_t watchVersToIOSVers(uint32_t vers) +{ + return vers + 0x00070000; +} + +uint32_t dyld_get_program_sdk_watch_os_version() +{ + const mach_header* mh = (mach_header*)_NSGetMachExecuteHeader(); + uint32_t loadCommand; + uint32_t minOS; + uint32_t sdk; + + if ( getVersionLoadCommandInfo(mh, &loadCommand, &minOS, &sdk) ) { + if ( loadCommand == LC_VERSION_MIN_WATCHOS ) + return sdk; + } + return 0; +} + +uint32_t dyld_get_program_min_watch_os_version() +{ + const mach_header* mh = (mach_header*)_NSGetMachExecuteHeader(); + uint32_t loadCommand; + uint32_t minOS; + uint32_t sdk; + + if ( getVersionLoadCommandInfo(mh, &loadCommand, &minOS, &sdk) ) { + if ( loadCommand == LC_VERSION_MIN_WATCHOS ) + return minOS; // return raw minOS (not mapped to iOS version) + } + return 0; +} + +#endif + +/* + * Returns the sdk version (encode as nibble XXXX.YY.ZZ) the + * specified binary was built against. + * + * First looks for LC_VERSION_MIN_* in binary and if sdk field is + * not zero, return that value. + * Otherwise, looks for the libSystem.B.dylib the binary linked + * against and uses a table to convert that to an sdk version. + */ +uint32_t dyld_get_sdk_version(const mach_header* mh) +{ + uint32_t loadCommand; + uint32_t minOS; + uint32_t sdk; + + if ( getVersionLoadCommandInfo(mh, &loadCommand, &minOS, &sdk) ) { + switch (loadCommand) { +#if __WATCH_OS_VERSION_MIN_REQUIRED + case LC_VERSION_MIN_WATCHOS: + // new binary. sdk version looks like "2.0" but API wants "9.0" + return watchVersToIOSVers(sdk); + case LC_VERSION_MIN_IPHONEOS: + // old binary. sdk matches API semantics so can return directly. + return sdk; +#elif __TV_OS_VERSION_MIN_REQUIRED + case LC_VERSION_MIN_TVOS: + case LC_VERSION_MIN_IPHONEOS: + return sdk; +#elif __IPHONE_OS_VERSION_MIN_REQUIRED + case LC_VERSION_MIN_IPHONEOS: + if ( sdk != 0 ) // old binaries might not have SDK set + return sdk; + break; +#else + case LC_VERSION_MIN_MACOSX: + if ( sdk != 0 ) // old binaries might not have SDK set + return sdk; + break; +#endif + } + } + +#if __WATCH_OS_VERSION_MIN_REQUIRED ||__TV_OS_VERSION_MIN_REQUIRED + // All WatchOS and tv OS binaries should have version load command. + return 0; +#else + // MacOSX and iOS have old binaries without version load commmand. + return deriveSDKVersFromDylibs(mh); +#endif +} + +uint32_t dyld_get_program_sdk_version() +{ + return dyld_get_sdk_version((mach_header*)_NSGetMachExecuteHeader()); +} + +uint32_t dyld_get_min_os_version(const struct mach_header* mh) +{ + uint32_t loadCommand; + uint32_t minOS; + uint32_t sdk; + + if ( getVersionLoadCommandInfo(mh, &loadCommand, &minOS, &sdk) ) { + switch (loadCommand) { +#if __WATCH_OS_VERSION_MIN_REQUIRED + case LC_VERSION_MIN_WATCHOS: + // new binary. OS version looks like "2.0" but API wants "9.0" + return watchVersToIOSVers(minOS); + case LC_VERSION_MIN_IPHONEOS: + // old binary. OS matches API semantics so can return directly. + return minOS; +#elif __TV_OS_VERSION_MIN_REQUIRED + case LC_VERSION_MIN_TVOS: + case LC_VERSION_MIN_IPHONEOS: + return minOS; +#elif __IPHONE_OS_VERSION_MIN_REQUIRED + case LC_VERSION_MIN_IPHONEOS: + return minOS; +#else + case LC_VERSION_MIN_MACOSX: + return minOS; +#endif + } + } + return 0; +} + + +uint32_t dyld_get_program_min_os_version() +{ + return dyld_get_min_os_version((mach_header*)_NSGetMachExecuteHeader()); +} + + +bool _dyld_get_image_uuid(const struct mach_header* mh, uuid_t uuid) +{ + const load_command* startCmds = NULL; + if ( mh->magic == MH_MAGIC_64 ) + startCmds = (load_command*)((char *)mh + sizeof(mach_header_64)); + else if ( mh->magic == MH_MAGIC ) + startCmds = (load_command*)((char *)mh + sizeof(mach_header)); + else + return false; // not a mach-o file, or wrong endianness + + const load_command* const cmdsEnd = (load_command*)((char*)startCmds + mh->sizeofcmds); + const load_command* cmd = startCmds; + for(uint32_t i = 0; i < mh->ncmds; ++i) { + const load_command* nextCmd = (load_command*)((char *)cmd + cmd->cmdsize); + if ( (cmd->cmdsize < 8) || (nextCmd > cmdsEnd) || (nextCmd < startCmds)) { + return false; + } + if ( cmd->cmd == LC_UUID ) { + const uuid_command* uuidCmd = (uuid_command*)cmd; + memcpy(uuid, uuidCmd->uuid, 16); + return true; + } + cmd = nextCmd; + } + bzero(uuid, 16); + return false; +} + + + +#if DEPRECATED_APIS_SUPPORTED +/* + * NSCreateObjectFileImageFromFile() creates an NSObjectFileImage for the + * specified file name if the file is a correct Mach-O file that can be loaded + * with NSloadModule(). For return codes of NSObjectFileImageFailure and + * NSObjectFileImageFormat an error message is printed to stderr. All + * other codes cause no printing. + */ +NSObjectFileImageReturnCode +NSCreateObjectFileImageFromFile( +const char* pathName, +NSObjectFileImage *objectFileImage) +{ + DYLD_LOCK_THIS_BLOCK; + static NSObjectFileImageReturnCode (*p)(const char*, NSObjectFileImage*) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_NSCreateObjectFileImageFromFile", (void**)&p); + return p(pathName, objectFileImage); +} + + +/* + * NSCreateObjectFileImageFromMemory() creates an NSObjectFileImage for the + * object file mapped into memory at address of size length if the object file + * is a correct Mach-O file that can be loaded with NSloadModule(). For return + * codes of NSObjectFileImageFailure and NSObjectFileImageFormat an error + * message is printed to stderr. All other codes cause no printing. + */ +NSObjectFileImageReturnCode +NSCreateObjectFileImageFromMemory( +const void* address, +size_t size, +NSObjectFileImage *objectFileImage) +{ + DYLD_LOCK_THIS_BLOCK; + static NSObjectFileImageReturnCode (*p)(const void*, size_t, NSObjectFileImage*) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_NSCreateObjectFileImageFromMemory", (void**)&p); + return p(address, size, objectFileImage); +} + +#if OBSOLETE_DYLD_API +/* + * NSCreateCoreFileImageFromFile() creates an NSObjectFileImage for the + * specified core file name if the file is a correct Mach-O core file. + * For return codes of NSObjectFileImageFailure and NSObjectFileImageFormat + * an error message is printed to stderr. All other codes cause no printing. + */ +NSObjectFileImageReturnCode +NSCreateCoreFileImageFromFile( +const char* pathName, +NSObjectFileImage *objectFileImage) +{ + DYLD_LOCK_THIS_BLOCK; + static NSObjectFileImageReturnCode (*p)(const char*, NSObjectFileImage*) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_NSCreateCoreFileImageFromFile", (void**)&p); + return p(pathName, objectFileImage); +} +#endif + +bool +NSDestroyObjectFileImage( +NSObjectFileImage objectFileImage) +{ + DYLD_LOCK_THIS_BLOCK; + static bool (*p)(NSObjectFileImage) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_NSDestroyObjectFileImage", (void**)&p); + return p(objectFileImage); +} + + +NSModule +NSLinkModule( +NSObjectFileImage objectFileImage, +const char* moduleName, +uint32_t options) +{ + DYLD_LOCK_THIS_BLOCK; + static NSModule (*p)(NSObjectFileImage, const char*, unsigned long) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_NSLinkModule", (void**)&p); + + return p(objectFileImage, moduleName, options); +} + + + + +/* + * NSSymbolDefinitionCountInObjectFileImage() returns the number of symbol + * definitions in the NSObjectFileImage. + */ +uint32_t +NSSymbolDefinitionCountInObjectFileImage( +NSObjectFileImage objectFileImage) +{ + DYLD_LOCK_THIS_BLOCK; + static uint32_t (*p)(NSObjectFileImage) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_NSSymbolDefinitionCountInObjectFileImage", (void**)&p); + + return p(objectFileImage); +} + +/* + * NSSymbolDefinitionNameInObjectFileImage() returns the name of the i'th + * symbol definitions in the NSObjectFileImage. If the ordinal specified is + * outside the range [0..NSSymbolDefinitionCountInObjectFileImage], NULL will + * be returned. + */ +const char* +NSSymbolDefinitionNameInObjectFileImage( +NSObjectFileImage objectFileImage, +uint32_t ordinal) +{ + DYLD_LOCK_THIS_BLOCK; + static const char* (*p)(NSObjectFileImage, uint32_t) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_NSSymbolDefinitionNameInObjectFileImage", (void**)&p); + + return p(objectFileImage, ordinal); +} + +/* + * NSSymbolReferenceCountInObjectFileImage() returns the number of references + * to undefined symbols the NSObjectFileImage. + */ +uint32_t +NSSymbolReferenceCountInObjectFileImage( +NSObjectFileImage objectFileImage) +{ + DYLD_LOCK_THIS_BLOCK; + static uint32_t (*p)(NSObjectFileImage) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_NSSymbolReferenceCountInObjectFileImage", (void**)&p); + + return p(objectFileImage); +} + +/* + * NSSymbolReferenceNameInObjectFileImage() returns the name of the i'th + * undefined symbol in the NSObjectFileImage. If the ordinal specified is + * outside the range [0..NSSymbolReferenceCountInObjectFileImage], NULL will be + * returned. + */ +const char* +NSSymbolReferenceNameInObjectFileImage( +NSObjectFileImage objectFileImage, +uint32_t ordinal, +bool *tentative_definition) /* can be NULL */ +{ + DYLD_LOCK_THIS_BLOCK; + static const char* (*p)(NSObjectFileImage, uint32_t, bool*) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_NSSymbolReferenceNameInObjectFileImage", (void**)&p); + + return p(objectFileImage, ordinal, tentative_definition); +} + +/* + * NSIsSymbolDefinedInObjectFileImage() returns TRUE if the specified symbol + * name has a definition in the NSObjectFileImage and FALSE otherwise. + */ +bool +NSIsSymbolDefinedInObjectFileImage( +NSObjectFileImage objectFileImage, +const char* symbolName) +{ + DYLD_LOCK_THIS_BLOCK; + static bool (*p)(NSObjectFileImage, const char*) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_NSIsSymbolDefinedInObjectFileImage", (void**)&p); + + return p(objectFileImage, symbolName); +} + +/* + * NSGetSectionDataInObjectFileImage() returns a pointer to the section contents + * in the NSObjectFileImage for the specified segmentName and sectionName if + * it exists and it is not a zerofill section. If not it returns NULL. If + * the parameter size is not NULL the size of the section is also returned + * indirectly through that pointer. + */ +void * +NSGetSectionDataInObjectFileImage( +NSObjectFileImage objectFileImage, +const char* segmentName, +const char* sectionName, +unsigned long *size) /* can be NULL */ +{ + DYLD_LOCK_THIS_BLOCK; + static void* (*p)(NSObjectFileImage, const char*, const char*, unsigned long*) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_NSGetSectionDataInObjectFileImage", (void**)&p); + + return p(objectFileImage, segmentName, sectionName, size); +} + + +void +NSLinkEditError( +NSLinkEditErrors *c, +int *errorNumber, +const char* *fileName, +const char* *errorString) +{ + DYLD_LOCK_THIS_BLOCK; + static void (*p)(NSLinkEditErrors *c, + int *errorNumber, + const char* *fileName, + const char* *errorString) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_link_edit_error", (void**)&p); + if(p != NULL) + p(c, errorNumber, fileName, errorString); +} + +bool +NSUnLinkModule( +NSModule module, +uint32_t options) +{ + DYLD_LOCK_THIS_BLOCK; + static bool (*p)(NSModule module, uint32_t options) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_unlink_module", (void**)&p); + + return p(module, options); +} + +#if OBSOLETE_DYLD_API +NSModule +NSReplaceModule( +NSModule moduleToReplace, +NSObjectFileImage newObjectFileImage, +uint32_t options) +{ + return(NULL); +} +#endif + + +#endif // DEPRECATED_APIS_SUPPORTED + +/* + *_NSGetExecutablePath copies the path of the executable into the buffer and + * returns 0 if the path was successfully copied in the provided buffer. If the + * buffer is not large enough, -1 is returned and the expected buffer size is + * copied in *bufsize. Note that _NSGetExecutablePath will return "a path" to + * the executable not a "real path" to the executable. That is the path may be + * a symbolic link and not the real file. And with deep directories the total + * bufsize needed could be more than MAXPATHLEN. + */ +int +_NSGetExecutablePath( +char *buf, +uint32_t *bufsize) +{ + DYLD_NO_LOCK_THIS_BLOCK; + static int (*p)(char *buf, uint32_t *bufsize) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld__NSGetExecutablePath", (void**)&p); + return(p(buf, bufsize)); +} + +#if DEPRECATED_APIS_SUPPORTED +void +_dyld_lookup_and_bind( +const char* symbol_name, +void** address, +NSModule* module) +{ + DYLD_LOCK_THIS_BLOCK; + static void (*p)(const char*, void** , NSModule*) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_lookup_and_bind", (void**)&p); + p(symbol_name, address, module); +} + +void +_dyld_lookup_and_bind_with_hint( +const char* symbol_name, +const char* library_name_hint, +void** address, +NSModule* module) +{ + DYLD_LOCK_THIS_BLOCK; + static void (*p)(const char*, const char*, void**, NSModule*) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_lookup_and_bind_with_hint", (void**)&p); + p(symbol_name, library_name_hint, address, module); +} + +#if OBSOLETE_DYLD_API +void +_dyld_lookup_and_bind_objc( +const char* symbol_name, +void** address, +NSModule* module) +{ + DYLD_LOCK_THIS_BLOCK; + static void (*p)(const char* , void**, NSModule*) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_lookup_and_bind_objc", (void**)&p); + p(symbol_name, address, module); +} +#endif + +void +_dyld_lookup_and_bind_fully( +const char* symbol_name, +void** address, +NSModule* module) +{ + DYLD_LOCK_THIS_BLOCK; + static void (*p)(const char*, void**, NSModule*) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_lookup_and_bind_fully", (void**)&p); + p(symbol_name, address, module); +} + +bool +_dyld_bind_fully_image_containing_address( +const void* address) +{ + DYLD_LOCK_THIS_BLOCK; + static bool (*p)(const void*) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_bind_fully_image_containing_address", (void**)&p); + return p(address); +} +#endif // DEPRECATED_APIS_SUPPORTED + + +/* + * _dyld_register_func_for_add_image registers the specified function to be + * called when a new image is added (a bundle or a dynamic shared library) to + * the program. When this function is first registered it is called for once + * for each image that is currently part of the program. + */ +void +_dyld_register_func_for_add_image( +void (*func)(const struct mach_header *mh, intptr_t vmaddr_slide)) +{ + DYLD_LOCK_THIS_BLOCK; + typedef void (*callback_t)(const struct mach_header *mh, intptr_t vmaddr_slide); + static void (*p)(callback_t func) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_register_func_for_add_image", (void**)&p); + p(func); +} + +/* + * _dyld_register_func_for_remove_image registers the specified function to be + * called when an image is removed (a bundle or a dynamic shared library) from + * the program. + */ +void +_dyld_register_func_for_remove_image( +void (*func)(const struct mach_header *mh, intptr_t vmaddr_slide)) +{ + DYLD_LOCK_THIS_BLOCK; + typedef void (*callback_t)(const struct mach_header *mh, intptr_t vmaddr_slide); + static void (*p)(callback_t func) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_register_func_for_remove_image", (void**)&p); + p(func); +} + +#if OBSOLETE_DYLD_API +/* + * _dyld_register_func_for_link_module registers the specified function to be + * called when a module is bound into the program. When this function is first + * registered it is called for once for each module that is currently bound into + * the program. + */ +void +_dyld_register_func_for_link_module( +void (*func)(NSModule module)) +{ + DYLD_LOCK_THIS_BLOCK; + static void (*p)(void (*func)(NSModule module)) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_register_func_for_link_module", (void**)&p); + p(func); +} + +/* + * _dyld_register_func_for_unlink_module registers the specified function to be + * called when a module is unbound from the program. + */ +void +_dyld_register_func_for_unlink_module( +void (*func)(NSModule module)) +{ + DYLD_LOCK_THIS_BLOCK; + static void (*p)(void (*func)(NSModule module)) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_register_func_for_unlink_module", (void**)&p); + p(func); +} + +/* + * _dyld_register_func_for_replace_module registers the specified function to be + * called when a module is to be replace with another module in the program. + */ +void +_dyld_register_func_for_replace_module( +void (*func)(NSModule oldmodule, NSModule newmodule)) +{ + DYLD_LOCK_THIS_BLOCK; + static void (*p)(void (*func)(NSModule oldmodule, + NSModule newmodule)) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_register_func_for_replace_module", (void**)&p); + p(func); +} + + +/* + * _dyld_get_objc_module_sect_for_module is passed a module and sets a + * pointer to the (__OBJC,__module) section and its size for the specified + * module. + */ +void +_dyld_get_objc_module_sect_for_module( +NSModule module, +void **objc_module, +unsigned long *size) +{ + DYLD_LOCK_THIS_BLOCK; + static void (*p)(NSModule module, + void **objc_module, + unsigned long *size) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_get_objc_module_sect_for_module", (void**)&p); + p(module, objc_module, size); +} + +/* + * _dyld_bind_objc_module() is passed a pointer to something in an (__OBJC, + * __module) section and causes the module that is associated with that address + * to be bound. + */ +void +_dyld_bind_objc_module(const void* objc_module) +{ + DYLD_LOCK_THIS_BLOCK; + static void (*p)(const void *objc_module) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_bind_objc_module", (void**)&p); + p(objc_module); +} +#endif + +#if DEPRECATED_APIS_SUPPORTED +bool +_dyld_present(void) +{ + // this function exists for compatiblity only + return true; +} +#endif + +uint32_t +_dyld_image_count(void) +{ + DYLD_NO_LOCK_THIS_BLOCK; + static uint32_t (*p)(void) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_image_count", (void**)&p); + return(p()); +} + +const struct mach_header * +_dyld_get_image_header(uint32_t image_index) +{ + DYLD_NO_LOCK_THIS_BLOCK; + static struct mach_header * (*p)(uint32_t image_index) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_get_image_header", (void**)&p); + return(p(image_index)); +} + +intptr_t +_dyld_get_image_vmaddr_slide(uint32_t image_index) +{ + DYLD_NO_LOCK_THIS_BLOCK; + static unsigned long (*p)(uint32_t image_index) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_get_image_vmaddr_slide", (void**)&p); + return(p(image_index)); +} + +const char* +_dyld_get_image_name(uint32_t image_index) +{ + DYLD_NO_LOCK_THIS_BLOCK; + static const char* (*p)(uint32_t image_index) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_get_image_name", (void**)&p); + return(p(image_index)); +} + +// SPI in Mac OS X 10.6 +intptr_t _dyld_get_image_slide(const struct mach_header* mh) +{ + DYLD_NO_LOCK_THIS_BLOCK; + static intptr_t (*p)(const struct mach_header*) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_get_image_slide", (void**)&p); + return(p(mh)); +} + + +bool +_dyld_image_containing_address(const void* address) +{ + DYLD_LOCK_THIS_BLOCK; + static bool (*p)(const void*) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_image_containing_address", (void**)&p); + return(p(address)); +} + +const struct mach_header * +_dyld_get_image_header_containing_address( +const void* address) +{ + DYLD_LOCK_THIS_BLOCK; + static const struct mach_header * (*p)(const void*) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_get_image_header_containing_address", (void**)&p); + return p(address); +} + + +#if DEPRECATED_APIS_SUPPORTED +bool _dyld_launched_prebound(void) +{ + DYLD_LOCK_THIS_BLOCK; + static bool (*p)(void) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_launched_prebound", (void**)&p); + return(p()); +} + +bool _dyld_all_twolevel_modules_prebound(void) +{ + DYLD_LOCK_THIS_BLOCK; + static bool (*p)(void) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_all_twolevel_modules_prebound", (void**)&p); + return(p()); +} +#endif // DEPRECATED_APIS_SUPPORTED + + +#include +#include +#include +#include +#include +#include +#include "dyldLibSystemInterface.h" + + +// pthread key used to access per-thread dlerror message +static pthread_key_t dlerrorPerThreadKey; +static bool dlerrorPerThreadKeyInitialized = false; + +// data kept per-thread +struct dlerrorPerThreadData +{ + size_t sizeAllocated; + char message[1]; +}; + +// function called by dyld to get buffer to store dlerror message +static char* getPerThreadBufferFor_dlerror(size_t sizeRequired) +{ + // ok to create key lazily because this function is called within dyld lock, so there is no race condition + if (!dlerrorPerThreadKeyInitialized ) { + // create key and tell pthread package to call free() on any data associated with key if thread dies + pthread_key_create(&dlerrorPerThreadKey, &free); + dlerrorPerThreadKeyInitialized = true; + } + + const size_t size = (sizeRequired < 256) ? 256 : sizeRequired; + dlerrorPerThreadData* data = (dlerrorPerThreadData*)pthread_getspecific(dlerrorPerThreadKey); + if ( data == NULL ) { + //int mallocSize = offsetof(dlerrorPerThreadData, message[size]); + const size_t mallocSize = sizeof(dlerrorPerThreadData)+size; + data = (dlerrorPerThreadData*)malloc(mallocSize); + data->sizeAllocated = size; + pthread_setspecific(dlerrorPerThreadKey, data); + } + else if ( data->sizeAllocated < sizeRequired ) { + free(data); + //int mallocSize = offsetof(dlerrorPerThreadData, message[size]); + const size_t mallocSize = sizeof(dlerrorPerThreadData)+size; + data = (dlerrorPerThreadData*)malloc(mallocSize); + data->sizeAllocated = size; + pthread_setspecific(dlerrorPerThreadKey, data); + } + return data->message; +} + +// dlerror buffer leak +// Only allocate buffer if an actual error message needs to be set +static bool hasPerThreadBufferFor_dlerror() +{ + if (!dlerrorPerThreadKeyInitialized ) + return false; + + return (pthread_getspecific(dlerrorPerThreadKey) != NULL); +} + +// use non-lazy pointer to vproc_swap_integer so that lazy binding does not recurse +typedef vproc_err_t (*vswapproc)(vproc_t vp, vproc_gsk_t key,int64_t *inval, int64_t *outval); +static vswapproc swapProc = &vproc_swap_integer; + +static bool isLaunchdOwned() +{ + int64_t val = 0; + (*swapProc)(NULL, VPROC_GSK_IS_MANAGED, NULL, &val); + return ( val != 0 ); +} + +#if DYLD_SHARED_CACHE_SUPPORT +static void shared_cache_missing() +{ + // leave until dyld's that might call this are rare +} + +static void shared_cache_out_of_date() +{ + // leave until dyld's that might call this are rare +} +#endif // DYLD_SHARED_CACHE_SUPPORT + + +// the table passed to dyld containing thread helpers +static dyld::LibSystemHelpers sHelpers = { 13, &dyldGlobalLockAcquire, &dyldGlobalLockRelease, + &getPerThreadBufferFor_dlerror, &malloc, &free, &__cxa_atexit, + #if DYLD_SHARED_CACHE_SUPPORT + &shared_cache_missing, &shared_cache_out_of_date, + #else + NULL, NULL, + #endif + NULL, NULL, + &pthread_key_create, &pthread_setspecific, + &malloc_size, + &pthread_getspecific, + &__cxa_finalize, + address_of_start, + &hasPerThreadBufferFor_dlerror, + &isLaunchdOwned, + &vm_allocate, + &mmap, + &__cxa_finalize_ranges + }; + + +// +// during initialization of libSystem this routine will run +// and call dyld, registering the helper functions. +// +extern "C" void tlv_initializer(); +extern "C" void _dyld_initializer(); +void _dyld_initializer() +{ + void (*p)(dyld::LibSystemHelpers*); + + _dyld_func_lookup("__dyld_register_thread_helpers", (void**)&p); + if(p != NULL) + p(&sHelpers); + + tlv_initializer(); +} + + +char* dlerror() +{ + DYLD_LOCK_THIS_BLOCK; + static char* (*p)() = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_dlerror", (void**)&p); + return(p()); +} + +int dladdr(const void* addr, Dl_info* info) +{ + DYLD_LOCK_THIS_BLOCK; + static int (*p)(const void* , Dl_info*) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_dladdr", (void**)&p); + return(p(addr, info)); +} + +int dlclose(void* handle) +{ + DYLD_LOCK_THIS_BLOCK; + static int (*p)(void* handle) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_dlclose", (void**)&p); + return(p(handle)); +} + +void* dlopen(const char* path, int mode) +{ + // dlopen is special. locking is done inside dyld to allow initializer to run without lock + DYLD_NO_LOCK_THIS_BLOCK; + + static void* (*p)(const char* path, int) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_dlopen", (void**)&p); + void* result = p(path, mode); + // use asm block to prevent tail call optimization + // this is needed because dlopen uses __builtin_return_address() and depends on this glue being in the frame chain + // + __asm__ volatile(""); + + return result; +} + +bool dlopen_preflight(const char* path) +{ + DYLD_LOCK_THIS_BLOCK; + static bool (*p)(const char* path) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_dlopen_preflight", (void**)&p); + return(p(path)); +} + +void* dlsym(void* handle, const char* symbol) +{ + DYLD_LOCK_THIS_BLOCK; + static void* (*p)(void* handle, const char* symbol) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_dlsym", (void**)&p); + return(p(handle, symbol)); +} + + +const struct dyld_all_image_infos* _dyld_get_all_image_infos() +{ + DYLD_NO_LOCK_THIS_BLOCK; + static struct dyld_all_image_infos* (*p)() = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_get_all_image_infos", (void**)&p); + return p(); +} + +#if SUPPORT_ZERO_COST_EXCEPTIONS +bool _dyld_find_unwind_sections(void* addr, dyld_unwind_sections* info) +{ + DYLD_NO_LOCK_THIS_BLOCK; + static void* (*p)(void*, dyld_unwind_sections*) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_find_unwind_sections", (void**)&p); + return p(addr, info); +} +#endif + + +#if __i386__ || __x86_64__ || __arm__ || __arm64__ +__attribute__((visibility("hidden"))) +void* _dyld_fast_stub_entry(void* loadercache, long lazyinfo) +{ + DYLD_NO_LOCK_THIS_BLOCK; + static void* (*p)(void*, long) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_fast_stub_entry", (void**)&p); + return p(loadercache, lazyinfo); +} +#endif + + +const char* dyld_image_path_containing_address(const void* addr) +{ + DYLD_NO_LOCK_THIS_BLOCK; + static const char* (*p)(const void*) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_image_path_containing_address", (void**)&p); + return p(addr); +} + +const struct mach_header* dyld_image_header_containing_address(const void* addr) +{ + DYLD_NO_LOCK_THIS_BLOCK; + static const mach_header* (*p)(const void*) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_get_image_header_containing_address", (void**)&p); + return p(addr); +} + + +bool dyld_shared_cache_some_image_overridden() +{ + DYLD_NO_LOCK_THIS_BLOCK; + static bool (*p)() = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_shared_cache_some_image_overridden", (void**)&p); + return p(); +} + +bool _dyld_get_shared_cache_uuid(uuid_t uuid) +{ + DYLD_NO_LOCK_THIS_BLOCK; + static bool (*p)(uuid_t) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_get_shared_cache_uuid", (void**)&p); + return p(uuid); +} + + +bool dyld_process_is_restricted() +{ + DYLD_NO_LOCK_THIS_BLOCK; + static bool (*p)() = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_process_is_restricted", (void**)&p); + return p(); +} + +#if DYLD_SHARED_CACHE_SUPPORT +const char* dyld_shared_cache_file_path() +{ + DYLD_NO_LOCK_THIS_BLOCK; + static const char* (*p)() = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_shared_cache_file_path", (void**)&p); + return p(); +} +#endif + +void dyld_dynamic_interpose(const struct mach_header* mh, const struct dyld_interpose_tuple array[], size_t count) +{ + DYLD_LOCK_THIS_BLOCK; + static void (*p)(const struct mach_header* mh, const struct dyld_interpose_tuple array[], size_t count) = NULL; + + if (p == NULL) + _dyld_func_lookup("__dyld_dynamic_interpose", (void**)&p); + p(mh, array, count); +} + + +// SPI called __fork +void _dyld_fork_child() +{ + DYLD_NO_LOCK_THIS_BLOCK; + static void (*p)() = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_fork_child", (void**)&p); + return p(); +} + + + +static void* mapStartOfCache(const char* path, size_t length) +{ + struct stat statbuf; + if ( ::stat(path, &statbuf) == -1 ) + return NULL; + + if ( statbuf.st_size < length ) + return NULL; + + int cache_fd = ::open(path, O_RDONLY); + if ( cache_fd < 0 ) + return NULL; + + void* result = ::mmap(NULL, length, PROT_READ, MAP_PRIVATE, cache_fd, 0); + close(cache_fd); + + if ( result == MAP_FAILED ) + return NULL; + + return result; +} + + +static const dyld_cache_header* findCacheInDirAndMap(const uuid_t cacheUuid, const char* dirPath) +{ + DIR* dirp = ::opendir(dirPath); + if ( dirp != NULL) { + dirent entry; + dirent* entp = NULL; + char cachePath[PATH_MAX]; + while ( ::readdir_r(dirp, &entry, &entp) == 0 ) { + if ( entp == NULL ) + break; + if ( entp->d_type != DT_REG ) + continue; + if ( strlcpy(cachePath, dirPath, PATH_MAX) >= PATH_MAX ) + continue; + if ( strlcat(cachePath, "/", PATH_MAX) >= PATH_MAX ) + continue; + if ( strlcat(cachePath, entp->d_name, PATH_MAX) >= PATH_MAX ) + continue; + if ( const dyld_cache_header* cacheHeader = (dyld_cache_header*)mapStartOfCache(cachePath, 0x00100000) ) { + if ( ::memcmp(cacheHeader->uuid, cacheUuid, 16) != 0 ) { + // wrong uuid, unmap and keep looking + ::munmap((void*)cacheHeader, 0x00100000); + } + else { + // found cache + closedir(dirp); + return cacheHeader; + } + } + } + closedir(dirp); + } + return NULL; +} + +int dyld_shared_cache_find_iterate_text(const uuid_t cacheUuid, const char* extraSearchDirs[], void (^callback)(const dyld_shared_cache_dylib_text_info* info)) +{ + const dyld_cache_header* cacheHeader = NULL; + bool needToUnmap = true; + + // get info from dyld about this process, to see if requested cache is already mapped into this process + const dyld_all_image_infos* allInfo = _dyld_get_all_image_infos(); + if ( (allInfo != NULL) && (memcmp(allInfo->sharedCacheUUID, cacheUuid, 16) == 0) ) { + // requested cache is already mapped, just re-use it + cacheHeader = (dyld_cache_header*)(SHARED_REGION_BASE + allInfo->sharedCacheSlide); + needToUnmap = false; + } + else { + // look first is default location for cache files + #if __IPHONE_OS_VERSION_MIN_REQUIRED + const char* defaultSearchDir = IPHONE_DYLD_SHARED_CACHE_DIR; + #else + const char* defaultSearchDir = MACOSX_DYLD_SHARED_CACHE_DIR; + #endif + cacheHeader = findCacheInDirAndMap(cacheUuid, defaultSearchDir); + // if not there, look in extra search locations + if ( cacheHeader == NULL ) { + for (const char** p = extraSearchDirs; *p != NULL; ++p) { + cacheHeader = findCacheInDirAndMap(cacheUuid, *p); + if ( cacheHeader != NULL ) + break; + } + } + } + + if ( cacheHeader == NULL ) + return -1; + + if ( cacheHeader->mappingOffset < sizeof(dyld_cache_header) ) { + // old cache without imagesText array + if ( needToUnmap ) + ::munmap((void*)cacheHeader, 0x00100000); + return -1; + } + + // walk imageText table and call callback for each entry + const dyld_cache_image_text_info* imagesText = (dyld_cache_image_text_info*)((char*)cacheHeader + cacheHeader->imagesTextOffset); + const dyld_cache_image_text_info* imagesTextEnd = &imagesText[cacheHeader->imagesTextCount]; + for (const dyld_cache_image_text_info* p=imagesText; p < imagesTextEnd; ++p) { + dyld_shared_cache_dylib_text_info dylibTextInfo; + dylibTextInfo.version = 1; + dylibTextInfo.loadAddressUnslid = p->loadAddress; + dylibTextInfo.textSegmentSize = p->textSegmentSize; + dylibTextInfo.path = (char*)cacheHeader + p->pathOffset; + ::memcpy(dylibTextInfo.dylibUuid, p->uuid, 16); + callback(&dylibTextInfo); + } + + if ( needToUnmap ) + ::munmap((void*)cacheHeader, 0x00100000); + + return 0; +} + +int dyld_shared_cache_iterate_text(const uuid_t cacheUuid, void (^callback)(const dyld_shared_cache_dylib_text_info* info)) +{ + const char* extraSearchDirs[] = { NULL }; + return dyld_shared_cache_find_iterate_text(cacheUuid, extraSearchDirs, callback); +} + + +bool _dyld_is_memory_immutable(const void* addr, size_t length) +{ + DYLD_NO_LOCK_THIS_BLOCK; + static bool (*p)(const void*, size_t) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_is_memory_immutable", (void**)&p); + return p(addr, length); +} + + +void _dyld_objc_notify_register(_dyld_objc_notify_mapped mapped, + _dyld_objc_notify_init init, + _dyld_objc_notify_unmapped unmapped) +{ + DYLD_LOCK_THIS_BLOCK; + static bool (*p)(_dyld_objc_notify_mapped, _dyld_objc_notify_init, _dyld_objc_notify_unmapped) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_objc_notify_register", (void**)&p); + p(mapped, init, unmapped); +} + + + + + diff --git a/src/dyld-apple/src/dyldExceptions.c b/src/dyld-apple/src/dyldExceptions.c new file mode 100644 index 000000000..487835e4d --- /dev/null +++ b/src/dyld-apple/src/dyldExceptions.c @@ -0,0 +1,205 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2004-2011 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@ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mach-o/dyld_priv.h" +#include "dyldLibSystemInterface.h" + + +extern void _ZN4dyld3logEPKcz(const char*, ...); +extern struct LibSystemHelpers* _ZN4dyld17gLibSystemHelpersE; + + +#if __LP64__ + #define LC_SEGMENT_COMMAND LC_SEGMENT_64 + #define macho_header mach_header_64 + #define macho_segment_command segment_command_64 + #define macho_section section_64 + #define getsectdatafromheader getsectdatafromheader_64 +#else + #define LC_SEGMENT_COMMAND LC_SEGMENT + #define macho_header mach_header + #define macho_segment_command segment_command + #define macho_section section +#endif + + +// +// The standard versions of __cxa_get_globals*() from libstdc++-static.a cannot be used. +// On Mac OS X, they use keymgr which dyld does not implement. +// On iPhoneOS, they use pthread_key_create which dyld cannot use. +// +// Implement these ourselves to make upcalls into libSystem to malloc and create a pthread key +// +static pthread_key_t sCxaKey = 0; +static char sPreMainCxaGlobals[2*sizeof(long)]; + +// called by libstdc++.a +char* __cxa_get_globals() +{ + // if libSystem.dylib not yet initialized, or is old libSystem, use shared global + if ( (_ZN4dyld17gLibSystemHelpersE == NULL) || (_ZN4dyld17gLibSystemHelpersE->version < 7) ) + return sPreMainCxaGlobals; + + if ( sCxaKey == 0 ) { + // create key + // we don't need a lock because only one thread can be in dyld at a time + _ZN4dyld17gLibSystemHelpersE->pthread_key_create(&sCxaKey, &free); + } + char* data = (char*)_ZN4dyld17gLibSystemHelpersE->pthread_getspecific(sCxaKey); + if ( data == NULL ) { + data = calloc(2,sizeof(void*)); + _ZN4dyld17gLibSystemHelpersE->pthread_setspecific(sCxaKey, data); + } + return data; +} + +// called by libstdc++.a +char* __cxa_get_globals_fast() +{ + // if libSystem.dylib not yet initialized, or is old libSystem, use shared global + if ( (_ZN4dyld17gLibSystemHelpersE == NULL) || (_ZN4dyld17gLibSystemHelpersE->version < 7) ) + return sPreMainCxaGlobals; + + return _ZN4dyld17gLibSystemHelpersE->pthread_getspecific(sCxaKey); +} + + + +#if !__USING_SJLJ_EXCEPTIONS__ +// +// When dyld uses zero-cost exceptions it just needs to implement +// _dyld_find_unwind_sections to return sections inside dyld proper. +// + +extern void* ehStart __asm("section$start$__TEXT$__eh_frame"); +extern void* ehEnd __asm("section$end$__TEXT$__eh_frame"); +extern void* uwStart __asm("section$start$__TEXT$__unwind_info"); +extern void* uwEnd __asm("section$end$__TEXT$__unwind_info"); + +extern void* textStart __asm("section$start$__TEXT$__text"); +extern void* textEnd __asm("section$end$__TEXT$__text"); + +extern void* __dso_handle; + +// called by libuwind code to find unwind information sections in dyld +bool _dyld_find_unwind_sections(void* addr, struct dyld_unwind_sections* info) +{ + if ( ((void*)&textStart < addr) && (addr < (void*)&textEnd) ) { + info->mh = (struct mach_header*)&__dso_handle; + info->dwarf_section = &ehStart; + info->dwarf_section_length = ((char*)&ehEnd - (char*)&ehStart); + info->compact_unwind_section = &uwStart; + info->compact_unwind_section_length = ((char*)&uwEnd - (char*)&uwStart); + return true; + } + else { + return false; + } +} + +#else +// +// When dyld uses setjump-longjump exceptions it needs to implement +// routines to push and pop a stack of _Unwind_FunctionContext. +// + +struct _Unwind_FunctionContext +{ + // next function in stack of handlers + struct _Unwind_FunctionContext* prev; + +}; + + +static pthread_key_t sThreadChainKey = 0; +static struct _Unwind_FunctionContext* sStaticThreadChain = NULL; + +// +// When libSystem's initializers are run, they call back into dyld's +// registerThreadHelpers which creates a pthread key and calls +// __Unwind_SjLj_SetThreadKey(). +// +void __Unwind_SjLj_SetThreadKey(pthread_key_t key) +{ + sThreadChainKey = key; + //_ZN4dyld3logEPKcz("__Unwind_SjLj_SetThreadKey(key=%d), sStaticThreadChain=%p\n", key, sStaticThreadChain); + // switch static chain to be per thread + _ZN4dyld17gLibSystemHelpersE->pthread_setspecific(sThreadChainKey, sStaticThreadChain); + sStaticThreadChain = NULL; + //_ZN4dyld3logEPKcz("__Unwind_SjLj_SetThreadKey(key=%d), result=%d, new top=%p\n", key, result, pthread_getspecific(sThreadChainKey)); +} + + +//static void printChain() +//{ +// _ZN4dyld3logEPKcz("chain: "); +// struct _Unwind_FunctionContext* start = sStaticThreadChain; +// if ( sThreadChainKey != 0 ) { +// start = (struct _Unwind_FunctionContext*)pthread_getspecific(sThreadChainKey); +// } +// for (struct _Unwind_FunctionContext* p = start; p != NULL; p = p->prev) { +// _ZN4dyld3logEPKcz("%p -> ", p); +// } +// _ZN4dyld3logEPKcz("\n"); +//} + + +struct _Unwind_FunctionContext* __Unwind_SjLj_GetTopOfFunctionStack() +{ + //_ZN4dyld3logEPKcz("__Unwind_SjLj_GetTopOfFunctionStack(), key=%d, ", sThreadChainKey); + //printChain(); + if ( sThreadChainKey != 0 ) { + return (struct _Unwind_FunctionContext*)pthread_getspecific(sThreadChainKey); + } + else { + return sStaticThreadChain; + } +} + +void __Unwind_SjLj_SetTopOfFunctionStack(struct _Unwind_FunctionContext* fc) +{ + //_ZN4dyld3logEPKcz("__Unwind_SjLj_SetTopOfFunctionStack(%p), key=%d, prev=%p\n", + // fc, sThreadChainKey, (fc != NULL) ? fc->prev : NULL); + if ( sThreadChainKey != 0 ) + _ZN4dyld17gLibSystemHelpersE->pthread_setspecific(sThreadChainKey, fc); + else + sStaticThreadChain = fc; + //_ZN4dyld3logEPKcz("__Unwind_SjLj_SetTopOfFunctionStack(%p), key=%d, ", fc, sThreadChainKey); + //printChain(); +} + +#endif + + + diff --git a/src/dyld-apple/src/dyldInitialization.cpp b/src/dyld-apple/src/dyldInitialization.cpp new file mode 100644 index 000000000..692b271ec --- /dev/null +++ b/src/dyld-apple/src/dyldInitialization.cpp @@ -0,0 +1,282 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2004-2008 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@ + */ + +#define __STDC_LIMIT_MACROS +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if __x86_64__ + #include +#endif +#include "dyld.h" +#include "dyldSyscallInterface.h" + +// from dyld_gdb.cpp +extern void addImagesToAllImages(uint32_t infoCount, const dyld_image_info info[]); +extern void syncProcessInfo(); + +#ifndef MH_PIE + #define MH_PIE 0x200000 +#endif + +// currently dyld has no initializers, but if some come back, set this to non-zero +#define DYLD_INITIALIZER_SUPPORT 0 + +#if __LP64__ + #define LC_SEGMENT_COMMAND LC_SEGMENT_64 + #define macho_segment_command segment_command_64 + #define macho_section section_64 + #define RELOC_SIZE 3 +#else + #define LC_SEGMENT_COMMAND LC_SEGMENT + #define macho_segment_command segment_command + #define macho_section section + #define RELOC_SIZE 2 +#endif + +#if __x86_64__ + #define POINTER_RELOC X86_64_RELOC_UNSIGNED +#else + #define POINTER_RELOC GENERIC_RELOC_VANILLA +#endif + + +#if TARGET_IPHONE_SIMULATOR +const dyld::SyscallHelpers* gSyscallHelpers = NULL; +#endif + + +// +// Code to bootstrap dyld into a runnable state +// +// + +namespace dyldbootstrap { + + + +#if DYLD_INITIALIZER_SUPPORT + +typedef void (*Initializer)(int argc, const char* argv[], const char* envp[], const char* apple[]); + +extern const Initializer inits_start __asm("section$start$__DATA$__mod_init_func"); +extern const Initializer inits_end __asm("section$end$__DATA$__mod_init_func"); + +// +// For a regular executable, the crt code calls dyld to run the executables initializers. +// For a static executable, crt directly runs the initializers. +// dyld (should be static) but is a dynamic executable and needs this hack to run its own initializers. +// We pass argc, argv, etc in case libc.a uses those arguments +// +static void runDyldInitializers(const struct macho_header* mh, intptr_t slide, int argc, const char* argv[], const char* envp[], const char* apple[]) +{ + for (const Initializer* p = &inits_start; p < &inits_end; ++p) { + (*p)(argc, argv, envp, apple); + } +} +#endif // DYLD_INITIALIZER_SUPPORT + + +// +// The kernel may have slid a Position Independent Executable +// +static uintptr_t slideOfMainExecutable(const struct macho_header* mh) +{ + const uint32_t cmd_count = mh->ncmds; + const struct load_command* const cmds = (struct load_command*)(((char*)mh)+sizeof(macho_header)); + const struct load_command* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + if ( cmd->cmd == LC_SEGMENT_COMMAND ) { + const struct macho_segment_command* segCmd = (struct macho_segment_command*)cmd; + if ( (segCmd->fileoff == 0) && (segCmd->filesize != 0)) { + return (uintptr_t)mh - segCmd->vmaddr; + } + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + return 0; +} + + +// +// If the kernel does not load dyld at its preferred address, we need to apply +// fixups to various initialized parts of the __DATA segment +// +static void rebaseDyld(const struct macho_header* mh, intptr_t slide) +{ + // rebase non-lazy pointers (which all point internal to dyld, since dyld uses no shared libraries) + // and get interesting pointers into dyld + const uint32_t cmd_count = mh->ncmds; + const struct load_command* const cmds = (struct load_command*)(((char*)mh)+sizeof(macho_header)); + const struct load_command* cmd = cmds; + const struct macho_segment_command* linkEditSeg = NULL; +#if __x86_64__ + const struct macho_segment_command* firstWritableSeg = NULL; +#endif + const struct dysymtab_command* dynamicSymbolTable = NULL; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd) { + case LC_SEGMENT_COMMAND: + { + const struct macho_segment_command* seg = (struct macho_segment_command*)cmd; + if ( strcmp(seg->segname, "__LINKEDIT") == 0 ) + linkEditSeg = seg; + const struct macho_section* const sectionsStart = (struct macho_section*)((char*)seg + sizeof(struct macho_segment_command)); + const struct macho_section* const sectionsEnd = §ionsStart[seg->nsects]; + for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) { + const uint8_t type = sect->flags & SECTION_TYPE; + if ( type == S_NON_LAZY_SYMBOL_POINTERS ) { + // rebase non-lazy pointers (which all point internal to dyld, since dyld uses no shared libraries) + const uint32_t pointerCount = (uint32_t)(sect->size / sizeof(uintptr_t)); + uintptr_t* const symbolPointers = (uintptr_t*)(sect->addr + slide); + for (uint32_t j=0; j < pointerCount; ++j) { + symbolPointers[j] += slide; + } + } + } +#if __x86_64__ + if ( (firstWritableSeg == NULL) && (seg->initprot & VM_PROT_WRITE) ) + firstWritableSeg = seg; +#endif + } + break; + case LC_DYSYMTAB: + dynamicSymbolTable = (struct dysymtab_command *)cmd; + break; + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + + // use reloc's to rebase all random data pointers +#if __x86_64__ + const uintptr_t relocBase = firstWritableSeg->vmaddr + slide; +#else + const uintptr_t relocBase = (uintptr_t)mh; +#endif + const relocation_info* const relocsStart = (struct relocation_info*)(linkEditSeg->vmaddr + slide + dynamicSymbolTable->locreloff - linkEditSeg->fileoff); + const relocation_info* const relocsEnd = &relocsStart[dynamicSymbolTable->nlocrel]; + for (const relocation_info* reloc=relocsStart; reloc < relocsEnd; ++reloc) { + if ( reloc->r_length != RELOC_SIZE ) + throw "relocation in dyld has wrong size"; + + if ( reloc->r_type != POINTER_RELOC ) + throw "relocation in dyld has wrong type"; + + // update pointer by amount dyld slid + *((uintptr_t*)(reloc->r_address + relocBase)) += slide; + } +} + + +extern "C" void mach_init(); +extern "C" void __guard_setup(const char* apple[]); + + +// +// This is code to bootstrap dyld. This work in normally done for a program by dyld and crt. +// In dyld we have to do this manually. +// +uintptr_t start(const struct macho_header* appsMachHeader, int argc, const char* argv[], + intptr_t slide, const struct macho_header* dyldsMachHeader, + uintptr_t* startGlue) +{ + // if kernel had to slide dyld, we need to fix up load sensitive locations + // we have to do this before using any global variables + if ( slide != 0 ) { + rebaseDyld(dyldsMachHeader, slide); + } + + // allow dyld to use mach messaging + mach_init(); + + // kernel sets up env pointer to be just past end of agv array + const char** envp = &argv[argc+1]; + + // kernel sets up apple pointer to be just past end of envp array + const char** apple = envp; + while(*apple != NULL) { ++apple; } + ++apple; + + // set up random value for stack canary + __guard_setup(apple); + +#if DYLD_INITIALIZER_SUPPORT + // run all C++ initializers inside dyld + runDyldInitializers(dyldsMachHeader, slide, argc, argv, envp, apple); +#endif + + // now that we are done bootstrapping dyld, call dyld's main + uintptr_t appsSlide = slideOfMainExecutable(appsMachHeader); + return dyld::_main(appsMachHeader, appsSlide, argc, argv, envp, apple, startGlue); +} + + +#if TARGET_IPHONE_SIMULATOR + +extern "C" uintptr_t start_sim(int argc, const char* argv[], const char* envp[], const char* apple[], + const macho_header* mainExecutableMH, const macho_header* dyldMH, uintptr_t dyldSlide, + const dyld::SyscallHelpers*, uintptr_t* startGlue); + + +uintptr_t start_sim(int argc, const char* argv[], const char* envp[], const char* apple[], + const macho_header* mainExecutableMH, const macho_header* dyldMH, uintptr_t dyldSlide, + const dyld::SyscallHelpers* sc, uintptr_t* startGlue) +{ + // if simulator dyld loaded slid, it needs to rebase itself + // we have to do this before using any global variables + if ( dyldSlide != 0 ) { + rebaseDyld(dyldMH, dyldSlide); + } + + // save table of syscall pointers + gSyscallHelpers = sc; + + // allow dyld to use mach messaging + mach_init(); + + // set up random value for stack canary + __guard_setup(apple); + + // setup gProcessInfo to point to host dyld's struct + dyld::gProcessInfo = (struct dyld_all_image_infos*)(sc->getProcessInfo()); + syncProcessInfo(); + + // now that we are done bootstrapping dyld, call dyld's main + uintptr_t appsSlide = slideOfMainExecutable(mainExecutableMH); + return dyld::_main(mainExecutableMH, appsSlide, argc, argv, envp, apple, startGlue); +} +#endif + + +} // end of namespace + + + + diff --git a/src/dyld-apple/src/dyldLibSystemGlue.c b/src/dyld-apple/src/dyldLibSystemGlue.c new file mode 100644 index 000000000..4b6a63820 --- /dev/null +++ b/src/dyld-apple/src/dyldLibSystemGlue.c @@ -0,0 +1,76 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2009-2010 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@ + */ + +#include +#include +#include + + +// +// This is the temporary private interface between libSystem.B.dylib and dyld +// + + +int NXArgc = 0; +const char** NXArgv = NULL; +const char** environ = NULL; +const char* __progname = NULL; + + + +// +// Long ago, the compiler driver added -ldylib1.o to every dylib which caused a +// __DATA,__dyld section to be added every dylib. The new LINKEDIT format no longer requires +// images to have a __DATA,__dyld section. But until libdyld.dylib and dyld update +// to some sort of vtable based interface, libdyld still needs a __DATA,__dyld section. +// The code below adds that section. +// +struct __DATA__dyld { + long lazy; + int (*lookup)(const char*, void**); + // ProgramVars + const void* mh; + int* NXArgcPtr; + const char*** NXArgvPtr; + const char*** environPtr; + const char** __prognamePtr; +}; + +static volatile struct __DATA__dyld myDyldSection __attribute__ ((section ("__DATA,__dyld"))) + = { 0, 0, NULL, &NXArgc, &NXArgv, &environ, &__progname }; + +#if __arm__ && __MAC_OS_X_VERSION_MIN_REQUIRED +// +// For historical reasons, gcc and llvm-gcc added -ldylib1.o to the link line of armv6 +// dylibs when targeting MacOSX (but not iOS). clang cleans up that mistake, but doing +// so would break the libdyld build. Making _dyld_func_lookup weak,hidden means if +// dylib1.o is used, it overrides this, otherwise this implementation is used. +__attribute__((weak)) +#endif +__attribute__((visibility("hidden"))) +int _dyld_func_lookup(const char* dyld_func_name, void **address) +{ + return (*myDyldSection.lookup)(dyld_func_name, address); +} + diff --git a/src/dyld-apple/src/dyldLibSystemInterface.h b/src/dyld-apple/src/dyldLibSystemInterface.h new file mode 100644 index 000000000..f39a275d3 --- /dev/null +++ b/src/dyld-apple/src/dyldLibSystemInterface.h @@ -0,0 +1,86 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2004-2009 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@ + */ + + +#ifndef __DYLDLIBSYSTEMHELPERS__ +#define __DYLDLIBSYSTEMHELPERS__ + +#include + +struct __cxa_range_t { const void* addr; size_t length; }; + +#if __cplusplus +namespace dyld { +#endif + // + // This file contains the synchronization utilities used by dyld to be thread safe. + // This struct is implemented in in libSystem (where pthreads is available) + // and passed to dyld to use. + // + struct LibSystemHelpers + { + uintptr_t version; + void (*acquireGlobalDyldLock)(); + void (*releaseGlobalDyldLock)(); + char* (*getThreadBufferFor_dlerror)(size_t sizeRequired); + // addded in version 2 + void* (*malloc)(size_t); + void (*free)(void*); + int (*cxa_atexit)(void (*)(void*), void*, void*); + // addded in version 3 + void (*dyld_shared_cache_missing)(); + void (*dyld_shared_cache_out_of_date)(); + // addded in version 4 + void (*acquireDyldInitializerLock)(); + void (*releaseDyldInitializerLock)(); + // added in version 5 + int (*pthread_key_create)(pthread_key_t*, void (*destructor)(void*)); + int (*pthread_setspecific)(pthread_key_t, const void*); + // added in version 6 + size_t (*malloc_size)(const void *ptr); + // added in version 7 + void* (*pthread_getspecific)(pthread_key_t); + // added in version 8 + void (*cxa_finalize)(const void*); + // added in version 9 + void* startGlueToCallExit; + // added in version 10 + bool (*hasPerThreadBufferFor_dlerror)(); + // added in version 11 + bool (*isLaunchdOwned)(); + // added in version 12 + kern_return_t (*vm_alloc)(vm_map_t task, vm_address_t* addr, vm_size_t size, int flags); + void* (*mmap)(void* addr, size_t len, int prot, int flags, int fd, off_t offset); + // added in version 13 + void (*cxa_finalize_ranges)(const struct __cxa_range_t ranges[], int count); + }; +#if __cplusplus +} +#endif + + + + +#endif // __DYLDLIBSYSTEMHELPERS__ + diff --git a/src/dyld-apple/src/dyldLock.cpp b/src/dyld-apple/src/dyldLock.cpp new file mode 100644 index 000000000..18dfe1494 --- /dev/null +++ b/src/dyld-apple/src/dyldLock.cpp @@ -0,0 +1,59 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2004-2012 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@ + */ + +#include + +#include "dyldLock.h" + + + +static pthread_mutex_t sGlobalMutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER; + +// Need a way to determine if a gdb call to dlopen() would block +int __attribute__((visibility("hidden"))) _dyld_global_lock_held = 0; + + +LockHelper::LockHelper() +{ + dyldGlobalLockAcquire(); +} + +LockHelper::~LockHelper() +{ + dyldGlobalLockRelease(); +} + +void dyldGlobalLockAcquire() +{ + pthread_mutex_lock(&sGlobalMutex); + ++_dyld_global_lock_held; +} + +void dyldGlobalLockRelease() +{ + --_dyld_global_lock_held; + pthread_mutex_unlock(&sGlobalMutex); +} + + diff --git a/src/dyld-apple/src/dyldLock.h b/src/dyld-apple/src/dyldLock.h new file mode 100644 index 000000000..461012d68 --- /dev/null +++ b/src/dyld-apple/src/dyldLock.h @@ -0,0 +1,68 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2004-2012 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@ + */ + + +#ifndef __DYLDLOCK__ +#define __DYLDLOCK__ + +// +// This file contains the synchronization utilities used to make dyld APIs be thread safe. +// +// The implementation of all dyld API's must hold acquire global lock (in libSystem) +// before calling into dyld proper, and release the lock after returning from dyld. +// This is done using the macros DYLD_LOCK_THIS_BLOCK. +// Example: +// +// void dyld_api() { +// DYLD_LOCK_THIS_BLOCK; +// // free to do stuff here +// // that accesses dyld internal data structures +// } +// +// void dyld_api_state_free() { +// DYLD_NO_LOCK_THIS_BLOCK +// // can only do stuff here +// // that does not require locking +// } +// + +#define DYLD_LOCK_THIS_BLOCK LockHelper _dyld_lock; +#define DYLD_NO_LOCK_THIS_BLOCK + +// used by dyld wrapper functions in libSystem +class __attribute__((visibility("hidden"))) LockHelper +{ +public: + LockHelper(); + ~LockHelper(); +}; + + +// to initialize +extern void dyldGlobalLockInitialize() __attribute__((visibility("hidden"))); +extern void dyldGlobalLockAcquire() __attribute__((visibility("hidden"))); +extern void dyldGlobalLockRelease() __attribute__((visibility("hidden"))); + +#endif // __DYLDLOCK__ + diff --git a/src/dyld-apple/src/dyldNew.cpp b/src/dyld-apple/src/dyldNew.cpp new file mode 100644 index 000000000..cf64f58d9 --- /dev/null +++ b/src/dyld-apple/src/dyldNew.cpp @@ -0,0 +1,183 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2004-2008 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@ + */ + + +#include +#include +#include +#include +#include + +extern "C" void* __dso_handle; + +#include "dyld.h" +#include "dyldLibSystemInterface.h" + +// +// dyld initially allocates all memory from a pool inside dyld. +// Once libSystem.dylib is initialized, dyld uses libSystem's malloc/free. +// + +#if __LP64__ + // room for about ~1000 initial dylibs + #define DYLD_POOL_CHUNK_SIZE 200*1024 +#else + // room for about ~900 initial dylibs + #define DYLD_POOL_CHUNK_SIZE 150*1024 +#endif + +struct dyld_static_pool { + dyld_static_pool* previousPool; + uint8_t* current; + uint8_t* end; + uint8_t pool[1]; +}; + +// allocate initial pool independently of pool header to take less space on disk +static uint8_t initialPoolContent[DYLD_POOL_CHUNK_SIZE] __attribute__((__aligned__(16))); +static dyld_static_pool initialPool = { NULL, initialPoolContent, &initialPoolContent[DYLD_POOL_CHUNK_SIZE] }; +static dyld_static_pool* currentPool = &initialPool; + + +void* malloc(size_t size) +{ + if ( (dyld::gLibSystemHelpers != NULL) && dyld::gProcessInfo->libSystemInitialized ) { + void* p = dyld::gLibSystemHelpers->malloc(size); + //dyld::log("malloc(%lu) => %p from libSystem\n", size, p); + return p; + } + else { + if ( size > DYLD_POOL_CHUNK_SIZE ) { + dyld::log("dyld malloc overflow: size=%zu\n", size); + exit(1); + } + size = (size+sizeof(void*)-1) & (-sizeof(void*)); // pointer align + uint8_t* result = currentPool->current; + currentPool->current += size; + if ( currentPool->current > currentPool->end ) { + vm_address_t addr = 0; + kern_return_t r = vm_allocate(mach_task_self(), &addr, DYLD_POOL_CHUNK_SIZE, VM_FLAGS_ANYWHERE); + if ( r != KERN_SUCCESS ) { + dyld::log("out of address space for dyld memory pool\n"); + exit(1); + } + dyld_static_pool* newPool = (dyld_static_pool*)addr; + newPool->previousPool = NULL; + newPool->current = newPool->pool; + newPool->end = (uint8_t*)(addr + DYLD_POOL_CHUNK_SIZE); + newPool->previousPool = currentPool; + currentPool = newPool; + if ( (currentPool->current + size) > currentPool->end ) { + dyld::log("dyld memory pool exhausted: size=%lu\n", size); + exit(1); + } + result = currentPool->current; + currentPool->current += size; + } + //dyld::log("%p = malloc(%3lu) from pool %p, free space = %lu\n", result, size, currentPool, (long)(currentPool->end - currentPool->current)); + return result; + } +} + + +void free(void* ptr) +{ + // ignore any pointer within dyld (i.e. stuff from pool or static strings) + if ( (dyld::gLibSystemHelpers != NULL) && ((ptr < &__dso_handle) || (ptr >= &initialPoolContent[DYLD_POOL_CHUNK_SIZE])) ) { + // ignore stuff in any dynamically alloated dyld pools + for (dyld_static_pool* p = currentPool; p != NULL; p = p->previousPool) { + if ( (p->pool <= ptr) && (ptr < p->end) ) { + // do nothing, pool entries can't be reclaimed + //dyld::log("free(%p) from dynamic pool\n", ptr); + return; + } + } + + //dyld::log("free(%p) from libSystem\n", ptr); + return dyld::gLibSystemHelpers->free(ptr); + } + else { + // do nothing, pool entries can't be reclaimed + //dyld::log("free(%p) from static pool\n", ptr); + } +} + + +void* calloc(size_t count, size_t size) +{ + if ( dyld::gLibSystemHelpers != NULL ) { + void* result = dyld::gLibSystemHelpers->malloc(size*count); + bzero(result, size*count); + return result; + } + else { + // Check for overflow of integer multiplication + size_t total = count * size; + if ( total/count != size ) { + dyld::log("dyld calloc overflow: count=%zu, size=%zu\n", count, size); + exit(1); + } + return malloc(total); + } +} + + +void* realloc(void *ptr, size_t size) +{ + void* result = malloc(size); + memcpy(result, ptr, size); + return result; +} + +// void* reallocf(void *ptr, size_t size); +// void* valloc(size_t size); + +// needed __libc_init() +extern "C" int _malloc_lock; +int _malloc_lock = 0; + + +// dyld calls this which uses libSystem.dylib's vm_allocate if available +int vm_alloc(vm_address_t* addr, vm_size_t size, uint32_t flags) +{ + if ( (dyld::gLibSystemHelpers != NULL) && (dyld::gLibSystemHelpers->version >= 12) ) { + return dyld::gLibSystemHelpers->vm_alloc(mach_task_self(), addr, size, flags); + } + else { + return ::vm_allocate(mach_task_self(), addr, size, flags); + } +} + +void* xmmap(void* addr, size_t len, int prot, int flags, int fd, off_t offset) +{ + if ( (dyld::gLibSystemHelpers != NULL) && (dyld::gLibSystemHelpers->version >= 12) ) { + return dyld::gLibSystemHelpers->mmap(addr, len, prot, flags, fd, offset); + } + else { + return ::mmap(addr, len, prot, flags, fd, offset); + } +} + + + diff --git a/src/dyld-apple/src/dyldStartup.S b/src/dyld-apple/src/dyldStartup.S new file mode 100644 index 000000000..8f71e0c8e --- /dev/null +++ b/src/dyld-apple/src/dyldStartup.S @@ -0,0 +1,341 @@ +/* + * Copyright (c) 1999-2011 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@ + */ +/* + * C runtime startup for interface to the dynamic linker. + * This is the same as the entry point in crt0.o with the addition of the + * address of the mach header passed as the an extra first argument. + * + * Kernel sets up stack frame to look like: + * + * | STRING AREA | + * +-------------+ + * | 0 | +* +-------------+ + * | apple[n] | + * +-------------+ + * : + * +-------------+ + * | apple[0] | + * +-------------+ + * | 0 | + * +-------------+ + * | env[n] | + * +-------------+ + * : + * : + * +-------------+ + * | env[0] | + * +-------------+ + * | 0 | + * +-------------+ + * | arg[argc-1] | + * +-------------+ + * : + * : + * +-------------+ + * | arg[0] | + * +-------------+ + * | argc | + * +-------------+ + * sp-> | mh | address of where the a.out's file offset 0 is in memory + * +-------------+ + * + * Where arg[i] and env[i] point into the STRING AREA + */ + + +#include + + .globl __dyld_start + +#ifdef __i386__ + +#if !TARGET_IPHONE_SIMULATOR + .text + .align 4, 0x90 + .globl __dyld_start +__dyld_start: + popl %edx # edx = mh of app + pushl $0 # push a zero for debugger end of frames marker + movl %esp,%ebp # pointer to base of kernel frame + andl $-16,%esp # force SSE alignment + subl $32,%esp # room for locals and outgoing parameters + + call L__dyld_start_picbase +L__dyld_start_picbase: + popl %ebx # set %ebx to runtime value of picbase + + movl Lmh-L__dyld_start_picbase(%ebx), %ecx # ecx = prefered load address + movl __dyld_start_static_picbase-L__dyld_start_picbase(%ebx), %eax + subl %eax, %ebx # ebx = slide = L__dyld_start_picbase - [__dyld_start_static_picbase] + addl %ebx, %ecx # ecx = actual load address + # call dyldbootstrap::start(app_mh, argc, argv, slide, dyld_mh, &startGlue) + movl %edx,(%esp) # param1 = app_mh + movl 4(%ebp),%eax + movl %eax,4(%esp) # param2 = argc + lea 8(%ebp),%eax + movl %eax,8(%esp) # param3 = argv + movl %ebx,12(%esp) # param4 = slide + movl %ecx,16(%esp) # param5 = actual load address + lea 28(%esp),%eax + movl %eax,20(%esp) # param6 = &startGlue + call __ZN13dyldbootstrap5startEPK12macho_headeriPPKclS2_Pm + movl 28(%esp),%edx + cmpl $0,%edx + jne Lnew + + # clean up stack and jump to "start" in main executable + movl %ebp,%esp # restore the unaligned stack pointer + addl $4,%esp # remove debugger end frame marker + movl $0,%ebp # restore ebp back to zero + jmp *%eax # jump to the entry point + + # LC_MAIN case, set up stack for call to main() +Lnew: movl 4(%ebp),%ebx + movl %ebx,(%esp) # main param1 = argc + leal 8(%ebp),%ecx + movl %ecx,4(%esp) # main param2 = argv + leal 0x4(%ecx,%ebx,4),%ebx + movl %ebx,8(%esp) # main param3 = env +Lapple: movl (%ebx),%ecx # look for NULL ending env[] array + add $4,%ebx + testl %ecx,%ecx + jne Lapple # once found, next pointer is "apple" parameter now in %ebx + movl %ebx,12(%esp) # main param4 = apple + pushl %edx # simulate return address into _start in libdyld + jmp *%eax # jump to main(argc,argv,env,apple) with return address set to _start +#endif + +#if !TARGET_IPHONE_SIMULATOR + .data +__dyld_start_static_picbase: + .long L__dyld_start_picbase +Lmh: .long ___dso_handle +#endif + + +#endif /* __i386__ */ + + + +#if __x86_64__ +#if !TARGET_IPHONE_SIMULATOR + .data + .align 3 +__dyld_start_static: + .quad __dyld_start +#endif + + +#if !TARGET_IPHONE_SIMULATOR + .text + .align 2,0x90 + .globl __dyld_start +__dyld_start: + popq %rdi # param1 = mh of app + pushq $0 # push a zero for debugger end of frames marker + movq %rsp,%rbp # pointer to base of kernel frame + andq $-16,%rsp # force SSE alignment + subq $16,%rsp # room for local variables + + # call dyldbootstrap::start(app_mh, argc, argv, slide, dyld_mh, &startGlue) + movl 8(%rbp),%esi # param2 = argc into %esi + leaq 16(%rbp),%rdx # param3 = &argv[0] into %rdx + movq __dyld_start_static(%rip), %r8 + leaq __dyld_start(%rip), %rcx + subq %r8, %rcx # param4 = slide into %rcx + leaq ___dso_handle(%rip),%r8 # param5 = dyldsMachHeader + leaq -8(%rbp),%r9 + call __ZN13dyldbootstrap5startEPK12macho_headeriPPKclS2_Pm + movq -8(%rbp),%rdi + cmpq $0,%rdi + jne Lnew + + # clean up stack and jump to "start" in main executable + movq %rbp,%rsp # restore the unaligned stack pointer + addq $8,%rsp # remove the mh argument, and debugger end frame marker + movq $0,%rbp # restore ebp back to zero + jmp *%rax # jump to the entry point + + # LC_MAIN case, set up stack for call to main() +Lnew: addq $16,%rsp # remove local variables + pushq %rdi # simulate return address into _start in libdyld + movq 8(%rbp),%rdi # main param1 = argc into %rdi + leaq 16(%rbp),%rsi # main param2 = &argv[0] into %rsi + leaq 0x8(%rsi,%rdi,8),%rdx # main param3 = &env[0] into %rdx + movq %rdx,%rcx +Lapple: movq (%rcx),%r8 + add $8,%rcx + testq %r8,%r8 # look for NULL ending env[] array + jne Lapple # main param4 = apple into %rcx + jmp *%rax # jump to main(argc,argv,env,apple) with return address set to _start + +#endif /* TARGET_IPHONE_SIMULATOR */ +#endif /* __x86_64__ */ + + + +#if __arm__ + .syntax unified + .data + .align 2 +__dyld_start_static_picbase: + .long L__dyld_start_picbase + + + // Hack to make ___dso_handle work + // Without this local symbol, assembler will error out about in subtraction expression + // The real ___dso_handle (non-weak) sythesized by the linker + // Since this one is weak, the linker will throw this one away and use the real one instead. + .data + .globl ___dso_handle + .weak_definition ___dso_handle +___dso_handle: .long 0 + + .text + .align 2 +__dyld_start: + mov r8, sp // save stack pointer + sub sp, #16 // make room for outgoing parameters + bic sp, sp, #15 // force 16-byte alignment + + // call dyldbootstrap::start(app_mh, argc, argv, slide, dyld_mh, &startGlue) + + ldr r3, L__dyld_start_picbase_ptr +L__dyld_start_picbase: + sub r0, pc, #8 // load actual PC + ldr r3, [r0, r3] // load expected PC + sub r3, r0, r3 // r3 = slide + + ldr r0, [r8] // r0 = mach_header + ldr r1, [r8, #4] // r1 = argc + add r2, r8, #8 // r2 = argv + + ldr r4, Lmh +L3: add r4, r4, pc + str r4, [sp, #0] // [sp] = dyld_mh + add r4, sp, #12 + str r4, [sp, #4] // [sp+4] = &startGlue + + bl __ZN13dyldbootstrap5startEPK12macho_headeriPPKclS2_Pm + ldr r5, [sp, #12] + cmp r5, #0 + bne Lnew + + // traditional case, clean up stack and jump to result + add sp, r8, #4 // remove the mach_header argument. + bx r0 // jump to the program's entry point + + // LC_MAIN case, set up stack for call to main() +Lnew: mov lr, r5 // simulate return address into _start in libdyld + mov r5, r0 // save address of main() for later use + ldr r0, [r8, #4] // main param1 = argc + add r1, r8, #8 // main param2 = argv + add r2, r1, r0, lsl #2 + add r2, r2, #4 // main param3 = &env[0] + mov r3, r2 +Lapple: ldr r4, [r3] + add r3, #4 + cmp r4, #0 + bne Lapple // main param4 = apple + bx r5 + + .align 2 +L__dyld_start_picbase_ptr: + .long __dyld_start_static_picbase-L__dyld_start_picbase +Lmh: .long ___dso_handle-L3-8 + +#endif /* __arm__ */ + + + + +#if __arm64__ + .data + .align 3 +__dso_static: + .quad ___dso_handle + + .text + .align 2 + .globl __dyld_start +__dyld_start: + mov x28, sp + and sp, x28, #~15 // force 16-byte alignment of stack + mov x0, #0 + mov x1, #0 + stp x1, x0, [sp, #-16]! // make aligned terminating frame + mov fp, sp // set up fp to point to terminating frame + sub sp, sp, #16 // make room for local variables + ldr x0, [x28] // get app's mh into x0 + ldr x1, [x28, #8] // get argc into x1 (kernel passes 32-bit int argc as 64-bits on stack to keep alignment) + add x2, x28, #16 // get argv into x2 + adrp x4,___dso_handle@page + add x4,x4,___dso_handle@pageoff // get dyld's mh in to x4 + adrp x3,__dso_static@page + ldr x3,[x3,__dso_static@pageoff] // get unslid start of dyld + sub x3,x4,x3 // x3 now has slide of dyld + mov x5,sp // x5 has &startGlue + + // call dyldbootstrap::start(app_mh, argc, argv, slide, dyld_mh, &startGlue) + bl __ZN13dyldbootstrap5startEPK12macho_headeriPPKclS2_Pm + mov x16,x0 // save entry point address in x16 + ldr x1, [sp] + cmp x1, #0 + b.ne Lnew + + // LC_UNIXTHREAD way, clean up stack and jump to result + add sp, x28, #8 // restore unaligned stack pointer without app mh + br x16 // jump to the program's entry point + + // LC_MAIN case, set up stack for call to main() +Lnew: mov lr, x1 // simulate return address into _start in libdyld.dylib + ldr x0, [x28, #8] // main param1 = argc + add x1, x28, #16 // main param2 = argv + add x2, x1, x0, lsl #3 + add x2, x2, #8 // main param3 = &env[0] + mov x3, x2 +Lapple: ldr x4, [x3] + add x3, x3, #8 + cmp x4, #0 + b.ne Lapple // main param4 = apple + br x16 + +#endif // __arm64__ + + +// When iOS 10.0 simulator runs on 10.11, abort_with_payload() does not exist, +// so it falls back and uses dyld_fatal_error(). +#if TARGET_IPHONE_SIMULATOR + .text + .align 2 + .globl _dyld_fatal_error +_dyld_fatal_error: + int3 + nop +#endif + + + + + diff --git a/src/dyld-apple/src/dyldSyscallInterface.h b/src/dyld-apple/src/dyldSyscallInterface.h new file mode 100644 index 000000000..b67004a1e --- /dev/null +++ b/src/dyld-apple/src/dyldSyscallInterface.h @@ -0,0 +1,106 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2004-2013 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@ + */ + + +#ifndef __DYLD_SYSCALL_HELPERS__ +#define __DYLD_SYSCALL_HELPERS__ + +#include +#include +#include "../dyld_kernel.h" + +#if __cplusplus +namespace dyld { +#endif + + // + // This file contains the table of function pointers the host dyld supplies + // to the iOS simulator dyld. + // + struct SyscallHelpers + { + uintptr_t version; + int (*open)(const char* path, int oflag, int extra); + int (*close)(int fd); + ssize_t (*pread)(int fd, void* buf, size_t nbyte, off_t offset); + ssize_t (*write)(int fd, const void* buf, size_t nbyte); + void* (*mmap)(void* addr, size_t len, int prot, int flags, int fd, off_t offset); + int (*munmap)(void* addr, size_t len); + int (*madvise)(void* addr, size_t len, int advice); + int (*stat)(const char* path, struct stat* buf); + int (*fcntl)(int fildes, int cmd, void* result); + int (*ioctl)(int fildes, unsigned long request, void* result); + int (*issetugid)(void); + char* (*getcwd)(char* buf, size_t size); + char* (*realpath)(const char* file_name, char* resolved_name); + kern_return_t (*vm_allocate)(vm_map_t target_task, vm_address_t *address, vm_size_t size, int flags); + kern_return_t (*vm_deallocate)(vm_map_t target_task, vm_address_t address, vm_size_t size); + kern_return_t (*vm_protect)(vm_map_t target_task, vm_address_t address, vm_size_t size, boolean_t max, vm_prot_t prot); + void (*vlog)(const char* format, va_list list); + void (*vwarn)(const char* format, va_list list); + int (*pthread_mutex_lock)(pthread_mutex_t* m); + int (*pthread_mutex_unlock)(pthread_mutex_t* m); + mach_port_t (*mach_thread_self)(void); + kern_return_t (*mach_port_deallocate)(ipc_space_t task, mach_port_name_t name); + mach_port_name_t(*task_self_trap)(void); + kern_return_t (*mach_timebase_info)(mach_timebase_info_t info); + bool (*OSAtomicCompareAndSwapPtrBarrier)(void* old, void* nw, void * volatile *value); + void (*OSMemoryBarrier)(void); + void* (*getProcessInfo)(void); // returns dyld_all_image_infos*; + int* (*errnoAddress)(); + uint64_t (*mach_absolute_time)(); + // Added in version 2 + kern_return_t (*thread_switch)(mach_port_name_t, int, mach_msg_timeout_t); + // Added in version 3 + DIR* (*opendir)(const char* path); + int (*readdir_r)(DIR* dirp, struct dirent* entry, struct dirent **result); + int (*closedir)(DIR* dirp); + // Added in version 4 + void (*coresymbolication_load_notifier)(void *connection, uint64_t load_timestamp, const char *image_path, const struct mach_header *mach_header); + void (*coresymbolication_unload_notifier)(void *connection, uint64_t unload_timestamp, const char *image_path, const struct mach_header *mach_header); + // Added in version 5 + int (*proc_regionfilename)(int pid, uint64_t address, void* buffer, uint32_t buffersize); + int (*getpid)(); + kern_return_t (*mach_port_insert_right)(ipc_space_t task, mach_port_name_t name, mach_port_t poly, mach_msg_type_name_t polyPoly); + kern_return_t (*mach_port_allocate)(ipc_space_t, mach_port_right_t, mach_port_name_t*); + kern_return_t (*mach_msg)(mach_msg_header_t *, mach_msg_option_t , mach_msg_size_t , mach_msg_size_t , mach_port_name_t , mach_msg_timeout_t , mach_port_name_t); + // Added in version 6 + void (*abort_with_payload)(uint32_t reason_namespace, uint64_t reason_code, void* payload, uint32_t payload_size, const char* reason_string, uint64_t reason_flags); + // Add in version 7 + kern_return_t (*task_register_dyld_image_infos)(task_t task, dyld_kernel_image_info_array_t dyld_images, mach_msg_type_number_t dyld_imagesCnt); + kern_return_t (*task_unregister_dyld_image_infos)(task_t task, dyld_kernel_image_info_array_t dyld_images, mach_msg_type_number_t dyld_imagesCnt); + kern_return_t (*task_get_dyld_image_infos)(task_t task, dyld_kernel_image_info_array_t *dyld_images, mach_msg_type_number_t *dyld_imagesCnt); + kern_return_t (*task_register_dyld_shared_cache_image_info)(task_t task, dyld_kernel_image_info_t dyld_cache_image, boolean_t no_cache, boolean_t private_cache); + kern_return_t (*task_register_dyld_set_dyld_state)(task_t task, uint8_t dyld_state); + kern_return_t (*task_register_dyld_get_process_state)(task_t task, dyld_kernel_process_info_t *dyld_process_state); + }; + + extern const struct SyscallHelpers* gSyscallHelpers; + + +#if __cplusplus +} +#endif + +#endif diff --git a/src/dyld-apple/src/dyld_gdb.cpp b/src/dyld-apple/src/dyld_gdb.cpp new file mode 100644 index 000000000..f13064d27 --- /dev/null +++ b/src/dyld-apple/src/dyld_gdb.cpp @@ -0,0 +1,241 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2004-2009 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@ + */ + +#include +#include +#include +#include +#include + +#include + +#include "mach-o/dyld_gdb.h" +#include "mach-o/dyld_images.h" +#include "mach-o/dyld_process_info.h" +#include "ImageLoader.h" +#include "dyld.h" + +#if __IPHONE_OS_VERSION_MIN_REQUIRED + #define INITIAL_UUID_IMAGE_COUNT 4 +#else + #define INITIAL_UUID_IMAGE_COUNT 32 +#endif + +VECTOR_NEVER_DESTRUCTED(dyld_image_info); +VECTOR_NEVER_DESTRUCTED(dyld_uuid_info); + +static std::vector sImageInfos; +static std::vector sImageUUIDs; + +size_t allImagesCount() +{ + return sImageInfos.size(); +} + +const mach_header* allImagesIndexedMachHeader(uint32_t index) +{ + if ( index < sImageInfos.size() ) + return sImageInfos[index].imageLoadAddress; + else + return NULL; +} + +const char* allImagesIndexedPath(uint32_t index) +{ + if ( index < sImageInfos.size() ) + return sImageInfos[index].imageFilePath; + else + return NULL; +} + + +void addImagesToAllImages(uint32_t infoCount, const dyld_image_info info[]) +{ + // make initial size large enough that we probably won't need to re-alloc it + if ( sImageInfos.size() == 0 ) + sImageInfos.reserve(INITIAL_IMAGE_COUNT); + if ( sImageUUIDs.capacity() == 0 ) + sImageUUIDs.reserve(4); + // set infoArray to NULL to denote it is in-use + dyld::gProcessInfo->infoArray = NULL; + + // append all new images + for (uint32_t i=0; i < infoCount; ++i) + sImageInfos.push_back(info[i]); + dyld::gProcessInfo->infoArrayCount = (uint32_t)sImageInfos.size(); + dyld::gProcessInfo->infoArrayChangeTimestamp = mach_absolute_time(); + + // set infoArray back to base address of vector (other process can now read) + dyld::gProcessInfo->infoArray = &sImageInfos[0]; +} + +#if TARGET_IPHONE_SIMULATOR +// called once in dyld_sim start up to copy image list from host dyld to sImageInfos +void syncProcessInfo() +{ + // may want to set version field of gProcessInfo if it might be different than host + if ( sImageInfos.size() == 0 ) { + sImageInfos.reserve(INITIAL_IMAGE_COUNT); + if ( dyld::gProcessInfo->infoArray != NULL ) { + for (uint32_t i=0; i < dyld::gProcessInfo->infoArrayCount; ++i) { + sImageInfos.push_back(dyld::gProcessInfo->infoArray[i]); + } + dyld::gProcessInfo->infoArray = &sImageInfos[0]; + dyld::gProcessInfo->infoArrayCount = (uint32_t)sImageInfos.size(); + } + } + dyld::gProcessInfo->notification(dyld_image_info_change, 0, NULL); +} +#endif + +const char* notifyGDB(enum dyld_image_states state, uint32_t infoCount, const dyld_image_info info[]) +{ + // tell gdb that about the new images + uint64_t t0 = mach_absolute_time(); + dyld::gProcessInfo->notification(dyld_image_adding, infoCount, info); + uint64_t t1 = mach_absolute_time(); + ImageLoader::fgTotalDebuggerPausedTime += (t1-t0); + + // record initial count of images + // so CrashReporter can note which images were dynamically loaded + if ( dyld::gProcessInfo->initialImageCount == 0 ) + dyld::gProcessInfo->initialImageCount = dyld::gProcessInfo->infoArrayCount; + return NULL; +} + + + +void addNonSharedCacheImageUUID(const dyld_uuid_info& info) +{ + // set uuidArray to NULL to denote it is in-use + dyld::gProcessInfo->uuidArray = NULL; + + // append all new images + sImageUUIDs.push_back(info); + dyld::gProcessInfo->uuidArrayCount = sImageUUIDs.size(); + + // set uuidArray back to base address of vector (other process can now read) + dyld::gProcessInfo->uuidArray = &sImageUUIDs[0]; +} + +void removeImageFromAllImages(const struct mach_header* loadAddress) +{ + dyld_image_info goingAway; + + // set infoArray to NULL to denote it is in-use + dyld::gProcessInfo->infoArray = NULL; + + // remove image from infoArray + for (std::vector::iterator it=sImageInfos.begin(); it != sImageInfos.end(); it++) { + if ( it->imageLoadAddress == loadAddress ) { + goingAway = *it; + sImageInfos.erase(it); + break; + } + } + dyld::gProcessInfo->infoArrayCount = (uint32_t)sImageInfos.size(); + + // set infoArray back to base address of vector + dyld::gProcessInfo->infoArray = &sImageInfos[0]; + + + // set uuidArrayCount to NULL to denote it is in-use + dyld::gProcessInfo->uuidArray = NULL; + + // remove image from infoArray + for (std::vector::iterator it=sImageUUIDs.begin(); it != sImageUUIDs.end(); it++) { + if ( it->imageLoadAddress == loadAddress ) { + sImageUUIDs.erase(it); + break; + } + } + dyld::gProcessInfo->uuidArrayCount = sImageUUIDs.size(); + dyld::gProcessInfo->infoArrayChangeTimestamp = mach_absolute_time(); + + // set infoArray back to base address of vector + dyld::gProcessInfo->uuidArray = &sImageUUIDs[0]; + + // tell gdb that about the new images + dyld::gProcessInfo->notification(dyld_image_removing, 1, &goingAway); +} + + +#if TARGET_IPHONE_SIMULATOR + namespace dyld { + struct dyld_all_image_infos* gProcessInfo = NULL; + } +#else + + static void gdb_image_notifier(enum dyld_image_mode mode, uint32_t infoCount, const dyld_image_info info[]) + { + uint64_t machHeaders[infoCount]; + for (uint32_t i=0; i < infoCount; ++i) { + machHeaders[i] = (uintptr_t)(info[i].imageLoadAddress); + } + switch ( mode ) { + case dyld_image_adding: + _dyld_debugger_notification(dyld_notify_adding, infoCount, machHeaders); + break; + case dyld_image_removing: + _dyld_debugger_notification(dyld_notify_removing, infoCount, machHeaders); + break; + default: + break; + } + // do nothing + // gdb sets a break point here to catch notifications + //dyld::log("dyld: gdb_image_notifier(%s, %d, ...)\n", mode ? "dyld_image_removing" : "dyld_image_adding", infoCount); + //for (uint32_t i=0; i < infoCount; ++i) + // dyld::log("dyld: %d loading at %p %s\n", i, info[i].imageLoadAddress, info[i].imageFilePath); + //for (uint32_t i=0; i < dyld::gProcessInfo->infoArrayCount; ++i) + // dyld::log("dyld: %d loading at %p %s\n", i, dyld::gProcessInfo->infoArray[i].imageLoadAddress, dyld::gProcessInfo->infoArray[i].imageFilePath); + } + + // only used with accelerator tables and ASan which images need to be re-loaded + void resetAllImages() + { + sImageInfos.clear(); + sImageUUIDs.clear(); + _dyld_debugger_notification(dyld_notify_remove_all, 0, NULL); + } + + extern void* __dso_handle; + #define STR(s) # s + #define XSTR(s) STR(s) + + struct dyld_all_image_infos dyld_all_image_infos __attribute__ ((section ("__DATA,__all_image_info"))) + = { + 15, 0, NULL, &gdb_image_notifier, false, false, (const mach_header*)&__dso_handle, NULL, + XSTR(DYLD_VERSION), NULL, 0, NULL, 0, 0, NULL, &dyld_all_image_infos, + 0, 0, NULL, NULL, NULL, 0, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,}, + 0, 0, "/usr/lib/dyld", {0}, {0} + }; + + struct dyld_shared_cache_ranges dyld_shared_cache_ranges; + + namespace dyld { + struct dyld_all_image_infos* gProcessInfo = &dyld_all_image_infos; + } +#endif + diff --git a/src/dyld-apple/src/dyld_process_info.cpp b/src/dyld-apple/src/dyld_process_info.cpp new file mode 100644 index 000000000..849f17c1b --- /dev/null +++ b/src/dyld-apple/src/dyld_process_info.cpp @@ -0,0 +1,648 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2016 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@ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dyld_process_info.h" +#include "dyld_process_info_internal.h" +#include "dyld_images.h" +#include "dyld_priv.h" + + +// +// Opaque object returned by _dyld_process_info_create() +// + +struct __attribute__((visibility("hidden"))) dyld_process_info_base { + static dyld_process_info_base* make(task_t task, const dyld_all_image_infos_64& allImageInfo, const dyld_image_info_64 imageArray[], kern_return_t* kr); + static dyld_process_info_base* makeSuspended(task_t task, kern_return_t* kr); + + uint32_t& retainCount() const { return _retainCount; } + dyld_process_cache_info* cacheInfo() const { return (dyld_process_cache_info*)(((char*)this) + _cacheInfoOffset); } + dyld_process_state_info* stateInfo() const { return (dyld_process_state_info*)(((char*)this) + _stateInfoOffset); } + void forEachImage(void (^callback)(uint64_t machHeaderAddress, const uuid_t uuid, const char* path)) const; + void forEachSegment(uint64_t machHeaderAddress, void (^callback)(uint64_t segmentAddress, uint64_t segmentSize, const char* segmentName)) const; + +private: + struct ImageInfo { + uuid_t uuid; + uint64_t loadAddress; + const char* path; + uint32_t segmentStartIndex; + uint32_t segmentsCount; + }; + + struct SegmentInfo { + const char* name; + uint64_t addr; + uint64_t size; + }; + + dyld_process_info_base(unsigned imageCount, size_t totalSize); + void* operator new (size_t, void* buf) { return buf; } + + static bool inCache(uint64_t addr) { return (addr > SHARED_REGION_BASE) && (addr < SHARED_REGION_BASE+SHARED_REGION_SIZE); } + kern_return_t addImage(task_t task, bool sameCacheAsThisProcess, uint64_t imageAddress, uint64_t imagePath, const char* imagePathLocal); + kern_return_t addDyldImage(task_t task, uint64_t dyldAddress, uint64_t dyldPathAddress, const char* localPath); + + bool invalid() { return ((char*)_stringRevBumpPtr < (char*)_curSegment); } + const char* copyPath(task_t task, uint64_t pathAddr, kern_return_t* kr); + const char* addString(const char*); + const char* copySegmentName(const char*); + + void addInfoFromLoadCommands(const mach_header* mh, uint64_t addressInTask, size_t size); + + void inspectLocalImageLoadCommands(uint64_t imageAddress, void* func); + kern_return_t inspectRemoteImageLoadCommands(task_t task, uint64_t imageAddress, void* func); + + mutable uint32_t _retainCount; + const uint32_t _cacheInfoOffset; + const uint32_t _stateInfoOffset; + const uint32_t _imageInfosOffset; + const uint32_t _segmentInfosOffset; + ImageInfo* const _firstImage; + ImageInfo* _curImage; + SegmentInfo* const _firstSegment; + SegmentInfo* _curSegment; + uint32_t _curSegmentIndex; + char* _stringRevBumpPtr; + + // dyld_process_cache_info cacheInfo; + // dyld_process_state_info stateInfo; + // ImageInfo images[]; + // SegmentInfo segments[]; + // char stringPool[] +}; + +dyld_process_info_base::dyld_process_info_base(unsigned imageCount, size_t totalSize) + : _retainCount(1), _cacheInfoOffset(sizeof(dyld_process_info_base)), + _stateInfoOffset(sizeof(dyld_process_info_base) + sizeof(dyld_process_cache_info)), + _imageInfosOffset(sizeof(dyld_process_info_base) + sizeof(dyld_process_cache_info) + sizeof(dyld_process_state_info)), + _segmentInfosOffset(sizeof(dyld_process_info_base) + sizeof(dyld_process_cache_info) + sizeof(dyld_process_state_info) + imageCount*sizeof(ImageInfo)), + _firstImage((ImageInfo*)(((uint8_t*)this) + _imageInfosOffset)), + _curImage((ImageInfo*)(((uint8_t*)this) + _imageInfosOffset)), + _firstSegment((SegmentInfo*)(((uint8_t*)this) + _segmentInfosOffset)), + _curSegment((SegmentInfo*)(((uint8_t*)this) + _segmentInfosOffset)), + _curSegmentIndex(0), + _stringRevBumpPtr((char*)(this)+totalSize) +{ +} + + +dyld_process_info_base* dyld_process_info_base::make(task_t task, const dyld_all_image_infos_64& allImageInfo, const dyld_image_info_64 imageArray[], kern_return_t* kr) +{ + // figure out how many path strings will need to be copied and their size + const dyld_all_image_infos* myInfo = _dyld_get_all_image_infos(); + bool sameCacheAsThisProcess = ((memcmp(myInfo->sharedCacheUUID, allImageInfo.sharedCacheUUID, 16) == 0) && (myInfo->sharedCacheSlide == allImageInfo.sharedCacheSlide)); + unsigned countOfPathsNeedingCopying = 0; + if ( sameCacheAsThisProcess ) { + for (int i=0; i < allImageInfo.infoArrayCount; ++i) { + if ( !inCache(imageArray[i].imageFilePath) ) + ++countOfPathsNeedingCopying; + } + } + else { + countOfPathsNeedingCopying = allImageInfo.infoArrayCount+1; + } + unsigned imageCountWithDyld = allImageInfo.infoArrayCount+1; + + // allocate result object + size_t allocationSize = sizeof(dyld_process_info_base) + + sizeof(dyld_process_cache_info) + + sizeof(dyld_process_state_info) + + sizeof(ImageInfo)*(imageCountWithDyld) + + sizeof(SegmentInfo)*imageCountWithDyld*5 + + countOfPathsNeedingCopying*PATH_MAX; + void* storage = malloc(allocationSize); + dyld_process_info_base* obj = new (storage) dyld_process_info_base(imageCountWithDyld, allocationSize); // placement new() + + // fill in base info + dyld_process_cache_info* cacheInfo = obj->cacheInfo(); + memcpy(cacheInfo->cacheUUID, allImageInfo.sharedCacheUUID, 16); + cacheInfo->cacheBaseAddress = allImageInfo.sharedCacheBaseAddress; + cacheInfo->privateCache = allImageInfo.processDetachedFromSharedRegion; + // if no cache is used, allImageInfo has all zeros for cache UUID + cacheInfo->noCache = true; + for (int i=0; i < 16; ++i) { + if ( cacheInfo->cacheUUID[i] != 0 ) { + cacheInfo->noCache = false; + } + } + + dyld_process_state_info* stateInfo = obj->stateInfo(); + stateInfo->timestamp = allImageInfo.infoArrayChangeTimestamp; + stateInfo->imageCount = imageCountWithDyld; + stateInfo->initialImageCount = (uint32_t)(allImageInfo.initialImageCount+1); + if ( allImageInfo.infoArray != 0 ) + stateInfo->dyldState = dyld_process_state_dyld_initialized; + if ( allImageInfo.libSystemInitialized != 0 ) { + stateInfo->dyldState = dyld_process_state_libSystem_initialized; + if ( allImageInfo.initialImageCount != allImageInfo.infoArrayCount ) + stateInfo->dyldState = dyld_process_state_program_running; + } + if ( allImageInfo.errorMessage != 0 ) + stateInfo->dyldState = allImageInfo.terminationFlags ? dyld_process_state_terminated_before_inits : dyld_process_state_dyld_terminated; + + // fill in info for dyld + if ( allImageInfo.dyldPath != 0 ) { + if ( kern_return_t r = obj->addDyldImage(task, allImageInfo.dyldImageLoadAddress, allImageInfo.dyldPath, NULL) ) { + if ( kr != NULL ) + *kr = r; + goto fail; + } + } + + // fill in info for each image + for (uint32_t i=0; i < allImageInfo.infoArrayCount; ++i) { + if ( kern_return_t r = obj->addImage(task, sameCacheAsThisProcess, imageArray[i].imageLoadAddress, imageArray[i].imageFilePath, NULL) ) { + if ( kr != NULL ) + *kr = r; + goto fail; + } + } + + // sanity check internal data did not overflow + if ( obj->invalid() ) + goto fail; + + return obj; + +fail: + free(obj); + return NULL; +} + +dyld_process_info_base* dyld_process_info_base::makeSuspended(task_t task, kern_return_t* kr) +{ + pid_t pid; + kern_return_t result = pid_for_task(task, &pid); + if ( result != KERN_SUCCESS ) { + if ( kr != NULL ) + *kr = result; + return NULL; + } + + unsigned imageCount = 0; // main executable and dyld + uint64_t mainExecutableAddress = 0; + uint64_t dyldAddress = 0; + char dyldPathBuffer[PATH_MAX+1]; + char mainExecutablePathBuffer[PATH_MAX+1]; + mach_vm_size_t size; + for (mach_vm_address_t address = 0; ; address += size) { + vm_region_basic_info_data_64_t info; + mach_port_t objectName; + unsigned int infoCount = VM_REGION_BASIC_INFO_COUNT_64; + result = mach_vm_region(task, &address, &size, VM_REGION_BASIC_INFO, + (vm_region_info_t)&info, &infoCount, &objectName); + if ( result != KERN_SUCCESS ) + break; + if ( info.protection == (VM_PROT_READ|VM_PROT_EXECUTE) ) { + if ( mainExecutableAddress == 0 ) { + mainExecutableAddress = address; + int len = proc_regionfilename(pid, mainExecutableAddress, mainExecutablePathBuffer, PATH_MAX); + if ( len != 0 ) + mainExecutablePathBuffer[len] = '\0'; + ++imageCount; + } + else if ( dyldAddress == 0 ) { + dyldAddress = address; + int len = proc_regionfilename(pid, dyldAddress, dyldPathBuffer, PATH_MAX); + if ( len != 0 ) + dyldPathBuffer[len] = '\0'; + ++imageCount; + } + //fprintf(stderr, "vm region: addr=0x%llX, size=0x%llX, prot=0x%X\n", (uint64_t)address, (uint64_t)size, info.protection); + } + } + //fprintf(stderr, "dyld: addr=0x%llX, path=%s\n", dyldAddress, dyldPathBuffer); + //fprintf(stderr, "app: addr=0x%llX, path=%s\n", mainExecutableAddress, mainExecutablePathBuffer); + + // allocate result object + size_t allocationSize = sizeof(dyld_process_info_base) + + sizeof(dyld_process_cache_info) + + sizeof(dyld_process_state_info) + + sizeof(ImageInfo)*(imageCount) + + sizeof(SegmentInfo)*imageCount*5 + + imageCount*PATH_MAX; + void* storage = malloc(allocationSize); + dyld_process_info_base* obj = new (storage) dyld_process_info_base(imageCount, allocationSize); // placement new() + + // fill in base info + dyld_process_cache_info* cacheInfo = obj->cacheInfo(); + bzero(cacheInfo->cacheUUID, 16); + cacheInfo->cacheBaseAddress = 0; + cacheInfo->noCache = true; + cacheInfo->privateCache = false; + + dyld_process_state_info* stateInfo = obj->stateInfo(); + stateInfo->timestamp = 0; + stateInfo->imageCount = imageCount; + stateInfo->initialImageCount = imageCount; + stateInfo->dyldState = dyld_process_state_not_started; + + // fill in info for dyld + if ( dyldAddress != 0 ) { + if ( kern_return_t r = obj->addDyldImage(task, dyldAddress, 0, dyldPathBuffer) ) { + if ( kr != NULL ) + *kr = r; + free(obj); + return NULL; + } + } + + // fill in info for each image + if ( mainExecutableAddress != 0 ) { + if ( kern_return_t r = obj->addImage(task, false, mainExecutableAddress, 0, mainExecutablePathBuffer) ) { + if ( kr != NULL ) + *kr = r; + free(obj); + return NULL; + } + } + + return obj; +} + + + +const char* dyld_process_info_base::addString(const char* str) +{ + size_t len = strlen(str) + 1; + _stringRevBumpPtr -= len; + strcpy(_stringRevBumpPtr, str); + return _stringRevBumpPtr; +} + +const char* dyld_process_info_base::copyPath(task_t task, uint64_t stringAddressInTask, kern_return_t* kr) +{ + char temp[PATH_MAX+8]; // +8 is to allow '\0' at temp[PATH_MAX] + mach_vm_size_t readSize = PATH_MAX; + if ( ((stringAddressInTask & 0xFFF) + PATH_MAX) < 4096 ) { + // string fits within page, only one vm_read needed + if ( kern_return_t r = mach_vm_read_overwrite(task, stringAddressInTask, PATH_MAX, (vm_address_t)&temp, &readSize) ) { + if ( kr != NULL ) + *kr = r; + return NULL; + } + } + else { + // string may cross page boundary, split into two reads + size_t firstLen = 4096 - (stringAddressInTask & 0xFFF); + readSize = firstLen; + if ( kern_return_t r = mach_vm_read_overwrite(task, stringAddressInTask, firstLen, (vm_address_t)&temp, &readSize) ) { + if ( kr != NULL ) + *kr = r; + return NULL; + } + temp[firstLen] = '\0'; + if ( strlen(temp) >= firstLen ) { + readSize = PATH_MAX-firstLen; + if ( kern_return_t r = mach_vm_read_overwrite(task, stringAddressInTask+firstLen, PATH_MAX-firstLen, (vm_address_t)&temp+firstLen, &readSize) ) { + if ( kr != NULL ) + *kr = r; + return NULL; + temp[PATH_MAX] = '\0'; // truncate any string that is too long + } + } + } + if ( kr != NULL ) + *kr = KERN_SUCCESS; + return addString(temp); +} + + +kern_return_t dyld_process_info_base::addImage(task_t task, bool sameCacheAsThisProcess, uint64_t imageAddress, uint64_t imagePath, const char* imagePathLocal) +{ + _curImage->loadAddress = imageAddress; + _curImage->segmentStartIndex = _curSegmentIndex; + if ( imagePathLocal != NULL ) { + _curImage->path = addString(imagePathLocal); + } + else if ( sameCacheAsThisProcess && inCache(imagePath) ) { + _curImage->path = (const char*)imagePath; + } + else { + kern_return_t kr; + _curImage->path = copyPath(task, imagePath, &kr); + if ( kr ) + return kr; + } + if ( sameCacheAsThisProcess && inCache(imageAddress) ) { + addInfoFromLoadCommands((mach_header*)imageAddress, imageAddress, 32*1024); + } + else { + mach_vm_size_t readSize = sizeof(mach_header_64); + mach_header_64 mhBuffer; + if ( kern_return_t r = mach_vm_read_overwrite(task, imageAddress, sizeof(mach_header_64), (vm_address_t)&mhBuffer, &readSize) ) { + return r; + } + size_t headerPagesSize = (sizeof(mach_header_64) + mhBuffer.sizeofcmds + 4095) & (-4096); + vm_address_t localCopyBuffer; + unsigned int localCopyBufferSize; + if ( kern_return_t r = mach_vm_read(task, imageAddress, headerPagesSize, &localCopyBuffer, &localCopyBufferSize) ) { + return r; + } + addInfoFromLoadCommands((mach_header*)localCopyBuffer, imageAddress, localCopyBufferSize); + vm_deallocate(mach_task_self(), localCopyBuffer, localCopyBufferSize); + } + _curImage->segmentsCount = _curSegmentIndex - _curImage->segmentStartIndex; + _curImage++; + return KERN_SUCCESS; +} + + +kern_return_t dyld_process_info_base::addDyldImage(task_t task, uint64_t dyldAddress, uint64_t dyldPathAddress, const char* localPath) +{ + kern_return_t kr; + _curImage->loadAddress = dyldAddress; + _curImage->segmentStartIndex = _curSegmentIndex; + if ( localPath != NULL ) { + _curImage->path = addString(localPath); + } + else { + _curImage->path = copyPath(task, dyldPathAddress, &kr); + if ( kr ) + return kr; + } + + mach_vm_size_t readSize = sizeof(mach_header_64); + mach_header_64 mhBuffer; + if ( kern_return_t r = mach_vm_read_overwrite(task, dyldAddress, sizeof(mach_header_64), (vm_address_t)&mhBuffer, &readSize) ) { + return r; + } + size_t headerPagesSize = (sizeof(mach_header_64) + mhBuffer.sizeofcmds + 4095) & (-4096); + vm_address_t localCopyBuffer; + unsigned int localCopyBufferSize; + if ( kern_return_t r = mach_vm_read(task, dyldAddress, headerPagesSize, &localCopyBuffer, &localCopyBufferSize) ) { + return r; + } + addInfoFromLoadCommands((mach_header*)localCopyBuffer, dyldAddress, localCopyBufferSize); + vm_deallocate(mach_task_self(), localCopyBuffer, localCopyBufferSize); + _curImage->segmentsCount = _curSegmentIndex - _curImage->segmentStartIndex; + _curImage++; + return KERN_SUCCESS; +} + + +void dyld_process_info_base::addInfoFromLoadCommands(const mach_header* mh, uint64_t addressInTask, size_t size) +{ + const load_command* startCmds = NULL; + if ( mh->magic == MH_MAGIC_64 ) + startCmds = (load_command*)((char *)mh + sizeof(mach_header_64)); + else if ( mh->magic == MH_MAGIC ) + startCmds = (load_command*)((char *)mh + sizeof(mach_header)); + else + return; // not a mach-o file, or wrong endianness + + const load_command* const cmdsEnd = (load_command*)((char*)startCmds + mh->sizeofcmds); + const load_command* cmd = startCmds; + for(uint32_t i = 0; i < mh->ncmds; ++i) { + const load_command* nextCmd = (load_command*)((char *)cmd + cmd->cmdsize); + if ( (cmd->cmdsize < 8) || (nextCmd > cmdsEnd) || (nextCmd < startCmds) ) { + return; // malformed load command + } + if ( cmd->cmd == LC_UUID ) { + const uuid_command* uuidCmd = (uuid_command*)cmd; + memcpy(_curImage->uuid, uuidCmd->uuid, 16); + } + else if ( cmd->cmd == LC_SEGMENT ) { + const segment_command* segCmd = (segment_command*)cmd; + _curSegment->name = copySegmentName(segCmd->segname); + _curSegment->addr = segCmd->vmaddr; + _curSegment->size = segCmd->vmsize; + _curSegment++; + _curSegmentIndex++; + } + else if ( cmd->cmd == LC_SEGMENT_64 ) { + const segment_command_64* segCmd = (segment_command_64*)cmd; + _curSegment->name = copySegmentName(segCmd->segname); + _curSegment->addr = segCmd->vmaddr; + _curSegment->size = segCmd->vmsize; + _curSegment++; + _curSegmentIndex++; + } + cmd = nextCmd; + } +} + +const char* dyld_process_info_base::copySegmentName(const char* name) +{ + // don't copy names of standard segments into string pool + static const char* stdSegNames[] = {"__TEXT", "__DATA", "__LINKEDIT", "__DATA_DIRTY", "__DATA_CONST", "__OBJC", NULL }; + for (const char** s=stdSegNames; *s != NULL; ++s) { + if ( strcmp(name, *s) == 0 ) + return *s; + } + // copy custom segment names into string pool + return addString(name); +} + +void dyld_process_info_base::forEachImage(void (^callback)(uint64_t machHeaderAddress, const uuid_t uuid, const char* path)) const +{ + for (const ImageInfo* p = _firstImage; p < _curImage; ++p) { + callback(p->loadAddress, p->uuid, p->path); + } +} + +void dyld_process_info_base::forEachSegment(uint64_t machHeaderAddress, void (^callback)(uint64_t segmentAddress, uint64_t segmentSize, const char* segmentName)) const +{ + for (const ImageInfo* p = _firstImage; p < _curImage; ++p) { + if ( p->loadAddress == machHeaderAddress ) { + uint64_t slide = 0; + for (int i=0; i < p->segmentsCount; ++i) { + const SegmentInfo* seg = &_firstSegment[p->segmentStartIndex+i]; + if ( strcmp(seg->name, "__TEXT") == 0 ) { + slide = machHeaderAddress - seg->addr; + break; + } + } + for (int i=0; i < p->segmentsCount; ++i) { + const SegmentInfo* seg = &_firstSegment[p->segmentStartIndex+i]; + callback(seg->addr + slide, seg->size, seg->name); + } + break; + } + } +} + + + + + +// Implementation that works with existing dyld data structures +dyld_process_info _dyld_process_info_create(task_t task, uint64_t timestamp, kern_return_t* kr) +{ + if ( kr != NULL ) + *kr = KERN_SUCCESS; + + task_dyld_info_data_t task_dyld_info; + mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT; + if ( kern_return_t r = task_info(task, TASK_DYLD_INFO, (task_info_t)&task_dyld_info, &count) ) { + if ( kr != NULL ) + *kr = r; + return NULL; + } + + //The kernel will return MACH_VM_MIN_ADDRESS for an executable that has not had dyld loaded + if (task_dyld_info.all_image_info_addr == MACH_VM_MIN_ADDRESS) + return NULL; + + if ( task_dyld_info.all_image_info_size > sizeof(dyld_all_image_infos_64) ) + return NULL; + + // read all_image_infos struct + dyld_all_image_infos_64 allImageInfo64; + mach_vm_size_t readSize = task_dyld_info.all_image_info_size; + if ( kern_return_t r = mach_vm_read_overwrite(task, task_dyld_info.all_image_info_addr, task_dyld_info.all_image_info_size, (vm_address_t)&allImageInfo64, &readSize) ) { + if ( kr != NULL ) + *kr = r; + return NULL; + } + if ( allImageInfo64.infoArrayCount == 0 ) { + // could be task was launch suspended or still launching, wait a moment to see + usleep(1000 * 50); // 50ms + if ( kern_return_t r = mach_vm_read_overwrite(task, task_dyld_info.all_image_info_addr, task_dyld_info.all_image_info_size, (vm_address_t)&allImageInfo64, &readSize) ) { + if ( kr != NULL ) + *kr = r; + return NULL; + } + // if infoArrayCount is still zero, then target was most likely launched suspended + if ( allImageInfo64.infoArrayCount == 0 ) + return dyld_process_info_base::makeSuspended(task, kr); + } + + // bail out of dyld is too old + if ( allImageInfo64.version < 15 ) { + if ( kr != NULL ) + *kr = KERN_INVALID_HOST; + return NULL; + } + + // normalize by expanding 32-bit all_image_infos into 64-bit one + uint32_t imageCount = allImageInfo64.infoArrayCount; + size_t imageArraySize = imageCount * sizeof(dyld_image_info_64); + if ( task_dyld_info.all_image_info_format == TASK_DYLD_ALL_IMAGE_INFO_32 ) { + const dyld_all_image_infos_32* allImageInfo32 = (dyld_all_image_infos_32*)&allImageInfo64; + dyld_all_image_infos_64 info64; + bzero(&info64, sizeof(info64)); + info64.version = allImageInfo32->version; + info64.infoArrayCount = allImageInfo32->infoArrayCount; + info64.infoArray = allImageInfo32->infoArray; + info64.processDetachedFromSharedRegion = allImageInfo32->processDetachedFromSharedRegion; + info64.libSystemInitialized = allImageInfo32->libSystemInitialized; + info64.dyldImageLoadAddress = allImageInfo32->dyldImageLoadAddress; + info64.initialImageCount = allImageInfo32->initialImageCount; + info64.uuidArrayCount = allImageInfo32->uuidArrayCount; + info64.uuidArray = allImageInfo32->uuidArray; + info64.dyldAllImageInfosAddress = allImageInfo32->dyldAllImageInfosAddress; + info64.sharedCacheSlide = allImageInfo32->sharedCacheSlide; + info64.infoArrayChangeTimestamp = allImageInfo32->infoArrayChangeTimestamp; + info64.sharedCacheBaseAddress = allImageInfo32->sharedCacheBaseAddress; + info64.dyldPath = allImageInfo32->dyldPath; + memcpy((void*)(info64.sharedCacheUUID), (void*)(allImageInfo32->sharedCacheUUID), 16); + allImageInfo64 = info64; + imageCount = allImageInfo64.infoArrayCount; + imageArraySize = imageCount * sizeof(dyld_image_info_32); + } + + // don't do any (more) work if target process's dyld timestamp has not changed since previous query + if ( (timestamp != 0) && (timestamp == allImageInfo64.infoArrayChangeTimestamp) ) { + if ( kr != NULL ) + *kr = KERN_SUCCESS; + return NULL; + } + + // For the moment we are going to truncate any image list longer than 8192 because some programs do + // terrible things that corrupt their own image lists and we need to stop clients from crashing + // reading them. We can try to do something more advanced in the future. rdar://27446361 + imageCount = MIN(imageCount, 8192); + + // read image array + dyld_image_info_64 imageArray64[imageCount]; + if ( kern_return_t r = mach_vm_read_overwrite(task, allImageInfo64.infoArray, imageArraySize, (vm_address_t)&imageArray64, &readSize) ) { + if ( kr != NULL ) + *kr = r; + return NULL; + } + // normalize by expanding 32-bit image_infos into 64-bit ones + if ( task_dyld_info.all_image_info_format == TASK_DYLD_ALL_IMAGE_INFO_32 ) { + const dyld_image_info_32* imageArray32 = (dyld_image_info_32*)&imageArray64; + dyld_image_info_64 tempArray[imageCount]; + for (uint32_t i=0; i < imageCount; ++i) { + tempArray[i].imageLoadAddress = imageArray32[i].imageLoadAddress; + tempArray[i].imageFilePath = imageArray32[i].imageFilePath; + tempArray[i].imageFileModDate = imageArray32[i].imageFileModDate; + } + memcpy(imageArray64, tempArray, sizeof(dyld_image_info_64)*imageCount); + } + + // create object based on local copy of all image infos and image array + return dyld_process_info_base::make(task, allImageInfo64, imageArray64, kr); +} + + +void _dyld_process_info_get_state(dyld_process_info info, dyld_process_state_info* stateInfo) +{ + *stateInfo = *info->stateInfo(); +} + +void _dyld_process_info_get_cache(dyld_process_info info, dyld_process_cache_info* cacheInfo) +{ + *cacheInfo = *info->cacheInfo(); +} + +void _dyld_process_info_retain(dyld_process_info info) +{ + info->retainCount() += 1; +} + +void _dyld_process_info_release(dyld_process_info info) +{ + info->retainCount() -= 1; + if ( info->retainCount() == 0 ) + free((void*)info); +} + +void _dyld_process_info_for_each_image(dyld_process_info info, void (^callback)(uint64_t machHeaderAddress, const uuid_t uuid, const char* path)) +{ + info->forEachImage(callback); +} + + +void _dyld_process_info_for_each_segment(dyld_process_info info, uint64_t machHeaderAddress, void (^callback)(uint64_t segmentAddress, uint64_t segmentSize, const char* segmentName)) +{ + info->forEachSegment(machHeaderAddress, callback); +} + + + diff --git a/src/dyld-apple/src/dyld_process_info_internal.h b/src/dyld-apple/src/dyld_process_info_internal.h new file mode 100644 index 000000000..23f614bba --- /dev/null +++ b/src/dyld-apple/src/dyld_process_info_internal.h @@ -0,0 +1,135 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2016 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@ + */ + +#ifndef _DYLD_PROCESS_INFO_INTERNAL_H_ +#define _DYLD_PROCESS_INFO_INTERNAL_H_ + + +#include +#include +#include +#include + + +struct dyld_all_image_infos_32 { + uint32_t version; + uint32_t infoArrayCount; + uint32_t infoArray; + uint32_t notification; + bool processDetachedFromSharedRegion; + bool libSystemInitialized; + uint32_t dyldImageLoadAddress; + uint32_t jitInfo; + uint32_t dyldVersion; + uint32_t errorMessage; + uint32_t terminationFlags; + uint32_t coreSymbolicationShmPage; + uint32_t systemOrderFlag; + uint32_t uuidArrayCount; + uint32_t uuidArray; + uint32_t dyldAllImageInfosAddress; + uint32_t initialImageCount; + uint32_t errorKind; + uint32_t errorClientOfDylibPath; + uint32_t errorTargetDylibPath; + uint32_t errorSymbol; + uint32_t sharedCacheSlide; + uint8_t sharedCacheUUID[16]; + uint32_t sharedCacheBaseAddress; + uint64_t infoArrayChangeTimestamp; + uint32_t dyldPath; + uint32_t notifyMachPorts[2]; + uint32_t reserved[11]; +}; + +struct dyld_all_image_infos_64 { + uint32_t version; + uint32_t infoArrayCount; + uint64_t infoArray; + uint64_t notification; + bool processDetachedFromSharedRegion; + bool libSystemInitialized; + uint32_t paddingToMakeTheSizeCorrectOn32bitAndDoesntAffect64b; // NOT PART OF DYLD_ALL_IMAGE_INFOS! + uint64_t dyldImageLoadAddress; + uint64_t jitInfo; + uint64_t dyldVersion; + uint64_t errorMessage; + uint64_t terminationFlags; + uint64_t coreSymbolicationShmPage; + uint64_t systemOrderFlag; + uint64_t uuidArrayCount; + uint64_t uuidArray; + uint64_t dyldAllImageInfosAddress; + uint64_t initialImageCount; + uint64_t errorKind; + uint64_t errorClientOfDylibPath; + uint64_t errorTargetDylibPath; + uint64_t errorSymbol; + uint64_t sharedCacheSlide; + uint8_t sharedCacheUUID[16]; + uint64_t sharedCacheBaseAddress; + uint64_t infoArrayChangeTimestamp; + uint64_t dyldPath; + uint32_t notifyMachPorts[2]; + uint64_t reserved[12]; +}; + +struct dyld_image_info_32 { + uint32_t imageLoadAddress; + uint32_t imageFilePath; + uint32_t imageFileModDate; +}; +struct dyld_image_info_64 { + uint64_t imageLoadAddress; + uint64_t imageFilePath; + uint64_t imageFileModDate; +}; + +#define DYLD_PROCESS_INFO_NOTIFY_MAX_BUFFER_SIZE (32*1024) +#define DYLD_PROCESS_INFO_NOTIFY_LOAD_ID 0x1000 +#define DYLD_PROCESS_INFO_NOTIFY_UNLOAD_ID 0x2000 + + +struct dyld_process_info_image_entry { + uuid_t uuid; + uint64_t loadAddress; + uint32_t pathStringOffset; + uint32_t pathLength; +}; + +struct dyld_process_info_notify_header { + mach_msg_header_t header; + uint32_t version; + uint32_t imageCount; + uint32_t imagesOffset; + uint32_t stringsOffset; + uint64_t timestamp; +}; + + + + +#endif // _DYLD_PROCESS_INFO_INTERNAL_H_ + + diff --git a/src/dyld-apple/src/dyld_process_info_notify.cpp b/src/dyld-apple/src/dyld_process_info_notify.cpp new file mode 100644 index 000000000..3cfa78802 --- /dev/null +++ b/src/dyld-apple/src/dyld_process_info_notify.cpp @@ -0,0 +1,310 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2016 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@ + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "dyld_process_info.h" +#include "dyld_process_info_internal.h" +#include "dyld_images.h" +#include "dyld_priv.h" + +typedef void (^Notify)(bool unload, uint64_t timestamp, uint64_t machHeader, const uuid_t uuid, const char* path); +typedef void (^NotifyExit)(); + + +// +// Object used for monitoring another processes dyld loads +// +struct __attribute__((visibility("hidden"))) dyld_process_info_notify_base +{ + static dyld_process_info_notify_base* make(task_t task, dispatch_queue_t queue, Notify notify, NotifyExit notifyExit, kern_return_t* kr); + ~dyld_process_info_notify_base(); + + uint32_t& retainCount() const { return _retainCount; } + +private: + dyld_process_info_notify_base(dispatch_queue_t queue, Notify notify, NotifyExit notifyExit, task_t task); + kern_return_t makePorts(); + kern_return_t pokeSendPortIntoTarget(); + kern_return_t unpokeSendPortInTarget(); + void setMachSourceOnQueue(); + void* operator new (size_t, void* buf) { return buf; } + + mutable uint32_t _retainCount; + dispatch_queue_t _queue; + Notify _notify; + NotifyExit _notifyExit; + task_t _targetTask; + dispatch_source_t _machSource; + uint64_t _portAddressInTarget; + mach_port_t _sendPortInTarget; // target is process being watched for image loading/unloading + mach_port_t _receivePortInMonitor; // monitor is process being notified of image loading/unloading +}; + + +dyld_process_info_notify_base::dyld_process_info_notify_base(dispatch_queue_t queue, Notify notify, NotifyExit notifyExit, task_t task) + : _retainCount(1), _queue(queue), _notify(notify), _notifyExit(notifyExit), _targetTask(task), _machSource(NULL), _portAddressInTarget(0), _sendPortInTarget(0), _receivePortInMonitor(0) +{ + dispatch_retain(_queue); +} + +dyld_process_info_notify_base::~dyld_process_info_notify_base() +{ + if ( _machSource ) { + dispatch_release(_machSource); + _machSource = NULL; + } + if ( _portAddressInTarget ) { + unpokeSendPortInTarget(); + _portAddressInTarget = 0; + } + if ( _sendPortInTarget ) { + _sendPortInTarget = 0; + } + dispatch_release(_queue); + if ( _receivePortInMonitor != 0 ) { + mach_port_deallocate(mach_task_self(), _receivePortInMonitor); + _receivePortInMonitor = 0; + } +} + + +dyld_process_info_notify_base* dyld_process_info_notify_base::make(task_t task, dispatch_queue_t queue, Notify notify, NotifyExit notifyExit, kern_return_t* kr) +{ + void* storage = malloc(sizeof(dyld_process_info_notify_base)); + dyld_process_info_notify_base* obj = new (storage) dyld_process_info_notify_base(queue, notify, notifyExit, task); + + if ( kern_return_t r = obj->makePorts() ) { + if ( kr != NULL ) + *kr = r; + goto fail; + } + + obj->setMachSourceOnQueue(); + + if ( kern_return_t r = obj->pokeSendPortIntoTarget() ) { + if ( kr != NULL ) + *kr = r; + goto fail; + } + + if ( kr != NULL ) + *kr = KERN_SUCCESS; + return obj; + +fail: + free(obj); + return NULL; +} + + +kern_return_t dyld_process_info_notify_base::makePorts() +{ + // Allocate a port to listen on in this monitoring task + if ( kern_return_t r = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &_receivePortInMonitor) ) + return r; + + // Add send rights for replying + if ( kern_return_t r = mach_port_insert_right(mach_task_self(), _receivePortInMonitor, _receivePortInMonitor, MACH_MSG_TYPE_MAKE_SEND) ) + return r; + + // Allocate a name in the target. We need a new name to add send rights to + if ( kern_return_t r = mach_port_allocate(_targetTask, MACH_PORT_RIGHT_DEAD_NAME, &_sendPortInTarget) ) + return r; + + // Deallocate the dead name + if ( kern_return_t r = mach_port_mod_refs(_targetTask, _sendPortInTarget, MACH_PORT_RIGHT_DEAD_NAME, -1) ) + return r; + + // Make the dead name a send right to our listening port + if ( kern_return_t r = mach_port_insert_right(_targetTask, _sendPortInTarget, _receivePortInMonitor, MACH_MSG_TYPE_MAKE_SEND) ) + return r; + + // Notify us if the target dies + mach_port_t previous = MACH_PORT_NULL; + if ( kern_return_t r = mach_port_request_notification(_targetTask, _sendPortInTarget, MACH_NOTIFY_DEAD_NAME, 0, _receivePortInMonitor, MACH_MSG_TYPE_MAKE_SEND_ONCE, &previous)) + return r; + + //fprintf(stderr, "_sendPortInTarget=%d, _receivePortInMonitor=%d\n", _sendPortInTarget, _receivePortInMonitor); + return KERN_SUCCESS; +} + + + +void dyld_process_info_notify_base::setMachSourceOnQueue() +{ + NotifyExit exitHandler = _notifyExit; + _machSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV, _receivePortInMonitor, 0, _queue); + dispatch_source_set_event_handler(_machSource, ^{ + uint8_t messageBuffer[DYLD_PROCESS_INFO_NOTIFY_MAX_BUFFER_SIZE]; + mach_msg_header_t* h = (mach_msg_header_t*)messageBuffer; + + kern_return_t r = mach_msg(h, MACH_RCV_MSG, 0, sizeof(messageBuffer), _receivePortInMonitor, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); + if ( r == KERN_SUCCESS ) { + //fprintf(stderr, "received message id=0x%X, size=%d\n", h->msgh_id, h->msgh_size); + if ( h->msgh_id == DYLD_PROCESS_INFO_NOTIFY_LOAD_ID || h->msgh_id == DYLD_PROCESS_INFO_NOTIFY_UNLOAD_ID ) { + // run notifier block for each [un]load image + const dyld_process_info_notify_header* header = (dyld_process_info_notify_header*)messageBuffer; + const dyld_process_info_image_entry* entries = (dyld_process_info_image_entry*)&messageBuffer[header->imagesOffset]; + const char* const stringPool = (char*)&messageBuffer[header->stringsOffset]; + for (unsigned i=0; i < header->imageCount; ++i) { + bool isUnload = (h->msgh_id == DYLD_PROCESS_INFO_NOTIFY_UNLOAD_ID); + _notify(isUnload, header->timestamp, entries[i].loadAddress, entries[i].uuid, stringPool + entries[i].pathStringOffset); + } + // reply to dyld, so it can continue + mach_msg_header_t replyHeader; + replyHeader.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MAKE_SEND); + replyHeader.msgh_id = 0; + replyHeader.msgh_local_port = MACH_PORT_NULL; + replyHeader.msgh_remote_port = h->msgh_remote_port; + replyHeader.msgh_reserved = 0; + replyHeader.msgh_size = sizeof(replyHeader); + mach_msg(&replyHeader, MACH_SEND_MSG | MACH_SEND_TIMEOUT, replyHeader.msgh_size, 0, MACH_PORT_NULL, 100, MACH_PORT_NULL); + } + else if ( h->msgh_id == MACH_NOTIFY_PORT_DELETED ) { + mach_port_t deadPort = ((mach_port_deleted_notification_t *)h)->not_port; + //fprintf(stderr, "received message id=MACH_NOTIFY_PORT_DELETED, size=%d, deadPort=%d\n", h->msgh_size, deadPort); + if ( deadPort == _sendPortInTarget ) { + // target process died. Clean up ports + _sendPortInTarget = 0; + mach_port_deallocate(mach_task_self(), _receivePortInMonitor); + _receivePortInMonitor = 0; + _portAddressInTarget = 0; + // notify that target is gone + exitHandler(); + } + } + else { + fprintf(stderr, "received unknown message id=0x%X, size=%d\n", h->msgh_id, h->msgh_size); + } + } + }); + dispatch_resume(_machSource); +} + + +kern_return_t dyld_process_info_notify_base::pokeSendPortIntoTarget() +{ + // get location on all_image_infos in target task + task_dyld_info_data_t taskDyldInfo; + mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT; + kern_return_t r = task_info(_targetTask, TASK_DYLD_INFO, (task_info_t)&taskDyldInfo, &count); + if ( r ) + return r; + + // remap the page containing all_image_infos into this process r/w + mach_vm_address_t mappedAddress = 0; + mach_vm_size_t mappedSize = taskDyldInfo.all_image_info_size; + vm_prot_t curProt = VM_PROT_NONE; + vm_prot_t maxProt = VM_PROT_NONE; + r = mach_vm_remap(mach_task_self(), &mappedAddress, mappedSize, 0, VM_FLAGS_ANYWHERE | VM_FLAGS_RETURN_DATA_ADDR, + _targetTask, taskDyldInfo.all_image_info_addr, false, &curProt, &maxProt, VM_INHERIT_NONE); + if ( r ) + return r; + if ( curProt != (VM_PROT_READ|VM_PROT_WRITE) ) + return KERN_PROTECTION_FAILURE; + + // atomically set port into all_image_info_struct + static_assert(sizeof(mach_port_t) == sizeof(uint32_t), "machport size not 32-bits"); + + mach_vm_address_t mappedAddressToPokePort = 0; + if ( taskDyldInfo.all_image_info_format == TASK_DYLD_ALL_IMAGE_INFO_32 ) + mappedAddressToPokePort = mappedAddress + offsetof(dyld_all_image_infos_32,notifyMachPorts); + else + mappedAddressToPokePort = mappedAddress + offsetof(dyld_all_image_infos_64,notifyMachPorts); + + // use first available slot + bool slotFound = false; + for (int slotIndex=0; slotIndex < DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT; ++slotIndex) { + if ( OSAtomicCompareAndSwap32Barrier(0, _sendPortInTarget, (volatile int32_t*)mappedAddressToPokePort) ) { + slotFound = true; + break; + } + mappedAddressToPokePort += sizeof(uint32_t); + } + if ( !slotFound ) { + mach_vm_deallocate(mach_task_self(), mappedAddress, mappedSize); + return KERN_UREFS_OVERFLOW; + } + _portAddressInTarget = taskDyldInfo.all_image_info_addr + mappedAddressToPokePort - mappedAddress; + //fprintf(stderr, "poked port %d into target at address 0x%llX\n", _sendPortInTarget, _portAddressInTarget); + mach_vm_deallocate(mach_task_self(), mappedAddress, mappedSize); + return r; +} + + + +kern_return_t dyld_process_info_notify_base::unpokeSendPortInTarget() +{ + // remap the page containing all_image_infos into this process r/w + mach_vm_address_t mappedAddress = 0; + mach_vm_size_t mappedSize = sizeof(mach_port_t); + vm_prot_t curProt = VM_PROT_NONE; + vm_prot_t maxProt = VM_PROT_NONE; + kern_return_t r = mach_vm_remap(mach_task_self(), &mappedAddress, mappedSize, 0, VM_FLAGS_ANYWHERE | VM_FLAGS_RETURN_DATA_ADDR, + _targetTask, _portAddressInTarget, false, &curProt, &maxProt, VM_INHERIT_NONE); + if ( r ) + return r; + if ( curProt != (VM_PROT_READ|VM_PROT_WRITE) ) + return KERN_PROTECTION_FAILURE; + + OSAtomicCompareAndSwap32Barrier(_sendPortInTarget, 0, (volatile int32_t*)mappedAddress); + + //fprintf(stderr, "cleared port %d from target\n", _sendPortInTarget); + mach_vm_deallocate(mach_task_self(), mappedAddress, mappedSize); + return r; +} + + + +dyld_process_info_notify _dyld_process_info_notify(task_t task, dispatch_queue_t queue, + void (^notify)(bool unload, uint64_t timestamp, uint64_t machHeader, const uuid_t uuid, const char* path), + void (^notifyExit)(), + kern_return_t* kr) +{ + return dyld_process_info_notify_base::make(task, queue, notify, notifyExit, kr); +} + +void _dyld_process_info_notify_retain(dyld_process_info_notify object) +{ + object->retainCount() += 1; +} + +void _dyld_process_info_notify_release(dyld_process_info_notify object) +{ + object->retainCount() -= 1; + if ( object->retainCount() == 0 ) { + object->~dyld_process_info_notify_base(); + free((void*)object); + } +} + + + diff --git a/src/dyld-apple/src/dyld_sim.exp b/src/dyld-apple/src/dyld_sim.exp new file mode 100644 index 000000000..ccc744a1d --- /dev/null +++ b/src/dyld-apple/src/dyld_sim.exp @@ -0,0 +1,31 @@ +# +# Copyright (c) 2013 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@ +# + +# +# Be sure the following are not dead stripped +# + +# Used by various tools to see build number of dyld +_dyld_simVersionString +_dyld_simVersionNumber + diff --git a/src/dyld-apple/src/dyld_stub_binder.S b/src/dyld-apple/src/dyld_stub_binder.S new file mode 100644 index 000000000..5cc5658d2 --- /dev/null +++ b/src/dyld-apple/src/dyld_stub_binder.S @@ -0,0 +1,337 @@ +/* + * Copyright (c) 2008-2013 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@ + */ + +#include + +#include + + +#ifdef __i386__ + +#define MH_PARAM_OUT 0 +#define LP_PARAM_OUT 4 +#define XMMM0_SAVE 16 /* 16-byte align */ +#define XMMM1_SAVE 32 +#define XMMM2_SAVE 48 +#define XMMM3_SAVE 64 +#define EAX_SAVE 84 +#define ECX_SAVE 88 +#define EDX_SAVE 92 +#define LP_LOCAL 96 +#define MH_LOCAL 100 +#define STACK_SIZE 100 /* must be 4 mod 16 so that stack winds up 16-byte aliged */ +#define LP_OLD_BP_SAVE 104 + +/* + * sp+4 lazy binding info offset + * sp+0 address of ImageLoader cache + */ + .text + .align 4,0x90 + .globl dyld_stub_binder + .globl _misaligned_stack_error +dyld_stub_binder: + subl $STACK_SIZE,%esp # makes stack 16-byte aligned + movl %eax,EAX_SAVE(%esp) + movl LP_OLD_BP_SAVE(%esp),%eax # get lazy-pointer meta-parameter + movl %eax,LP_LOCAL(%esp) + movl %ebp,LP_OLD_BP_SAVE(%esp) # store epb back chain + movl %esp,%ebp # set epb to be this frame + add $LP_OLD_BP_SAVE,%ebp + movl %ecx,ECX_SAVE(%esp) + movl %edx,EDX_SAVE(%esp) + .align 0,0x90 +_misaligned_stack_error_: + movdqa %xmm0,XMMM0_SAVE(%esp) + movdqa %xmm1,XMMM1_SAVE(%esp) + movdqa %xmm2,XMMM2_SAVE(%esp) + movdqa %xmm3,XMMM3_SAVE(%esp) +dyld_stub_binder_: + movl MH_LOCAL(%esp),%eax # call dyld::fastBindLazySymbol(loadercache, lazyinfo) + movl %eax,MH_PARAM_OUT(%esp) + movl LP_LOCAL(%esp),%eax + movl %eax,LP_PARAM_OUT(%esp) + call __Z21_dyld_fast_stub_entryPvl + movdqa XMMM0_SAVE(%esp),%xmm0 # restore registers + movdqa XMMM1_SAVE(%esp),%xmm1 + movdqa XMMM2_SAVE(%esp),%xmm2 + movdqa XMMM3_SAVE(%esp),%xmm3 + movl ECX_SAVE(%esp),%ecx + movl EDX_SAVE(%esp),%edx + movl %eax,%ebp # move target address to epb + movl EAX_SAVE(%esp),%eax # restore eax + addl $STACK_SIZE+4,%esp # cut back stack + xchg %ebp, (%esp) # restore ebp and set target to top of stack + ret # jump to target + + +#endif /* __i386__ */ + + +#if __x86_64__ + +#define RET_ADDR_RBP 24 +#define LP_PARAM_RBP 16 +#define MH_PARAM_RBP 8 +#define OLD_RBP_RBP 0 + +#define RDI_SAVE_RBP -8 +#define RSI_SAVE_RBP -16 +#define RDX_SAVE_RBP -24 +#define RCX_SAVE_RBP -32 +#define RBX_SAVE_RBP -40 +#define RAX_SAVE_RBP -48 +#define R8_SAVE_RBP -56 +#define R9_SAVE_RBP -64 +#define STATIC_STACK_SIZE 256 // extra padding to allow it to be 64-byte aligned + +#define XMM0_SAVE_RSP 0x00 +#define XMM1_SAVE_RSP 0x10 +#define XMM2_SAVE_RSP 0x20 +#define XMM3_SAVE_RSP 0x30 +#define XMM4_SAVE_RSP 0x40 +#define XMM5_SAVE_RSP 0x50 +#define XMM6_SAVE_RSP 0x60 +#define XMM7_SAVE_RSP 0x70 + + + /* + * sp+16 return address + * sp+8 lazy binding info offset + * sp+0 address of ImageLoader cache + */ + .align 2,0x90 + .globl dyld_stub_binder +dyld_stub_binder: + pushq %rbp + test $0xF,%rsp # at this point stack should be 16-byte aligned + jne _stack_not_16_byte_aligned_error + movq %rsp,%rbp + subq $STATIC_STACK_SIZE,%rsp + movq %rdi,RDI_SAVE_RBP(%rbp) # save registers that might be used as parameters + movq %rsi,RSI_SAVE_RBP(%rbp) + movq %rdx,RDX_SAVE_RBP(%rbp) + movq %rcx,RCX_SAVE_RBP(%rbp) + movq %rbx,RBX_SAVE_RBP(%rbp) + movq %rax,RAX_SAVE_RBP(%rbp) + movq %r8, R8_SAVE_RBP(%rbp) + movq %r9, R9_SAVE_RBP(%rbp) + + cmpl $0, _inited(%rip) + jne Linited + movl $0x01,%eax + cpuid # get cpu features to check on xsave instruction support + andl $0x08000000,%ecx # check OSXSAVE bit + movl %ecx,_hasXSave(%rip) + cmpl $0, %ecx + jne LxsaveInfo + movl $1, _inited(%rip) + jmp Lsse + +LxsaveInfo: + movl $0x0D,%eax + movl $0x00,%ecx + cpuid # get xsave parameter info + movl %eax,_features_lo32(%rip) + movl %edx,_features_hi32(%rip) + movl %ecx,_bufferSize32(%rip) + movl $1, _inited(%rip) + +Linited: + cmpl $0, _hasXSave(%rip) + jne Lxsave + +Lsse: + subq $128, %rsp + movdqa %xmm0, XMM0_SAVE_RSP(%rsp) + movdqa %xmm1, XMM1_SAVE_RSP(%rsp) + movdqa %xmm2, XMM2_SAVE_RSP(%rsp) + movdqa %xmm3, XMM3_SAVE_RSP(%rsp) + movdqa %xmm4, XMM4_SAVE_RSP(%rsp) + movdqa %xmm5, XMM5_SAVE_RSP(%rsp) + movdqa %xmm6, XMM6_SAVE_RSP(%rsp) + movdqa %xmm7, XMM7_SAVE_RSP(%rsp) + jmp Lbind + +Lxsave: + movl _bufferSize32(%rip),%eax + movq %rsp, %rdi + subq %rax, %rdi # stack alloc buffer + andq $-64, %rdi # 64-byte align stack + movq %rdi, %rsp + # xsave requires buffer to be zero'ed out + movq $0, %rcx + movq %rdi, %r8 + movq %rdi, %r9 + addq %rax, %r9 +Lz: movq %rcx, (%r8) + addq $8, %r8 + cmpq %r8,%r9 + ja Lz + + movl _features_lo32(%rip),%eax + movl _features_hi32(%rip),%edx + # call xsave with buffer on stack and eax:edx flag bits + # note: do not use xsaveopt, it assumes you are using the same + # buffer as previous xsaves, and this thread is on the same cpu. + xsave (%rsp) + +Lbind: + movq MH_PARAM_RBP(%rbp),%rdi # call fastBindLazySymbol(loadercache, lazyinfo) + movq LP_PARAM_RBP(%rbp),%rsi + call __Z21_dyld_fast_stub_entryPvl + movq %rax,%r11 # copy jump target + + cmpl $0, _hasXSave(%rip) + jne Lxrstror + + movdqa XMM0_SAVE_RSP(%rsp),%xmm0 + movdqa XMM1_SAVE_RSP(%rsp),%xmm1 + movdqa XMM2_SAVE_RSP(%rsp),%xmm2 + movdqa XMM3_SAVE_RSP(%rsp),%xmm3 + movdqa XMM4_SAVE_RSP(%rsp),%xmm4 + movdqa XMM5_SAVE_RSP(%rsp),%xmm5 + movdqa XMM6_SAVE_RSP(%rsp),%xmm6 + movdqa XMM7_SAVE_RSP(%rsp),%xmm7 + jmp Ldone + +Lxrstror: + movl _features_lo32(%rip),%eax + movl _features_hi32(%rip),%edx + # call xsave with buffer on stack and eax:edx flag bits + xrstor (%rsp) + +Ldone: + movq RDI_SAVE_RBP(%rbp),%rdi + movq RSI_SAVE_RBP(%rbp),%rsi + movq RDX_SAVE_RBP(%rbp),%rdx + movq RCX_SAVE_RBP(%rbp),%rcx + movq RBX_SAVE_RBP(%rbp),%rbx + movq RAX_SAVE_RBP(%rbp),%rax + movq R8_SAVE_RBP(%rbp),%r8 + movq R9_SAVE_RBP(%rbp),%r9 + movq %rbp,%rsp + popq %rbp + addq $16,%rsp # remove meta-parameters + jmp *%r11 # jmp to target + +_stack_not_16_byte_aligned_error: + movdqa %xmm0, 0(%rsp) + int3 + + .data +# Cached info from cpuid. These must be lazily evaluated. +# You cannot initalize these from _dyld_initializer() because +# that function is called from another dylib... +_inited: .long 0 +_features_lo32: .long 0 +_features_hi32: .long 0 +_bufferSize32: .long 0 +_hasXSave: .long 0 + +#endif + + +#if __arm__ + /* + * sp+4 lazy binding info offset + * sp+0 address of ImageLoader cache + */ + + .text + .align 2 + .globl dyld_stub_binder +dyld_stub_binder: + stmfd sp!, {r0,r1,r2,r3,r7,lr} // save registers + add r7, sp, #16 // point FP to previous FP + + ldr r0, [sp, #24] // move address ImageLoader cache to 1st parameter + ldr r1, [sp, #28] // move lazy info offset 2nd parameter + +#if __ARM_ARCH_7K__ + vpush {d0, d1, d2, d3, d4, d5, d6, d7} + sub sp, sp, #8 // Align stack to 16 bytes. +#endif + // call dyld::fastBindLazySymbol(loadercache, lazyinfo) + bl __Z21_dyld_fast_stub_entryPvl + mov ip, r0 // move the symbol`s address into ip + +#if __ARM_ARCH_7K__ + add sp, sp, #8 + vpop {d0, d1, d2, d3, d4, d5, d6, d7} +#endif + + ldmfd sp!, {r0,r1,r2,r3,r7,lr} // restore registers + add sp, sp, #8 // remove meta-parameters + + bx ip // jump to the symbol`s address that was bound + +#endif /* __arm__ */ + + +#if __arm64__ + /* + * sp+0 lazy binding info offset + * sp+8 address of ImageLoader cache + */ + .text + .align 2 + .globl dyld_stub_binder +dyld_stub_binder: + stp fp, lr, [sp, #-16]! + mov fp, sp + sub sp, sp, #240 + stp x0,x1, [fp, #-16] ; x0-x7 are int parameter registers + stp x2,x3, [fp, #-32] + stp x4,x5, [fp, #-48] + stp x6,x7, [fp, #-64] + stp x8,x9, [fp, #-80] ; x8 is used for struct returns + stp q0,q1, [fp, #-128] ; q0-q7 are vector/fp parameter registers + stp q2,q3, [fp, #-160] + stp q4,q5, [fp, #-192] + stp q6,q7, [fp, #-224] + + ldr x0, [fp, #24] ; move address ImageLoader cache to 1st parameter + ldr x1, [fp, #16] ; move lazy info offset 2nd parameter + ; call dyld::fastBindLazySymbol(loadercache, lazyinfo) + bl __Z21_dyld_fast_stub_entryPvl + mov x16,x0 ; save target function address in lr + + ; restore parameter registers + ldp x0,x1, [fp, #-16] + ldp x2,x3, [fp, #-32] + ldp x4,x5, [fp, #-48] + ldp x6,x7, [fp, #-64] + ldp x8,x9, [fp, #-80] + ldp q0,q1, [fp, #-128] + ldp q2,q3, [fp, #-160] + ldp q4,q5, [fp, #-192] + ldp q6,q7, [fp, #-224] + + mov sp, fp + ldp fp, lr, [sp], #16 + add sp, sp, #16 ; remove meta-parameters + br x16 + +#endif + diff --git a/src/dyld-apple/src/glue.c b/src/dyld-apple/src/glue.c new file mode 100644 index 000000000..6eeed0a9e --- /dev/null +++ b/src/dyld-apple/src/glue.c @@ -0,0 +1,739 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2004-2010 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@ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if TARGET_IPHONE_SIMULATOR + #include "dyldSyscallInterface.h" + #include "dyld_images.h" + #include + #include + #include + #if __LP64__ + #define LC_SEGMENT_COMMAND LC_SEGMENT_64 + typedef struct segment_command_64 macho_segment_command; + typedef struct mach_header_64 macho_header; + typedef struct nlist_64 macho_nlist; + #else + #define LC_SEGMENT_COMMAND LC_SEGMENT + typedef struct segment_command macho_segment_command; + typedef struct mach_header macho_header; + typedef struct nlist macho_nlist; + #endif +#endif + +// from _simple.h in libc +typedef struct _SIMPLE* _SIMPLE_STRING; +extern void _simple_vdprintf(int __fd, const char *__fmt, va_list __ap); +extern void _simple_dprintf(int __fd, const char *__fmt, ...); +extern _SIMPLE_STRING _simple_salloc(void); +extern int _simple_vsprintf(_SIMPLE_STRING __b, const char *__fmt, va_list __ap); +extern void _simple_sfree(_SIMPLE_STRING __b); +extern char * _simple_string(_SIMPLE_STRING __b); + +// dyld::log(const char* format, ...) +extern void _ZN4dyld3logEPKcz(const char*, ...); + +// dyld::halt(const char* msg); +extern void _ZN4dyld4haltEPKc(const char* msg) __attribute__((noreturn)); + +extern void dyld_fatal_error(const char* errString) __attribute__((noreturn)); + + +// abort called by C++ unwinding code +void abort() +{ + _ZN4dyld4haltEPKc("dyld calling abort()\n"); +} + +// std::terminate called by C++ unwinding code +void _ZSt9terminatev() +{ + _ZN4dyld4haltEPKc("dyld std::terminate()\n"); +} + +// std::unexpected called by C++ unwinding code +void _ZSt10unexpectedv() +{ + _ZN4dyld4haltEPKc("dyld std::unexpected()\n"); +} + +// __cxxabiv1::__terminate(void (*)()) called to terminate process +void _ZN10__cxxabiv111__terminateEPFvvE() +{ + _ZN4dyld4haltEPKc("dyld std::__terminate()\n"); +} + +// __cxxabiv1::__unexpected(void (*)()) called to terminate process +void _ZN10__cxxabiv112__unexpectedEPFvvE() +{ + _ZN4dyld4haltEPKc("dyld std::__unexpected()\n"); +} + +// __cxxabiv1::__terminate_handler +void* _ZN10__cxxabiv119__terminate_handlerE = &_ZSt9terminatev; + +// __cxxabiv1::__unexpected_handler +void* _ZN10__cxxabiv120__unexpected_handlerE = &_ZSt10unexpectedv; + +// libc uses assert() +void __assert_rtn(const char* func, const char* file, int line, const char* failedexpr) +{ + if (func == NULL) + _ZN4dyld3logEPKcz("Assertion failed: (%s), file %s, line %d.\n", failedexpr, file, line); + else + _ZN4dyld3logEPKcz("Assertion failed: (%s), function %s, file %s, line %d.\n", failedexpr, func, file, line); + abort(); +} + + +int myfprintf(FILE* file, const char* format, ...) __asm("_fprintf"); + +// called by libuwind code before aborting +size_t fwrite(const void* ptr, size_t size, size_t nitme, FILE* stream) +{ + return myfprintf(stream, "%s", (char*)ptr); +} + +// called by libuwind code before aborting +int fprintf(FILE* file, const char* format, ...) +{ + va_list list; + va_start(list, format); + _simple_vdprintf(STDERR_FILENO, format, list); + va_end(list); + return 0; +} + +// called by LIBC_ABORT +void abort_report_np(const char* format, ...) +{ + va_list list; + const char *str; + _SIMPLE_STRING s = _simple_salloc(); + if ( s != NULL ) { + va_start(list, format); + _simple_vsprintf(s, format, list); + va_end(list); + str = _simple_string(s); + } + else { + // _simple_salloc failed, but at least format may have useful info by itself + str = format; + } + _ZN4dyld4haltEPKc(str); + // _ZN4dyld4haltEPKc doesn't return, so we can't call _simple_sfree +} + + +// real cthread_set_errno_self() has error handling that pulls in +// pthread_exit() which pulls in fprintf() +extern int* __error(void); +void cthread_set_errno_self(int err) +{ + int* ep = __error(); + *ep = err; +} + +/* + * We have our own localtime() to avoid needing the notify API which is used + * by the code in libc.a for localtime() which is used by arc4random(). + */ +struct tm* localtime(const time_t* t) +{ + return (struct tm*)NULL; +} + +// malloc calls exit(-1) in case of errors... +void exit(int x) +{ + _ZN4dyld4haltEPKc("exit()"); +} + +// static initializers make calls to __cxa_atexit +void __cxa_atexit() +{ + // do nothing, dyld never terminates +} + +// +// The stack protector routines in lib.c bring in too much stuff, so +// make our own custom ones. +// +long __stack_chk_guard = 0; + + +void __guard_setup(const char* apple[]) +{ + for (const char** p = apple; *p != NULL; ++p) { + if ( strncmp(*p, "stack_guard=", 12) == 0 ) { + // kernel has provide a random value for us + for (const char* s = *p + 12; *s != '\0'; ++s) { + char c = *s; + long value = 0; + if ( (c >= 'a') && (c <= 'f') ) + value = c - 'a' + 10; + else if ( (c >= 'A') && (c <= 'F') ) + value = c - 'A' + 10; + else if ( (c >= '0') && (c <= '9') ) + value = c - '0'; + __stack_chk_guard <<= 4; + __stack_chk_guard |= value; + } + if ( __stack_chk_guard != 0 ) + return; + } + } +#if !TARGET_IPHONE_SIMULATOR +#if __LP64__ + __stack_chk_guard = ((long)arc4random() << 32) | arc4random(); +#else + __stack_chk_guard = arc4random(); +#endif +#endif +} + +extern void _ZN4dyld4haltEPKc(const char*); +void __stack_chk_fail() +{ + _ZN4dyld4haltEPKc("stack buffer overrun"); +} + + +// std::_throw_bad_alloc() +void _ZSt17__throw_bad_allocv() +{ + _ZN4dyld4haltEPKc("__throw_bad_alloc()"); +} + +// std::_throw_length_error(const char* x) +void _ZSt20__throw_length_errorPKc() +{ + _ZN4dyld4haltEPKc("_throw_length_error()"); +} + +// the libc.a version of this drags in ASL +void __chk_fail() +{ + _ZN4dyld4haltEPKc("__chk_fail()"); +} + + +// referenced by libc.a(pthread.o) but unneeded in dyld +void _init_cpu_capabilities() { } +void _cpu_capabilities() {} +void set_malloc_singlethreaded() {} +int PR_5243343_flag = 0; + + +// used by some pthread routines +char* mach_error_string(mach_error_t err) +{ + return (char *)"unknown error code"; +} +char* mach_error_type(mach_error_t err) +{ + return (char *)"(unknown/unknown)"; +} + +// _pthread_reap_thread calls fprintf(stderr). +// We map fprint to _simple_vdprintf and ignore FILE* stream, so ok for it to be NULL +FILE* __stderrp = NULL; +FILE* __stdoutp = NULL; + +// work with c++abi.a +void (*__cxa_terminate_handler)() = _ZSt9terminatev; +void (*__cxa_unexpected_handler)() = _ZSt10unexpectedv; + +void abort_message(const char* format, ...) +{ + va_list list; + va_start(list, format); + _simple_vdprintf(STDERR_FILENO, format, list); + va_end(list); +} + +void __cxa_bad_typeid() +{ + _ZN4dyld4haltEPKc("__cxa_bad_typeid()"); +} + +// to work with libc++ +void _ZNKSt3__120__vector_base_commonILb1EE20__throw_length_errorEv() +{ + _ZN4dyld4haltEPKc("std::vector<>::_throw_length_error()"); +} + +// libc.a sometimes missing memset +#undef memset +void* memset(void* b, int c, size_t len) +{ + uint8_t* p = (uint8_t*)b; + for(size_t i=len; i > 0; --i) + *p++ = c; + return b; +} + + +// wrap calls to stat() with check for EAGAIN +int _ZN4dyld7my_statEPKcP4stat(const char* path, struct stat* buf) +{ + int result; + do { + result = stat(path, buf); + } while ((result == -1) && (errno == EAGAIN)); + + return result; +} + +// dyld should retry open() if it gets an EGAIN +int _ZN4dyld7my_openEPKcii(const char* path, int flag, int other) +{ + int result; + do { + result = open(path, flag, other); + } while ((result == -1) && (errno == EAGAIN)); + + return result; +} + + +// +// The dyld in the iOS simulator cannot do syscalls, so it calls back to +// host dyld. +// + +#if TARGET_IPHONE_SIMULATOR + +#include + +int myopen(const char* path, int oflag, int extra) __asm("_open"); +int myopen(const char* path, int oflag, int extra) { + return gSyscallHelpers->open(path, oflag, extra); +} + +int close(int fd) { + return gSyscallHelpers->close(fd); +} + +ssize_t pread(int fd, void* buf, size_t nbytes, off_t offset) { + return gSyscallHelpers->pread(fd, buf , nbytes, offset); +} + +ssize_t write(int fd, const void *buf, size_t nbytes) { + return gSyscallHelpers->write(fd, buf , nbytes); +} + +void* mmap(void* addr, size_t len, int prot, int flags, int fd, off_t offset) { + return gSyscallHelpers->mmap(addr, len, prot, flags, fd, offset); +} + +int munmap(void* addr, size_t len) { + return gSyscallHelpers->munmap(addr, len); +} + +int madvise(void* addr, size_t len, int advice) { + return gSyscallHelpers->madvise(addr, len, advice); +} + +int stat(const char* path, struct stat* buf) { + return gSyscallHelpers->stat(path, buf); +} + +int myfcntl(int fd, int cmd, void* result) __asm("_fcntl"); +int myfcntl(int fd, int cmd, void* result) { + return gSyscallHelpers->fcntl(fd, cmd, result); +} + +int myioctl(int fd, unsigned long request, void* result) __asm("_ioctl"); +int myioctl(int fd, unsigned long request, void* result) { + return gSyscallHelpers->ioctl(fd, request, result); +} + +int issetugid() { + return gSyscallHelpers->issetugid(); +} + +char* getcwd(char* buf, size_t size) { + return gSyscallHelpers->getcwd(buf, size); +} + +char* realpath(const char* file_name, char* resolved_name) { + return gSyscallHelpers->realpath(file_name, resolved_name); +} + + + +kern_return_t vm_allocate(vm_map_t target_task, vm_address_t *address, + vm_size_t size, int flags) { + return gSyscallHelpers->vm_allocate(target_task, address, size, flags); +} + +kern_return_t vm_deallocate(vm_map_t target_task, vm_address_t address, + vm_size_t size) { + return gSyscallHelpers->vm_deallocate(target_task, address, size); +} + +kern_return_t vm_protect(vm_map_t target_task, vm_address_t address, + vm_size_t size, boolean_t max, vm_prot_t prot) { + return gSyscallHelpers->vm_protect(target_task, address, size, max, prot); +} + + +void _ZN4dyld3logEPKcz(const char* format, ...) { + va_list list; + va_start(list, format); + gSyscallHelpers->vlog(format, list); + va_end(list); +} + +void _ZN4dyld4warnEPKcz(const char* format, ...) { + va_list list; + va_start(list, format); + gSyscallHelpers->vwarn(format, list); + va_end(list); +} + + +int pthread_mutex_lock(pthread_mutex_t* m) { + return gSyscallHelpers->pthread_mutex_lock(m); +} + +int pthread_mutex_unlock(pthread_mutex_t* m) { + return gSyscallHelpers->pthread_mutex_unlock(m); +} + +mach_port_t mach_thread_self() { + return gSyscallHelpers->mach_thread_self(); +} + +kern_return_t mach_port_deallocate(ipc_space_t task, mach_port_name_t name) { + return gSyscallHelpers->mach_port_deallocate(task, name); +} + +mach_port_name_t task_self_trap() { + return gSyscallHelpers->task_self_trap(); +} + +kern_return_t mach_timebase_info(mach_timebase_info_t info) { + return gSyscallHelpers->mach_timebase_info(info); +} + +bool OSAtomicCompareAndSwapPtrBarrier(void* old, void* new, void * volatile *value) { + return gSyscallHelpers->OSAtomicCompareAndSwapPtrBarrier(old, new, value); +} + +void OSMemoryBarrier() { + return gSyscallHelpers->OSMemoryBarrier(); +} + +uint64_t mach_absolute_time(void) { + return gSyscallHelpers->mach_absolute_time(); +} + +kern_return_t thread_switch(mach_port_name_t thread_name, + int option, mach_msg_timeout_t option_time) { + if ( gSyscallHelpers->version < 2 ) + return KERN_FAILURE; + return gSyscallHelpers->thread_switch(thread_name, option, option_time); +} + +DIR* opendir(const char* path) { + if ( gSyscallHelpers->version < 3 ) + return NULL; + return gSyscallHelpers->opendir(path); +} + +int readdir_r(DIR* dirp, struct dirent* entry, struct dirent **result) { + if ( gSyscallHelpers->version < 3 ) + return EPERM; + return gSyscallHelpers->readdir_r(dirp, entry, result); +} + +int closedir(DIR* dirp) { + if ( gSyscallHelpers->version < 3 ) + return EPERM; + return gSyscallHelpers->closedir(dirp); +} + +void xcoresymbolication_load_notifier(void* connection, uint64_t timestamp, const char* path, const struct mach_header* mh) +{ + // if host dyld supports this notifier, call into host dyld + if ( gSyscallHelpers->version >= 4 ) + return gSyscallHelpers->coresymbolication_load_notifier(connection, timestamp, path, mh); +} + +void xcoresymbolication_unload_notifier(void* connection, uint64_t timestamp, const char* path, const struct mach_header* mh) +{ + // if host dyld supports this notifier, call into host dyld + if ( gSyscallHelpers->version >= 4 ) + return gSyscallHelpers->coresymbolication_unload_notifier(connection, timestamp, path, mh); +} + + + +#define SUPPORT_HOST_10_11 1 + +#if SUPPORT_HOST_10_11 +typedef int (*FuncPtr_proc_regionfilename)(int pid, uint64_t address, void* buffer, uint32_t bufferSize); +typedef pid_t (*FuncPtr_getpid)(); +typedef bool (*FuncPtr_mach_port_insert_right)(ipc_space_t task, mach_port_name_t name, mach_port_t poly, mach_msg_type_name_t polyPoly); +typedef kern_return_t (*FuncPtr_mach_port_allocate)(ipc_space_t, mach_port_right_t, mach_port_name_t*); +typedef mach_msg_return_t (*FuncPtr_mach_msg)(mach_msg_header_t *, mach_msg_option_t , mach_msg_size_t , mach_msg_size_t , mach_port_name_t , mach_msg_timeout_t , mach_port_name_t); + +static FuncPtr_proc_regionfilename proc_proc_regionfilename = NULL; +static FuncPtr_getpid proc_getpid = NULL; +static FuncPtr_mach_port_insert_right proc_mach_port_insert_right = NULL; +static FuncPtr_mach_port_allocate proc_mach_port_allocate = NULL; +static FuncPtr_mach_msg proc_mach_msg = NULL; + + + +// Look up sycalls in host dyld needed by coresymbolication_ routines in dyld_sim +static void findHostFunctions() { + // Only look up symbols once + if ( proc_mach_msg != NULL ) + return; + + struct dyld_all_image_infos* imageInfo = (struct dyld_all_image_infos*)(gSyscallHelpers->getProcessInfo()); + const struct mach_header* hostDyldMH = imageInfo->dyldImageLoadAddress; + + // find symbol table and slide of host dyld + uintptr_t slide = 0; + const macho_nlist* symbolTable = NULL; + const char* symbolTableStrings = NULL; + const struct dysymtab_command* dynSymbolTable = NULL; + const uint32_t cmd_count = hostDyldMH->ncmds; + const struct load_command* const cmds = (struct load_command*)(((char*)hostDyldMH)+sizeof(macho_header)); + const struct load_command* cmd = cmds; + const uint8_t* linkEditBase = NULL; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd) { + case LC_SEGMENT_COMMAND: + { + const macho_segment_command* seg = (macho_segment_command*)cmd; + if ( (seg->fileoff == 0) && (seg->filesize != 0) ) + slide = (uintptr_t)hostDyldMH - seg->vmaddr; + if ( strcmp(seg->segname, "__LINKEDIT") == 0 ) + linkEditBase = (uint8_t*)(seg->vmaddr - seg->fileoff + slide); + } + break; + case LC_SYMTAB: + { + const struct symtab_command* symtab = (struct symtab_command*)cmd; + if ( linkEditBase == NULL ) + return; + symbolTableStrings = (const char*)&linkEditBase[symtab->stroff]; + symbolTable = (macho_nlist*)(&linkEditBase[symtab->symoff]); + } + break; + case LC_DYSYMTAB: + dynSymbolTable = (struct dysymtab_command*)cmd; + break; + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + if ( symbolTableStrings == NULL ) + return; + if ( dynSymbolTable == NULL ) + return; + + // scan local symbols in host dyld looking for load/unload functions + const macho_nlist* const localsStart = &symbolTable[dynSymbolTable->ilocalsym]; + const macho_nlist* const localsEnd= &localsStart[dynSymbolTable->nlocalsym]; + for (const macho_nlist* s = localsStart; s < localsEnd; ++s) { + if ( ((s->n_type & N_TYPE) == N_SECT) && ((s->n_type & N_STAB) == 0) ) { + const char* name = &symbolTableStrings[s->n_un.n_strx]; + if ( strcmp(name, "_proc_regionfilename") == 0 ) + proc_proc_regionfilename = (FuncPtr_proc_regionfilename)(s->n_value + slide); + else if ( strcmp(name, "_getpid") == 0 ) + proc_getpid = (FuncPtr_getpid)(s->n_value + slide); + else if ( strcmp(name, "mach_port_insert_right") == 0 ) + proc_mach_port_insert_right = (FuncPtr_mach_port_insert_right)(s->n_value + slide); + else if ( strcmp(name, "_mach_port_allocate") == 0 ) + proc_mach_port_allocate = (FuncPtr_mach_port_allocate)(s->n_value + slide); + else if ( strcmp(name, "_mach_msg") == 0 ) + proc_mach_msg = (FuncPtr_mach_msg)(s->n_value + slide); + } + } +} +#endif + + +int proc_regionfilename(int pid, uint64_t address, void* buffer, uint32_t bufferSize) +{ + if ( gSyscallHelpers->version >= 5 ) + return gSyscallHelpers->proc_regionfilename(pid, address, buffer, bufferSize); +#if SUPPORT_HOST_10_11 + findHostFunctions(); + if ( proc_proc_regionfilename ) + return (*proc_proc_regionfilename)(pid, address, buffer, bufferSize); + else + return 0; +#else + return 0; +#endif +} + +pid_t getpid() +{ + if ( gSyscallHelpers->version >= 5 ) + return gSyscallHelpers->getpid(); +#if SUPPORT_HOST_10_11 + findHostFunctions(); + return (*proc_getpid)(); +#else + return 0; +#endif +} + +kern_return_t mach_port_insert_right(ipc_space_t task, mach_port_name_t name, mach_port_t poly, mach_msg_type_name_t polyPoly) +{ + if ( gSyscallHelpers->version >= 5 ) + return gSyscallHelpers->mach_port_insert_right(task, name, poly, polyPoly); +#if SUPPORT_HOST_10_11 + findHostFunctions(); + if ( proc_mach_port_insert_right ) + return (*proc_mach_port_insert_right)(task, name, poly, polyPoly); + else + return KERN_NOT_SUPPORTED; +#else + return KERN_NOT_SUPPORTED; +#endif +} + +kern_return_t mach_port_allocate(ipc_space_t task, mach_port_right_t right, mach_port_name_t* name) +{ + if ( gSyscallHelpers->version >= 5 ) + return gSyscallHelpers->mach_port_allocate(task, right, name); +#if SUPPORT_HOST_10_11 + findHostFunctions(); + return (*proc_mach_port_allocate)(task, right, name); +#else + return KERN_NOT_SUPPORTED; +#endif +} + +kern_return_t mach_msg(mach_msg_header_t* msg, mach_msg_option_t option, mach_msg_size_t send_size, mach_msg_size_t rcv_size, mach_port_name_t rcv_name, mach_msg_timeout_t timeout, mach_port_name_t notify) +{ + if ( gSyscallHelpers->version >= 5 ) + return gSyscallHelpers->mach_msg(msg, option, send_size, rcv_size, rcv_name, timeout, notify); +#if SUPPORT_HOST_10_11 + findHostFunctions(); + return (*proc_mach_msg)(msg, option, send_size, rcv_size, rcv_name, timeout, notify); +#else + return KERN_NOT_SUPPORTED; +#endif +} + + +void abort_with_payload(uint32_t reason_namespace, uint64_t reason_code, void* payload, uint32_t payload_size, const char* reason_string, uint64_t reason_flags) +{ + if ( gSyscallHelpers->version >= 6 ) + gSyscallHelpers->abort_with_payload(reason_namespace, reason_code, payload, payload_size, reason_string, reason_flags); + dyld_fatal_error(reason_string); +} + +kern_return_t task_register_dyld_image_infos(task_t task, dyld_kernel_image_info_array_t dyld_images, mach_msg_type_number_t dyld_imagesCnt) { + if ( gSyscallHelpers->version >= 7 ) + return gSyscallHelpers->task_register_dyld_image_infos(task, dyld_images, dyld_imagesCnt); + return KERN_NOT_SUPPORTED; +} + +kern_return_t task_unregister_dyld_image_infos(task_t task, dyld_kernel_image_info_array_t dyld_images, mach_msg_type_number_t dyld_imagesCnt) { + if ( gSyscallHelpers->version >= 7 ) + return gSyscallHelpers->task_unregister_dyld_image_infos(task, dyld_images, dyld_imagesCnt); + return KERN_NOT_SUPPORTED; +} + +kern_return_t task_get_dyld_image_infos(task_t task, dyld_kernel_image_info_array_t *dyld_images, mach_msg_type_number_t *dyld_imagesCnt) { + if ( gSyscallHelpers->version >= 7 ) + return gSyscallHelpers->task_get_dyld_image_infos(task, dyld_images, dyld_imagesCnt); + return KERN_NOT_SUPPORTED; +} + +kern_return_t task_register_dyld_shared_cache_image_info(task_t task, dyld_kernel_image_info_t dyld_cache_image, boolean_t no_cache, boolean_t private_cache) { + if ( gSyscallHelpers->version >= 7 ) + return gSyscallHelpers->task_register_dyld_shared_cache_image_info(task, dyld_cache_image, no_cache, private_cache); + return KERN_NOT_SUPPORTED; +} + +kern_return_t task_register_dyld_set_dyld_state(task_t task, uint8_t dyld_state) { + if ( gSyscallHelpers->version >= 7 ) + return gSyscallHelpers->task_register_dyld_set_dyld_state(task, dyld_state); + return KERN_NOT_SUPPORTED; +} + +kern_return_t task_register_dyld_get_process_state(task_t task, dyld_kernel_process_info_t *dyld_process_state) { + if ( gSyscallHelpers->version >= 7 ) + return gSyscallHelpers->task_register_dyld_get_process_state(task, dyld_process_state); + return KERN_NOT_SUPPORTED; +} + +int* __error(void) { + return gSyscallHelpers->errnoAddress(); +} + +void mach_init() { + mach_task_self_ = task_self_trap(); + //_task_reply_port = _mach_reply_port(); +} + +mach_port_t mach_task_self_ = MACH_PORT_NULL; + +extern int myerrno_fallback __asm("_errno"); +int myerrno_fallback = 0; + +#endif // TARGET_IPHONE_SIMULATOR + + +#if ! TARGET_IPHONE_SIMULATOR + #include "mach-o/dyld_process_info.h" + + void _dyld_debugger_notification(enum dyld_notify_mode mode, unsigned long count, uint64_t machHeaders[]) + { + // Do nothing. This exists for the debugger to set a break point on to see what images have been loaded or unloaded. + } +#endif + + + + diff --git a/src/dyld-apple/src/libdyld_data_symbols.dirty b/src/dyld-apple/src/libdyld_data_symbols.dirty new file mode 100644 index 000000000..85d1a927c --- /dev/null +++ b/src/dyld-apple/src/libdyld_data_symbols.dirty @@ -0,0 +1,12 @@ +__ZL12sGlobalMutex +__ZZ26NSVersionOfLinkTimeLibraryE2mh +__ZZ33_dyld_register_func_for_add_imageE1p +__ZZ36_dyld_register_func_for_remove_imageE1p +__ZZ21_dyld_get_image_slideE1p +__ZZ6dlopenE1p +__ZZ40dyld_register_image_state_change_handlerE1p +__ZZ21_dyld_fast_stub_entryPvlE1p +__ZZ34dyld_image_path_containing_addressE1p +__ZZ20_NSGetExecutablePathE1p +_myDyldSection +_tlv_terminators_key diff --git a/src/dyld-apple/src/libdyld_sim.exp b/src/dyld-apple/src/libdyld_sim.exp new file mode 100644 index 000000000..be8a4e487 --- /dev/null +++ b/src/dyld-apple/src/libdyld_sim.exp @@ -0,0 +1,5 @@ +_dyld_get_sdk_version +_dyld_get_program_sdk_version +_dyld_get_min_os_version +_dyld_get_program_min_os_version + diff --git a/src/dyld-apple/src/start_glue.S b/src/dyld-apple/src/start_glue.S new file mode 100644 index 000000000..3336f66ed --- /dev/null +++ b/src/dyld-apple/src/start_glue.S @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2011 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@ + */ + + + +#ifdef __i386__ + + .align 2 + .globl _start + .private_extern _start +_start: + nop # backtraces of LC_MAIN binaries don't end in "start" +Lstart: + movl %eax,(%esp) # pass result from main() to exit() + call _exit + hlt + +#endif /* __i386__ */ + + +#if __x86_64__ + + .align 2 + .globl _start + .private_extern _start +_start: + nop # backtraces of LC_MAIN binaries don't end in "start" +Lstart: + movl %eax,%edi # pass result from main() to exit() + call _exit + hlt + +#endif + + +#if __arm__ + + .align 2 + .code 16 + .globl _start + .private_extern _start + .thumb_func _start +_start: + nop // backtraces of LC_MAIN binaries don't end in "start" +Lstart: + bl _exit // result in r0 already in param reg r0 + trap + +#endif /* __arm__ */ + + +#if __arm64__ + + .align 2 + .globl _start + .private_extern _start +_start: + nop +Lstart: + bl _exit // result in x0 already in param reg x0 + brk #3 + +#endif /* __arm64__ */ + + .subsections_via_symbols + diff --git a/src/dyld-apple/src/start_glue.h b/src/dyld-apple/src/start_glue.h new file mode 100644 index 000000000..57b03c1d0 --- /dev/null +++ b/src/dyld-apple/src/start_glue.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2013 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@ + */ + +#ifndef __START_GLUE_H__ +#define __START_GLUE_H__ + +// Implemented in start_glue.s +extern "C" void start(); + + +// need 'start' to be one atom, but entry is in interior + +#if __x86_64__ || __i386__ + #define address_of_start (void*)((uintptr_t)&start + 1) +#elif __arm64__ + #define address_of_start (void*)((uintptr_t)&start + 4) +#elif __arm__ + #define address_of_start (void*)((uintptr_t)&start + 2) +#endif + + + +#endif // __START_GLUE_H__ diff --git a/src/dyld-apple/src/stub_binding_helper.S b/src/dyld-apple/src/stub_binding_helper.S new file mode 100644 index 000000000..de2d90be5 --- /dev/null +++ b/src/dyld-apple/src/stub_binding_helper.S @@ -0,0 +1,226 @@ +/* + * Copyright (c) 1999-2006 Apple Computer, 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@ + */ + + + +#ifdef __i386__ +/* + * This is the interface for the stub_binding_helper for i386: + * The caller has pushed the address of the a lazy pointer to be filled in + * and pushed the address of the the mach header this pointer comes from. + * + * sp+4 address of lazy pointer + * sp+0 address of mach header + * + * Some inter-image function calls pass parameters in registers EAX, ECX, EDX, or XXM0-3, + * Therefore those registers need to be preserved during the lazy binding. + * + * After the symbol has been resolved and the lazy pointer filled in, this jumps + * to the target address. + */ +#define MH_PARAM_OUT 0 +#define LP_PARAM_OUT 4 +#define XMMM0_SAVE 16 /* 16-byte align */ +#define XMMM1_SAVE 32 +#define XMMM2_SAVE 48 +#define XMMM3_SAVE 64 +#define EAX_SAVE 84 +#define ECX_SAVE 88 +#define EDX_SAVE 92 +#define LP_LOCAL 96 +#define MH_LOCAL 100 +#define STACK_SIZE 100 /* must be 4 mod 16 so that stack winds up 16-byte aliged */ +#define LP_OLD_BP_SAVE 104 + + .text + .align 4,0x90 + .globl _stub_binding_helper_i386_old +_stub_binding_helper_i386_old: + pushl $0 + .globl _stub_binding_helper + .globl _misaligned_stack_error +_stub_binding_helper: + subl $STACK_SIZE,%esp # makes stack 16-byte aligned + movl %eax,EAX_SAVE(%esp) + movl LP_OLD_BP_SAVE(%esp),%eax # get lazy-pointer meta-parameter + movl %eax,LP_LOCAL(%esp) + movl %ebp,LP_OLD_BP_SAVE(%esp) # store epb back chain + movl %esp,%ebp # set epb to be this frame + add $LP_OLD_BP_SAVE,%ebp + movl %ecx,ECX_SAVE(%esp) + movl %edx,EDX_SAVE(%esp) + .align 0,0x90 +_misaligned_stack_error: + movdqa %xmm0,XMMM0_SAVE(%esp) + movdqa %xmm1,XMMM1_SAVE(%esp) + movdqa %xmm2,XMMM2_SAVE(%esp) + movdqa %xmm3,XMMM3_SAVE(%esp) +_stub_binding_helper_interface2: + movl MH_LOCAL(%esp),%eax # call dyld::bindLazySymbol(mh, lazy_ptr) + movl %eax,MH_PARAM_OUT(%esp) + movl LP_LOCAL(%esp),%eax + movl %eax,LP_PARAM_OUT(%esp) + call __ZN4dyld14bindLazySymbolEPK11mach_headerPm + movdqa XMMM0_SAVE(%esp),%xmm0 # restore registers + movdqa XMMM1_SAVE(%esp),%xmm1 + movdqa XMMM2_SAVE(%esp),%xmm2 + movdqa XMMM3_SAVE(%esp),%xmm3 + movl ECX_SAVE(%esp),%ecx + movl EDX_SAVE(%esp),%edx + movl %eax,%ebp # move target address to epb + movl EAX_SAVE(%esp),%eax # restore eaz + addl $STACK_SIZE+4,%esp # cut back stack + xchg %ebp, (%esp) # restore ebp and set target to top of stack + ret # jump to target + +#endif /* __i386__ */ + + +#if __x86_64__ +/* + * This is the interface for the stub_binding_helper for x86_64: + * The caller has pushed the address of the a lazy pointer to be filled in with + * the value for the defined symbol and pushed the address of the the mach + * header this pointer comes from. + * + * sp+8 address of lazy pointer + * sp+0 address of mach header + * + * All parameters registers must be preserved. + * + * After the symbol has been resolved and the pointer filled in this is to pop + * these arguments off the stack and jump to the address of the defined symbol. + */ +#define MH_PARAM_BP 8 +#define LP_PARAM_BP 16 + +#define RDI_SAVE 0 +#define RSI_SAVE 8 +#define RDX_SAVE 16 +#define RCX_SAVE 24 +#define R8_SAVE 32 +#define R9_SAVE 40 +#define RAX_SAVE 48 +#define XMMM0_SAVE 64 /* 16-byte align */ +#define XMMM1_SAVE 80 +#define XMMM2_SAVE 96 +#define XMMM3_SAVE 112 +#define XMMM4_SAVE 128 +#define XMMM5_SAVE 144 +#define XMMM6_SAVE 160 +#define XMMM7_SAVE 176 +#define STACK_SIZE 192 /* (XMMM7_SAVE+16) must be 16 byte aligned too */ + + .text + .align 2,0x90 + .globl _stub_binding_helper +_stub_binding_helper: + pushq %rbp + movq %rsp,%rbp + subq $STACK_SIZE,%rsp # at this point stack is 16-byte aligned because two meta-parameters where pushed + movq %rdi,RDI_SAVE(%rsp) # save registers that might be used as parameters + movq %rsi,RSI_SAVE(%rsp) + movq %rdx,RDX_SAVE(%rsp) + movq %rcx,RCX_SAVE(%rsp) + movq %r8,R8_SAVE(%rsp) + movq %r9,R9_SAVE(%rsp) + movq %rax,RAX_SAVE(%rsp) + movdqa %xmm0,XMMM0_SAVE(%rsp) + movdqa %xmm1,XMMM1_SAVE(%rsp) + movdqa %xmm2,XMMM2_SAVE(%rsp) + movdqa %xmm3,XMMM3_SAVE(%rsp) + movdqa %xmm4,XMMM4_SAVE(%rsp) + movdqa %xmm5,XMMM5_SAVE(%rsp) + movdqa %xmm6,XMMM6_SAVE(%rsp) + movdqa %xmm7,XMMM7_SAVE(%rsp) + movq MH_PARAM_BP(%rbp),%rdi # call dyld::bindLazySymbol(mh, lazy_ptr) + movq LP_PARAM_BP(%rbp),%rsi + call __ZN4dyld14bindLazySymbolEPK11mach_headerPm + movq %rax,%r11 # save target + movdqa XMMM0_SAVE(%rsp),%xmm0 # restore registers + movdqa XMMM1_SAVE(%rsp),%xmm1 + movdqa XMMM2_SAVE(%rsp),%xmm2 + movdqa XMMM3_SAVE(%rsp),%xmm3 + movdqa XMMM4_SAVE(%rsp),%xmm4 + movdqa XMMM5_SAVE(%rsp),%xmm5 + movdqa XMMM6_SAVE(%rsp),%xmm6 + movdqa XMMM7_SAVE(%rsp),%xmm7 + movq RDI_SAVE(%rsp),%rdi + movq RSI_SAVE(%rsp),%rsi + movq RDX_SAVE(%rsp),%rdx + movq RCX_SAVE(%rsp),%rcx + movq R8_SAVE(%rsp),%r8 + movq R9_SAVE(%rsp),%r9 + movq RAX_SAVE(%rsp),%rax + addq $STACK_SIZE,%rsp + popq %rbp + addq $16,%rsp # remove meta-parameters + jmp *%r11 # jmp to target + +#endif + + +#if __arm__ && !__ARM_ARCH_7K__ +/* + * This is the interface for the old stub_binding_helper for ARM: + * The caller has pushed the address of the a lazy pointer to be filled in with + * the value for the defined symbol and pushed the address of the the mach + * header this pointer comes from. + * + * sp+4 address of lazy pointer + * sp+0 address of mach header + * + * After the symbol has been resolved and the pointer filled in this is to pop + * these arguments off the stack and jump to the address of the defined symbol. + */ + + .text + .align 2 + .globl _stub_binding_helper +_stub_binding_helper: + stmfd sp!, {r0,r1,r2,r3,r7,lr} // save registers + add r7, sp, #16 // point FP to previous FP + + ldr r0, [sp, #24] // move address of mach header to 1st parameter + ldr r1, [sp, #28] // move address of lazy pointer to 2nd parameter + + // call dyld::bindLazySymbol(mh, lazy_symbol_pointer_address) + bl __ZN4dyld14bindLazySymbolEPK11mach_headerPm + mov ip, r0 // move the symbol`s address into ip + + ldmfd sp!, {r0,r1,r2,r3,r7,lr} // restore registers + add sp, sp, #8 // remove meta-parameters + + bx ip // jump to the symbol`s address that was bound + +#endif /* __arm__ */ + + + + + + + + + + diff --git a/src/dyld-apple/src/threadLocalHelpers.S b/src/dyld-apple/src/threadLocalHelpers.S new file mode 100644 index 000000000..fb63817ec --- /dev/null +++ b/src/dyld-apple/src/threadLocalHelpers.S @@ -0,0 +1,316 @@ +/* + * Copyright (c) 2010-2013 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@ + */ + +#include + +// bool save_xxm = (*((uint32_t*)_COMM_PAGE_CPU_CAPABILITIES) & kHasAVX1_0) != 0; + +#if __x86_64__ + +#define RDI_SAVE_RBP -8 +#define RSI_SAVE_RBP -16 +#define RDX_SAVE_RBP -24 +#define RCX_SAVE_RBP -32 +#define RBX_SAVE_RBP -40 +#define R8_SAVE_RBP -48 +#define R9_SAVE_RBP -56 +#define R10_SAVE_RBP -64 +#define R11_SAVE_RBP -72 +#define STATIC_STACK_SIZE 256 // extra padding to allow it to be 64-byte aligned + +#define XMM0_SAVE_RSP 0x00 +#define XMM1_SAVE_RSP 0x10 +#define XMM2_SAVE_RSP 0x20 +#define XMM3_SAVE_RSP 0x30 +#define XMM4_SAVE_RSP 0x40 +#define XMM5_SAVE_RSP 0x50 +#define XMM6_SAVE_RSP 0x60 +#define XMM7_SAVE_RSP 0x70 + + + // returns address of TLV in %rax, all other registers preserved + .globl _tlv_get_addr + .private_extern _tlv_get_addr +_tlv_get_addr: + movq 8(%rdi),%rax // get key from descriptor + movq %gs:0x0(,%rax,8),%rax // get thread value + testq %rax,%rax // if NULL, lazily allocate + je LlazyAllocate + addq 16(%rdi),%rax // add offset from descriptor + ret +LlazyAllocate: + pushq %rbp + movq %rsp,%rbp + subq $STATIC_STACK_SIZE,%rsp + movq %rdi,RDI_SAVE_RBP(%rbp) # save registers that might be used as parameters + movq %rsi,RSI_SAVE_RBP(%rbp) + movq %rdx,RDX_SAVE_RBP(%rbp) + movq %rcx,RCX_SAVE_RBP(%rbp) + movq %rbx,RBX_SAVE_RBP(%rbp) + movq %r8, R8_SAVE_RBP(%rbp) + movq %r9, R9_SAVE_RBP(%rbp) + movq %r10,R10_SAVE_RBP(%rbp) + movq %r11,R11_SAVE_RBP(%rbp) + + cmpl $0, _inited(%rip) + jne Linited + movl $0x01,%eax + cpuid # get cpu features to check on xsave instruction support + andl $0x08000000,%ecx # check OSXSAVE bit + movl %ecx,_hasXSave(%rip) + cmpl $0, %ecx + jne LxsaveInfo + movl $1, _inited(%rip) + jmp Lsse + +LxsaveInfo: + movl $0x0D,%eax + movl $0x00,%ecx + cpuid # get xsave parameter info + movl %eax,_features_lo32(%rip) + movl %edx,_features_hi32(%rip) + movl %ecx,_bufferSize32(%rip) + movl $1, _inited(%rip) + +Linited: + cmpl $0, _hasXSave(%rip) + jne Lxsave + +Lsse: + subq $128, %rsp + movdqa %xmm0, XMM0_SAVE_RSP(%rsp) + movdqa %xmm1, XMM1_SAVE_RSP(%rsp) + movdqa %xmm2, XMM2_SAVE_RSP(%rsp) + movdqa %xmm3, XMM3_SAVE_RSP(%rsp) + movdqa %xmm4, XMM4_SAVE_RSP(%rsp) + movdqa %xmm5, XMM5_SAVE_RSP(%rsp) + movdqa %xmm6, XMM6_SAVE_RSP(%rsp) + movdqa %xmm7, XMM7_SAVE_RSP(%rsp) + jmp Lalloc + +Lxsave: + movl _bufferSize32(%rip),%eax + movq %rsp, %rdi + subq %rax, %rdi # stack alloc buffer + andq $-64, %rdi # 64-byte align stack + movq %rdi, %rsp + # xsave requires buffer to be zero'ed out + movq $0, %rcx + movq %rdi, %r8 + movq %rdi, %r9 + addq %rax, %r9 +Lz: movq %rcx, (%r8) + addq $8, %r8 + cmpq %r8,%r9 + ja Lz + + movl _features_lo32(%rip),%eax + movl _features_hi32(%rip),%edx + # call xsave with buffer on stack and eax:edx flag bits + # note: do not use xsaveopt, it assumes you are using the same + # buffer as previous xsaves, and this thread is on the same cpu. + xsave (%rsp) + +Lalloc: + movq RDI_SAVE_RBP(%rbp),%rdi + movq 8(%rdi),%rdi // get key from descriptor + call _tlv_allocate_and_initialize_for_key + + cmpl $0, _hasXSave(%rip) + jne Lxrstror + + movdqa XMM0_SAVE_RSP(%rsp),%xmm0 + movdqa XMM1_SAVE_RSP(%rsp),%xmm1 + movdqa XMM2_SAVE_RSP(%rsp),%xmm2 + movdqa XMM3_SAVE_RSP(%rsp),%xmm3 + movdqa XMM4_SAVE_RSP(%rsp),%xmm4 + movdqa XMM5_SAVE_RSP(%rsp),%xmm5 + movdqa XMM6_SAVE_RSP(%rsp),%xmm6 + movdqa XMM7_SAVE_RSP(%rsp),%xmm7 + jmp Ldone + +Lxrstror: + movq %rax,%r11 + movl _features_lo32(%rip),%eax + movl _features_hi32(%rip),%edx + # call xsave with buffer on stack and eax:edx flag bits + xrstor (%rsp) + movq %r11,%rax + +Ldone: + movq RDI_SAVE_RBP(%rbp),%rdi + movq RSI_SAVE_RBP(%rbp),%rsi + movq RDX_SAVE_RBP(%rbp),%rdx + movq RCX_SAVE_RBP(%rbp),%rcx + movq RBX_SAVE_RBP(%rbp),%rbx + movq R8_SAVE_RBP(%rbp),%r8 + movq R9_SAVE_RBP(%rbp),%r9 + movq R10_SAVE_RBP(%rbp),%r10 + movq R11_SAVE_RBP(%rbp),%r11 + movq %rbp,%rsp + popq %rbp + addq 16(%rdi),%rax // result = buffer + offset + ret + + .data +# Cached info from cpuid. +_inited: .long 0 +_features_lo32: .long 0 +_features_hi32: .long 0 +_bufferSize32: .long 0 +_hasXSave: .long 0 + +#endif + + + +#if __i386__ + // returns address of TLV in %eax, all other registers (except %ecx) preserved + .globl _tlv_get_addr + .private_extern _tlv_get_addr +_tlv_get_addr: + movl 4(%eax),%ecx // get key from descriptor + movl %gs:0x0(,%ecx,4),%ecx // get thread value + testl %ecx,%ecx // if NULL, lazily allocate + je LlazyAllocate + movl 8(%eax),%eax // add offset from descriptor + addl %ecx,%eax + ret +LlazyAllocate: + pushl %ebp + movl %esp,%ebp + pushl %edx // save edx + subl $548,%esp + movl %eax,-8(%ebp) // save descriptor + lea -528(%ebp),%ecx // get 512 byte buffer in frame + and $-16, %ecx // 16-byte align buffer for fxsave + fxsave (%ecx) + movl 4(%eax),%ecx // get key from descriptor + movl %ecx,(%esp) // push key parameter, also leaves stack aligned properly + call _tlv_allocate_and_initialize_for_key + movl -8(%ebp),%ecx // get descriptor + movl 8(%ecx),%ecx // get offset from descriptor + addl %ecx,%eax // add offset to buffer + lea -528(%ebp),%ecx + and $-16, %ecx // 16-byte align buffer for fxrstor + fxrstor (%ecx) + addl $548,%esp + popl %edx // restore edx + popl %ebp + ret +#endif + +#if __arm64__ + // Parameters: X0 = descriptor + // Result: X0 = address of TLV + // Note: all registers except X0, x16, and x17 are preserved + .align 2 + .globl _tlv_get_addr + .private_extern _tlv_get_addr +_tlv_get_addr: + ldr x16, [x0, #8] // get key from descriptor + mrs x17, TPIDRRO_EL0 + and x17, x17, #-8 // clear low 3 bits??? + ldr x17, [x17, x16, lsl #3] // get thread allocation address for this key + cbz x17, LlazyAllocate // if NULL, lazily allocate + ldr x16, [x0, #16] // get offset from descriptor + add x0, x17, x16 // return allocation+offset + ret lr + +LlazyAllocate: + stp fp, lr, [sp, #-16]! + mov fp, sp + sub sp, sp, #288 + stp x1, x2, [sp, #-16]! // save all registers that C function might trash + stp x3, x4, [sp, #-16]! + stp x5, x6, [sp, #-16]! + stp x7, x8, [sp, #-16]! + stp x9, x10, [sp, #-16]! + stp x11, x12, [sp, #-16]! + stp x13, x14, [sp, #-16]! + stp x15, x16, [sp, #-16]! + stp q0, q1, [sp, #-32]! + stp q2, q3, [sp, #-32]! + stp q4, q5, [sp, #-32]! + stp q6, q7, [sp, #-32]! + stp x0, x17, [sp, #-16]! // save descriptor + + mov x0, x16 // use key from descriptor as parameter + bl _tlv_allocate_and_initialize_for_key + ldp x16, x17, [sp], #16 // pop descriptor + ldr x16, [x16, #16] // get offset from descriptor + add x0, x0, x16 // return allocation+offset + + ldp q6, q7, [sp], #32 + ldp q4, q5, [sp], #32 + ldp q2, q3, [sp], #32 + ldp q0, q1, [sp], #32 + ldp x15, x16, [sp], #16 + ldp x13, x14, [sp], #16 + ldp x11, x12, [sp], #16 + ldp x9, x10, [sp], #16 + ldp x7, x8, [sp], #16 + ldp x5, x6, [sp], #16 + ldp x3, x4, [sp], #16 + ldp x1, x2, [sp], #16 + + mov sp, fp + ldp fp, lr, [sp], #16 + ret lr + +#endif + +#if __arm__ + // returns address of TLV in r0, all other registers preserved + .align 2 + .globl _tlv_get_addr + .private_extern _tlv_get_addr +_tlv_get_addr: + push {r1,r2,r3,r7,lr} +#if __ARM_ARCH_7K__ + sub sp, sp, #12 // align stack to 16 bytes +#endif + mov r7, r0 // save descriptor in r7 + ldr r0, [r7, #4] // get key from descriptor + bl _pthread_getspecific // get thread value + cmp r0, #0 + bne L2 // if NULL, lazily allocate +#if __ARM_ARCH_7K__ + vpush {d0, d1, d2, d3, d4, d5, d6, d7} +#endif + ldr r0, [r7, #4] // get key from descriptor + bl _tlv_allocate_and_initialize_for_key +#if __ARM_ARCH_7K__ + vpop {d0, d1, d2, d3, d4, d5, d6, d7} +#endif +L2: ldr r1, [r7, #8] // get offset from descriptor + add r0, r1, r0 // add offset into allocation block +#if __ARM_ARCH_7K__ + add sp, sp, #12 +#endif + pop {r1,r2,r3,r7,pc} +#endif + + .subsections_via_symbols + + diff --git a/src/dyld-apple/src/threadLocalVariables.c b/src/dyld-apple/src/threadLocalVariables.c new file mode 100644 index 000000000..81322259b --- /dev/null +++ b/src/dyld-apple/src/threadLocalVariables.c @@ -0,0 +1,494 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2010 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@ + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dyld_priv.h" + + +#if __LP64__ + typedef struct mach_header_64 macho_header; + #define LC_SEGMENT_COMMAND LC_SEGMENT_64 + typedef struct segment_command_64 macho_segment_command; + typedef struct section_64 macho_section; +#else + typedef struct mach_header macho_header; + #define LC_SEGMENT_COMMAND LC_SEGMENT + typedef struct segment_command macho_segment_command; + typedef struct section macho_section; +#endif + +#ifndef S_THREAD_LOCAL_REGULAR +#define S_THREAD_LOCAL_REGULAR 0x11 +#endif + +#ifndef S_THREAD_LOCAL_ZEROFILL +#define S_THREAD_LOCAL_ZEROFILL 0x12 +#endif + +#ifndef S_THREAD_LOCAL_VARIABLES +#define S_THREAD_LOCAL_VARIABLES 0x13 +#endif + +#ifndef S_THREAD_LOCAL_VARIABLE_POINTERS +#define S_THREAD_LOCAL_VARIABLE_POINTERS 0x14 +#endif + +#ifndef S_THREAD_LOCAL_INIT_FUNCTION_POINTERS +#define S_THREAD_LOCAL_INIT_FUNCTION_POINTERS 0x15 +#endif + +#ifndef MH_HAS_TLV_DESCRIPTORS + #define MH_HAS_TLV_DESCRIPTORS 0x800000 +#endif + + +typedef void (*TermFunc)(void*); + + + +#if __has_feature(tls) || __arm64__ || __arm__ + +typedef struct TLVHandler { + struct TLVHandler *next; + dyld_tlv_state_change_handler handler; + enum dyld_tlv_states state; +} TLVHandler; + +// lock-free prepend-only linked list +static TLVHandler * volatile tlv_handlers = NULL; + + +struct TLVDescriptor +{ + void* (*thunk)(struct TLVDescriptor*); + unsigned long key; + unsigned long offset; +}; +typedef struct TLVDescriptor TLVDescriptor; + + +// implemented in assembly +extern void* tlv_get_addr(TLVDescriptor*); + +struct TLVImageInfo +{ + pthread_key_t key; + const struct mach_header* mh; +}; +typedef struct TLVImageInfo TLVImageInfo; + +static TLVImageInfo* tlv_live_images = NULL; +static unsigned int tlv_live_image_alloc_count = 0; +static unsigned int tlv_live_image_used_count = 0; +static pthread_mutex_t tlv_live_image_lock = PTHREAD_MUTEX_INITIALIZER; + +static void tlv_set_key_for_image(const struct mach_header* mh, pthread_key_t key) +{ + pthread_mutex_lock(&tlv_live_image_lock); + if ( tlv_live_image_used_count == tlv_live_image_alloc_count ) { + unsigned int newCount = (tlv_live_images == NULL) ? 8 : 2*tlv_live_image_alloc_count; + struct TLVImageInfo* newBuffer = malloc(sizeof(TLVImageInfo)*newCount); + if ( tlv_live_images != NULL ) { + memcpy(newBuffer, tlv_live_images, sizeof(TLVImageInfo)*tlv_live_image_used_count); + free(tlv_live_images); + } + tlv_live_images = newBuffer; + tlv_live_image_alloc_count = newCount; + } + tlv_live_images[tlv_live_image_used_count].key = key; + tlv_live_images[tlv_live_image_used_count].mh = mh; + ++tlv_live_image_used_count; + pthread_mutex_unlock(&tlv_live_image_lock); +} + +static const struct mach_header* tlv_get_image_for_key(pthread_key_t key) +{ + const struct mach_header* result = NULL; + pthread_mutex_lock(&tlv_live_image_lock); + for(unsigned int i=0; i < tlv_live_image_used_count; ++i) { + if ( tlv_live_images[i].key == key ) { + result = tlv_live_images[i].mh; + break; + } + } + pthread_mutex_unlock(&tlv_live_image_lock); + return result; +} + + +static void +tlv_notify(enum dyld_tlv_states state, void *buffer) +{ + if (!tlv_handlers) return; + + // Always use malloc_size() to ensure allocated and deallocated states + // send the same size. tlv_free() doesn't have anything else recorded. + dyld_tlv_info info = { sizeof(info), buffer, malloc_size(buffer) }; + + for (TLVHandler *h = tlv_handlers; h != NULL; h = h->next) { + if (h->state == state && h->handler) { + h->handler(h->state, &info); + } + } +} + + +// called lazily when TLV is first accessed +__attribute__((visibility("hidden"))) +void* tlv_allocate_and_initialize_for_key(pthread_key_t key) +{ + const struct mach_header* mh = tlv_get_image_for_key(key); + if ( mh == NULL ) + return NULL; // if data structures are screwed up, don't crash + + // first pass, find size and template + uint8_t* start = NULL; + unsigned long size = 0; + intptr_t slide = 0; + bool slideComputed = false; + bool hasInitializers = false; + const uint32_t cmd_count = mh->ncmds; + const struct load_command* const cmds = (struct load_command*)(((uint8_t*)mh) + sizeof(macho_header)); + const struct load_command* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + if ( cmd->cmd == LC_SEGMENT_COMMAND) { + const macho_segment_command* seg = (macho_segment_command*)cmd; + if ( !slideComputed && (seg->filesize != 0) ) { + slide = (uintptr_t)mh - seg->vmaddr; + slideComputed = true; + } + const macho_section* const sectionsStart = (macho_section*)((char*)seg + sizeof(macho_segment_command)); + const macho_section* const sectionsEnd = §ionsStart[seg->nsects]; + for (const macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) { + switch ( sect->flags & SECTION_TYPE ) { + case S_THREAD_LOCAL_INIT_FUNCTION_POINTERS: + hasInitializers = true; + break; + case S_THREAD_LOCAL_ZEROFILL: + case S_THREAD_LOCAL_REGULAR: + if ( start == NULL ) { + // first of N contiguous TLV template sections, record as if this was only section + start = (uint8_t*)(sect->addr + slide); + size = sect->size; + } + else { + // non-first of N contiguous TLV template sections, accumlate values + const uint8_t* newEnd = (uint8_t*)(sect->addr + slide + sect->size); + size = newEnd - start; + } + break; + } + } + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + + // allocate buffer and fill with template + void* buffer = malloc(size); + memcpy(buffer, start, size); + + // set this thread's value for key to be the new buffer. + pthread_setspecific(key, buffer); + + // send tlv state notifications + tlv_notify(dyld_tlv_state_allocated, buffer); + + // second pass, run initializers + if ( hasInitializers ) { + cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + if ( cmd->cmd == LC_SEGMENT_COMMAND) { + const macho_segment_command* seg = (macho_segment_command*)cmd; + const macho_section* const sectionsStart = (macho_section*)((char*)seg + sizeof(macho_segment_command)); + const macho_section* const sectionsEnd = §ionsStart[seg->nsects]; + for (const macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) { + if ( (sect->flags & SECTION_TYPE) == S_THREAD_LOCAL_INIT_FUNCTION_POINTERS ) { + typedef void (*InitFunc)(void); + InitFunc* funcs = (InitFunc*)(sect->addr + slide); + const size_t count = sect->size / sizeof(uintptr_t); + for (size_t j=count; j > 0; --j) { + InitFunc func = funcs[j-1]; + func(); + } + } + } + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + } + return buffer; +} + + +// pthread destructor for TLV storage +static void +tlv_free(void *storage) +{ + tlv_notify(dyld_tlv_state_deallocated, storage); + free(storage); +} + + +// called when image is loaded +static void tlv_initialize_descriptors(const struct mach_header* mh) +{ + pthread_key_t key = 0; + intptr_t slide = 0; + bool slideComputed = false; + const uint32_t cmd_count = mh->ncmds; + const struct load_command* const cmds = (struct load_command*)(((uint8_t*)mh) + sizeof(macho_header)); + const struct load_command* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + if ( cmd->cmd == LC_SEGMENT_COMMAND) { + const macho_segment_command* seg = (macho_segment_command*)cmd; + if ( !slideComputed && (seg->filesize != 0) ) { + slide = (uintptr_t)mh - seg->vmaddr; + slideComputed = true; + } + const macho_section* const sectionsStart = (macho_section*)((char*)seg + sizeof(macho_segment_command)); + const macho_section* const sectionsEnd = §ionsStart[seg->nsects]; + for (const macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) { + if ( (sect->flags & SECTION_TYPE) == S_THREAD_LOCAL_VARIABLES ) { + if ( sect->size != 0 ) { + // allocate pthread key when we first discover this image has TLVs + if ( key == 0 ) { + int result = pthread_key_create(&key, &tlv_free); + if ( result != 0 ) + abort(); + tlv_set_key_for_image(mh, key); + } + // initialize each descriptor + TLVDescriptor* start = (TLVDescriptor*)(sect->addr + slide); + TLVDescriptor* end = (TLVDescriptor*)(sect->addr + sect->size + slide); + for (TLVDescriptor* d=start; d < end; ++d) { + d->thunk = tlv_get_addr; + d->key = key; + //d->offset = d->offset; // offset unchanged + } + } + } + } + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } +} + + +void tlv_load_notification(const struct mach_header* mh, intptr_t slide) +{ + // This is called on all images, even those without TLVs. So we want this to be fast. + // The linker sets MH_HAS_TLV_DESCRIPTORS so we don't have to search images just to find the don't have TLVs. + if ( mh->flags & MH_HAS_TLV_DESCRIPTORS ) + tlv_initialize_descriptors(mh); +} + + +void dyld_register_tlv_state_change_handler(enum dyld_tlv_states state, dyld_tlv_state_change_handler handler) +{ + TLVHandler *h = malloc(sizeof(TLVHandler)); + h->state = state; + h->handler = Block_copy(handler); + + TLVHandler *old; + do { + old = tlv_handlers; + h->next = old; + } while (! OSAtomicCompareAndSwapPtrBarrier(old, h, (void * volatile *)&tlv_handlers)); +} + + +void dyld_enumerate_tlv_storage(dyld_tlv_state_change_handler handler) +{ + pthread_mutex_lock(&tlv_live_image_lock); + unsigned int count = tlv_live_image_used_count; + void *list[count]; + for (unsigned int i = 0; i < count; ++i) { + list[i] = pthread_getspecific(tlv_live_images[i].key); + } + pthread_mutex_unlock(&tlv_live_image_lock); + + for (unsigned int i = 0; i < count; ++i) { + if (list[i]) { + dyld_tlv_info info = { sizeof(info), list[i], malloc_size(list[i]) }; + handler(dyld_tlv_state_allocated, &info); + } + } +} + + +// +// thread_local terminators +// +// C++ 0x allows thread_local C++ objects which have constructors run +// on the thread before any use of the object and the object's destructor +// is run on the thread when the thread terminates. +// +// To support this libdyld gets a pthread key early in process start up and +// uses tlv_finalize and the key's destructor function. This key must be +// allocated before any thread local variables are instantiated because when +// a thread is terminated, the pthread package runs the destructor function +// on each key's storage values in key allocation order. Since we want +// C++ objects to be destructred before they are deallocated, we need the +// destructor key to come before the deallocation key. +// + +struct TLVTerminatorListEntry +{ + TermFunc termFunc; + void* objAddr; +}; + +struct TLVTerminatorList +{ + uint32_t allocCount; + uint32_t useCount; + struct TLVTerminatorListEntry entries[1]; // variable length +}; + + +static pthread_key_t tlv_terminators_key = 0; + +void _tlv_atexit(TermFunc func, void* objAddr) +{ + // NOTE: this does not need locks because it only operates on current thread data + struct TLVTerminatorList* list = (struct TLVTerminatorList*)pthread_getspecific(tlv_terminators_key); + if ( list == NULL ) { + // handle first allocation + list = (struct TLVTerminatorList*)malloc(offsetof(struct TLVTerminatorList, entries[1])); + list->allocCount = 1; + list->useCount = 1; + list->entries[0].termFunc = func; + list->entries[0].objAddr = objAddr; + pthread_setspecific(tlv_terminators_key, list); + } + else { + if ( list->allocCount == list->allocCount ) { + // handle resizing allocation + uint32_t newAllocCount = list->allocCount * 2; + size_t newAllocSize = offsetof(struct TLVTerminatorList, entries[newAllocCount]); + struct TLVTerminatorList* newlist = (struct TLVTerminatorList*)malloc(newAllocSize); + newlist->allocCount = newAllocCount; + newlist->useCount = list->useCount; + for(uint32_t i=0; i < list->useCount; ++i) + newlist->entries[i] = list->entries[i]; + pthread_setspecific(tlv_terminators_key, newlist); + free(list); + list = newlist; + } + // handle appending new entry + list->entries[list->useCount].termFunc = func; + list->entries[list->useCount].objAddr = objAddr; + list->useCount += 1; + } +} + +// called by pthreads when the current thread is going away and +// _tlv_atexit() has been called on the thread. +static void tlv_finalize(void* storage) +{ + struct TLVTerminatorList* list = (struct TLVTerminatorList*)storage; + // destroy in reverse order of construction + for(uint32_t i=list->useCount; i > 0 ; --i) { + struct TLVTerminatorListEntry* entry = &list->entries[i-1]; + if ( entry->termFunc != NULL ) { + (*entry->termFunc)(entry->objAddr); + } + } + free(storage); +} + +// +// called by exit() before it calls cxa_finalize() so that thread_local +// objects are destroyed before global objects. +void _tlv_exit() +{ + void* termFuncs = pthread_getspecific(tlv_terminators_key); + if ( termFuncs != NULL ) + tlv_finalize(termFuncs); +} + + +__attribute__((visibility("hidden"))) +void tlv_initializer() +{ + // create pthread key to handle thread_local destructors + // NOTE: this key must be allocated before any keys for TLV + // so that _pthread_tsd_cleanup will run destructors before deallocation + (void)pthread_key_create(&tlv_terminators_key, &tlv_finalize); + + // register with dyld for notification when images are loaded + _dyld_register_func_for_add_image(tlv_load_notification); + +} + + +// linked images with TLV have references to this symbol, but it is never used at runtime +void _tlv_bootstrap() +{ + abort(); +} + + + +#else + + + +void dyld_register_tlv_state_change_handler(enum dyld_tlv_states state, dyld_tlv_state_change_handler handler) +{ +} + +void dyld_enumerate_tlv_storage(dyld_tlv_state_change_handler handler) +{ +} + +void _tlv_exit() +{ +} + +void _tlv_atexit(TermFunc func, void* objAddr) +{ +} + +__attribute__((visibility("hidden"))) +void tlv_initializer() +{ +} + + + +#endif // __has_feature(tls) + + diff --git a/src/dyld-apple/testing/README.txt b/src/dyld-apple/testing/README.txt new file mode 100755 index 000000000..f94a6f6a6 --- /dev/null +++ b/src/dyld-apple/testing/README.txt @@ -0,0 +1,47 @@ + +When the dyld_tests target is built, all test cases are built into /AppleInternal/. +A test case is a directory in $SRCROOT/testing/test-cases/ whose name ends in ".dtest". +The build system scraps any .c or .cxx files in the .dtest directory looking for BUILD: or RUN: lines. +The BUILD: lines are use to build the test case binaries. +The RUN: lines are used to build the information needed for BATS to run the test cases. +Example, main.c may contain: + + // BUILD: $CC main.c -o $BUILD_DIR/example.exe + // RUN: ./example.exe + int main() { return 0; } + +When build lines are executed, the current directory is set to the test case's .dtest dir. +Build lines may contain the follow variables: + $BUILD_DIR - expands to the directory in $DSTROOT where this test case binaries are installed + $RUN_DIR - expands to the directory in /AppleInternal/ where this test case binaries will be run + $TEMP_DIR - expands to a temporary directory that will be delete after this test case is built + $CC - expands to the C compiler command line for the current platform. It includes + the min-os-version option, then SDK option, and the architectures. + $CXX - expands to the C++ compiler plus standard options + +When run lines are executed, the current directory is set to what $RUN_DIR was during build. +Run lines may contain the follow variables: + $RUN_DIR - expands to the directory in /AppleInternal/ where this test case binaries are installed + $REQUIRE_CRASH - expands to the 'nocr' tool which is used when a program is expected to "crash" in order to pass + + +When a test program runs, it should initially print out the name of the test case. Ex: + [BEGIN] dlfoo-thread-safe +Then it should print a pass or fail message with the same test name. Ex: + [PASS] dlfoo-thread-safe + + +To support tests that dyld is supposed to terminate, use $REQUIRE_CRASH on the RUN: line +along with setting env var NOCR_TEST_NAME to the name of the test case. When that env +var is set, the nocr tool wil print out the [BEGIN], then [PASS] if the test crashes +otherwise [FAIL]. Ex: + // RUN: NOCR_TEST_NAME="dylib-static-link missing" $REQUIRE_CRASH ./dylib-static-missing.exe + + +To support tests that are platform specific, add the BUILD_ONLY: line which specifies the platform. +Valid platforms are: MacOSX, iOS, watchOS, and tvOS. When a specific platform is specified, a +new min OS version can also be specified via the BUILD_MIN_OS option. For instance: + // BUILD_ONLY: MacOSX + // BUILD_MIN_OS: 10.5 + + diff --git a/src/dyld-apple/testing/build_tests.py b/src/dyld-apple/testing/build_tests.py new file mode 100755 index 000000000..fd7874a11 --- /dev/null +++ b/src/dyld-apple/testing/build_tests.py @@ -0,0 +1,199 @@ +#!/usr/bin/python2.7 + +import plistlib +import string +import argparse +import sys +import os +import tempfile +import shutil +import subprocess + + +# +# Scan files in .dtest directory looking for BUILD: or RUN: directives. +# Return a dictionary of all directives. +# +def parseDirectives(testCaseSourceDir): + onlyLines = [] + buildLines = [] + runLines = [] + minOS = "" + timeout = "" + for file in os.listdir(testCaseSourceDir): + if file.endswith((".c", ".cpp", ".cxx")): + with open(testCaseSourceDir + "/" + file) as f: + for line in f.read().splitlines(): + buildIndex = string.find(line, "BUILD:") + if buildIndex != -1: + buildLines.append(line[buildIndex + 6:].lstrip()) + runIndex = string.find(line, "RUN:") + if runIndex != -1: + runLines.append(line[runIndex+4:].lstrip()) + onlyIndex = string.find(line, "BUILD_ONLY:") + if onlyIndex != -1: + onlyLines.append(line[onlyIndex+11:].lstrip()) + minOsIndex = string.find(line, "BUILD_MIN_OS:") + if minOsIndex != -1: + minOS = line[minOsIndex+13:].lstrip() + timeoutIndex = string.find(line, "RUN_TIMEOUT:") + if timeoutIndex != -1: + timeout = line[timeoutIndex+12:].lstrip() + + return { + "BUILD": buildLines, + "BUILD_ONLY": onlyLines, + "BUILD_MIN_OS": minOS, + "RUN": runLines, + "RUN_TIMEOUT": timeout + } + + +# +# Look at directives dictionary to see if this test should be skipped for this platform +# +def useTestCase(testCaseDirectives, platformName): + onlyLines = testCaseDirectives["BUILD_ONLY"] + for only in onlyLines: + if only == "MacOSX" and platformName != "macosx": + return False + if only == "iOS" and platformName != "iphoneos": + return False + return True + + +# +# Use BUILD directives to construct the test case +# Use RUN directives to generate a shell script to run test(s) +# +def buildTestCase(testCaseDirectives, testCaseSourceDir, toolsDir, sdkDir, minOsOptionsName, defaultMinOS, archOptions, testCaseDestDirBuild, testCaseDestDirRun): + scratchDir = tempfile.mkdtemp() + if testCaseDirectives["BUILD_MIN_OS"]: + minOS = testCaseDirectives["BUILD_MIN_OS"] + else: + minOS = defaultMinOS + compilerSearchOptions = " -isysroot " + sdkDir + " -I" + sdkDir + "/System/Library/Frameworks/System.framework/PrivateHeaders" + if minOsOptionsName == "mmacosx-version-min": + taskForPidCommand = "touch " + else: + taskForPidCommand = "codesign --force --sign - --entitlements " + testCaseSourceDir + "/../../task_for_pid_entitlement.plist " + buildSubs = { + "CC": toolsDir + "/usr/bin/clang " + archOptions + " -" + minOsOptionsName + "=" + str(minOS) + compilerSearchOptions, + "CXX": toolsDir + "/usr/bin/clang++ " + archOptions + " -" + minOsOptionsName + "=" + str(minOS) + compilerSearchOptions, + "BUILD_DIR": testCaseDestDirBuild, + "RUN_DIR": testCaseDestDirRun, + "TEMP_DIR": scratchDir, + "TASK_FOR_PID_ENABLE": taskForPidCommand + } + os.makedirs(testCaseDestDirBuild) + os.chdir(testCaseSourceDir) + print >> sys.stderr, "cd " + testCaseSourceDir + for line in testCaseDirectives["BUILD"]: + cmd = string.Template(line).safe_substitute(buildSubs) + print >> sys.stderr, cmd + cmdList = [] + cmdList = string.split(cmd) + result = subprocess.call(cmdList) + if result: + return result + shutil.rmtree(scratchDir, ignore_errors=True) + sudoSub = "" + if minOsOptionsName == "mmacosx-version-min": + sudoSub = "sudo" + runSubs = { + "RUN_DIR": testCaseDestDirRun, + "REQUIRE_CRASH": "nocr -require_crash", + "SUDO": sudoSub, + } + runFilePath = testCaseDestDirBuild + "/run.sh" + with open(runFilePath, "a") as runFile: + runFile.write("#!/bin/sh\n") + runFile.write("cd " + testCaseDestDirRun + "\n") + os.chmod(runFilePath, 0755) + for runline in testCaseDirectives["RUN"]: + runFile.write(string.Template(runline).safe_substitute(runSubs) + "\n") + runFile.write("\n") + runFile.close() + return 0 + + + +# +# Use XCode build settings to build all unit tests for specified platform +# Generate a .plist for BATS to use to run all tests +# +if __name__ == "__main__": + dstDir = os.getenv("DSTROOT", "/tmp/dyld_tests/") + testsRunDstTopDir = "/AppleInternal/CoreOS/tests/dyld/" + testsBuildDstTopDir = dstDir + testsRunDstTopDir + shutil.rmtree(testsBuildDstTopDir, ignore_errors=True) + testsSrcTopDir = os.getenv("SRCROOT", "./") + "/testing/test-cases/" + sdkDir = os.getenv("SDKROOT", "/") + toolsDir = os.getenv("TOOLCHAIN_DIR", "/") + defaultMinOS = "" + minOSOption = os.getenv("DEPLOYMENT_TARGET_CLANG_FLAG_NAME", "") + if minOSOption: + minOSVersName = os.getenv("DEPLOYMENT_TARGET_CLANG_ENV_NAME", "") + if minOSVersName: + minVersNum = os.getenv(minOSVersName, "") + platformName = os.getenv("PLATFORM_NAME", "osx") + archOptions = "" + archList = os.getenv("RC_ARCHS", "") + if archList: + for arch in string.split(archList, " "): + archOptions = archOptions + " -arch " + arch + else: + archList = os.getenv("ARCHS_STANDARD_32_64_BIT", "") + if platformName == "watchos": + archOptions = "-arch armv7k" + elif platformName == "appletvos": + archOptions = "-arch arm64" + else: + archOptions = "" + for arch in string.split(archList, " "): + archOptions = archOptions + " -arch " + arch + allTests = [] + for f in os.listdir(testsSrcTopDir): + if f.endswith(".dtest"): + testName = f[0:-6] + outDirBuild = testsBuildDstTopDir + testName + outDirRun = testsRunDstTopDir + testName + testCaseDir = testsSrcTopDir + f + testCaseDirectives = parseDirectives(testCaseDir) + if useTestCase(testCaseDirectives, platformName): + result = buildTestCase(testCaseDirectives, testCaseDir, toolsDir, sdkDir, minOSOption, minVersNum, archOptions, outDirBuild, outDirRun) + if result: + sys.exit(result) + mytest = {} + mytest["TestName"] = testName + mytest["Arch"] = "platform-native" + mytest["WorkingDirectory"] = testsRunDstTopDir + testName + mytest["Command"] = [] + mytest["Command"].append("./run.sh") + for runline in testCaseDirectives["RUN"]: + if "$SUDO" in runline: + mytest["AsRoot"] = 1 + if testCaseDirectives["RUN_TIMEOUT"]: + mytest["Timeout"] = testCaseDirectives["RUN_TIMEOUT"] + allTests.append(mytest) + batsInfo = { "BATSConfigVersion": "0.1.0", + "Project": "dyld_tests", + "Tests": allTests } + batsFileDir = dstDir + "/AppleInternal/CoreOS/BATS/unit_tests/" + shutil.rmtree(batsFileDir, ignore_errors=True) + os.makedirs(batsFileDir) + batsFilePath = batsFileDir + "dyld.plist" + with open(batsFilePath, "w") as batsFile: + batsFile.write(plistlib.writePlistToString(batsInfo)) + batsFile.close() + os.system('plutil -convert binary1 ' + batsFilePath) # convert the plist in place to binary + runHelper = dstDir + "/AppleInternal/CoreOS/tests/dyld/run_all_dyld_tests.sh" + print runHelper + with open(runHelper, "w") as shFile: + shFile.write("#!/bin/sh\n") + for test in allTests: + shFile.write(test["WorkingDirectory"] + "/run.sh\n") + shFile.close() + os.chmod(runHelper, 0755) + + diff --git a/src/dyld-apple/testing/nocr/execserver.defs b/src/dyld-apple/testing/nocr/execserver.defs new file mode 100644 index 000000000..e528df455 --- /dev/null +++ b/src/dyld-apple/testing/nocr/execserver.defs @@ -0,0 +1 @@ +#include diff --git a/src/dyld-apple/testing/nocr/nocr.1 b/src/dyld-apple/testing/nocr/nocr.1 new file mode 100644 index 000000000..2c2bd3e81 --- /dev/null +++ b/src/dyld-apple/testing/nocr/nocr.1 @@ -0,0 +1,26 @@ +.Dd Dec 15, 2015 +.Dt nocr 1 +.Os Darwin +.Sh NAME +.Nm nocr +.Nd "runs a command with crash reporter disabled" +.Sh SYNOPSIS +.HP 5n +\fBnocr\fR +\fIcommand\fR +.br +.Sh DESCRIPTION +\fBnocr\fR +executes +\fIcommand\fR +in a way that blocks crash reporter should the command process crash. +This is useful when running test suites +.Pp +.Sh "EXIT VALUE" +Upon successful execution of a program, the exit status from +\fInocr\fR +will simply be the exit status of the program that was executed. +.Pp +If the program +\fIcommand\fR +crashed, the exit value will be 1 diff --git a/src/dyld-apple/testing/nocr/nocr.c b/src/dyld-apple/testing/nocr/nocr.c new file mode 100644 index 000000000..74c1e8841 --- /dev/null +++ b/src/dyld-apple/testing/nocr/nocr.c @@ -0,0 +1,189 @@ +#include "execserverServer.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + + +static pid_t sChildPid; +static dispatch_semaphore_t sServerRunning; +static bool sChildCrashed = false; +static bool sChildTerminatedByDyld = false; + +/* + * setup exception handling port for EXC_CRASH and EXC_CORPSE_NOTIFY. + * runs mach_msg_server once for receiving exception messages from kernel. + */ +static void* serverCode(void* arg) +{ + mach_port_t exception_port; + + kern_return_t kret = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &exception_port); + if (kret != KERN_SUCCESS) + errx(1, "mach_port_allocate: %s (%d)", mach_error_string(kret), kret); + + kret = mach_port_insert_right(mach_task_self(), exception_port, exception_port, MACH_MSG_TYPE_MAKE_SEND); + if (kret != KERN_SUCCESS) + errx(1, "mach_port_insert_right: %s (%d)", mach_error_string(kret), kret); + + kret = task_set_exception_ports(mach_task_self(), EXC_MASK_CRASH | EXC_MASK_CORPSE_NOTIFY, exception_port, + EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES, 0); + if (kret != KERN_SUCCESS) + errx(1, "task_set_exception_ports: %s (%d)", mach_error_string(kret), kret); + + dispatch_semaphore_signal(sServerRunning); + + kret = mach_msg_server(mach_exc_server, MACH_MSG_SIZE_RELIABLE, exception_port, 0); + if (kret != KERN_SUCCESS) + errx(1, "mach_msg_server: %s (%d)", mach_error_string(kret), kret); + + return NULL; +} + + +static void childDied(int sig) +{ + struct proc_exitreasoninfo info; + bzero(&info, sizeof(info)); + uint8_t packReasonData[OS_REASON_BUFFER_MAX_SIZE]; + bzero(packReasonData, OS_REASON_BUFFER_MAX_SIZE); + info.eri_reason_buf_size = OS_REASON_BUFFER_MAX_SIZE; + info.eri_kcd_buf = (user_addr_t)packReasonData; + //fprintf(stderr, "info=%p\n", &info); + if ( proc_pidinfo(sChildPid, PROC_PIDEXITREASONINFO, 1, &info, PROC_PIDEXITREASONINFO_SIZE) != sizeof(struct proc_exitreasoninfo) ) { + printf("bad return size from proc_pidinfo()\n"); + return; + } + sChildTerminatedByDyld = (info.eri_namespace == OS_REASON_DYLD); + } + + +int main(int argc, const char* argv[]) +{ + if ( argc < 2 ) { + fprintf(stderr, "usage: nocr [-require_crash] prog args...\n"); + return EXIT_FAILURE; + } + unsigned progArgIndex = 1; + bool requireCrash = false; + const char* testName = NULL; + if ( strcmp(argv[1], "-require_crash") == 0 ) { + progArgIndex = 2; + requireCrash = true; + testName = getenv("NOCR_TEST_NAME"); + if ( testName ) + printf("[BEGIN] %s\n", testName); + } + + signal(SIGCHLD, childDied); + + sServerRunning = dispatch_semaphore_create(0); + + // start up thread for mach server which handles mach exception ports + pthread_t serverThread; + int result = pthread_create(&serverThread, NULL, serverCode, NULL); + if ( result ) + err(EXIT_FAILURE, "pthread_create"); + + // wait until server is up before starting child + dispatch_semaphore_wait(sServerRunning, DISPATCH_TIME_FOREVER); + + // fork and exec child + sChildPid = fork(); + if ( sChildPid < 0 ) + err(EXIT_FAILURE, "fork"); + if ( sChildPid == 0 ) { + // child side + result = execvp(argv[progArgIndex], (char**)&argv[progArgIndex]); + err(EXIT_FAILURE, "exec(\"%s\",...)", argv[progArgIndex]); + } + + // wait for child to finish (including crash) + int status; + int waitResult; + int childResult = EXIT_FAILURE; + do { + waitResult = waitpid(sChildPid, &status, 0); + } while ( (waitResult == -1) && (errno == EINTR) ); + if ( waitResult != -1 ) { + if ( WIFEXITED(status) ) { + childResult = WEXITSTATUS(status); + } + } + + if ( requireCrash ) { + if ( testName ) { + if ( sChildCrashed || sChildTerminatedByDyld ) + printf("[PASS] %s\n", testName); + else + printf("[FAIL] %s\n", testName); + } + return sChildCrashed ? EXIT_SUCCESS : EXIT_FAILURE; + } + else + return childResult; +} + + + + +// Mach exception handler routines needed by execserverServer.c + +kern_return_t +catch_mach_exception_raise(mach_port_t exception_port, + mach_port_t thread, + mach_port_t task, + exception_type_t exception, + mach_exception_data_t code, + mach_msg_type_number_t codeCnt) +{ + //fprintf(stderr, "child crashed\n"); + sChildCrashed = true; + return KERN_SUCCESS; +} + +kern_return_t +catch_mach_exception_raise_state(mach_port_t exception_port, + exception_type_t exception, + const mach_exception_data_t code, + mach_msg_type_number_t codeCnt, + int * flavor, + const thread_state_t old_state, + mach_msg_type_number_t old_stateCnt, + thread_state_t new_state, + mach_msg_type_number_t * new_stateCnt) +{ + errx(1, "Unsupported catch_mach_exception_raise_state"); + return KERN_NOT_SUPPORTED; +} + +kern_return_t +catch_mach_exception_raise_state_identity(mach_port_t exception_port, + mach_port_t thread, + mach_port_t task, + exception_type_t exception, + mach_exception_data_t code, + mach_msg_type_number_t codeCnt, + int * flavor, + thread_state_t old_state, + mach_msg_type_number_t old_stateCnt, + thread_state_t new_state, + mach_msg_type_number_t * new_stateCnt) +{ + errx(1, "Unsupported catch_mach_exception_raise_state_identity"); + return KERN_NOT_SUPPORTED; +} + + + diff --git a/src/dyld-apple/testing/run_all_dyld_tests.py b/src/dyld-apple/testing/run_all_dyld_tests.py new file mode 100755 index 000000000..14ebc6064 --- /dev/null +++ b/src/dyld-apple/testing/run_all_dyld_tests.py @@ -0,0 +1,114 @@ +#!/usr/bin/python2.7 + +import plistlib +import string +import sys +import os +import shutil +import subprocess + + + + +# +# Parse dyld's BATS input file and run each test +# +if __name__ == "__main__": + batsPlist = plistlib.readPlist("/AppleInternal/CoreOS/BATS/unit_tests/dyld.plist") + tests = batsPlist["Tests"] + passedCount = 0 + failedCount = 0 + for test in tests: + cwd = test["WorkingDirectory"] + if cwd: + os.chdir(cwd) + cmd = test["Command"] + testName = test["TestName"] + if cwd: + try: + runOutput = subprocess.check_output(cmd,stderr= subprocess.STDOUT) + lines = runOutput.splitlines() + foundBegin = False + passed = False + failed = False + currentTestName = "" + for line in lines: + line = line.lstrip().rstrip() + #print testName + ": " + line + beginIndex = string.find(line, "[BEGIN]") + if beginIndex != -1: + beginName = line[beginIndex + 7:].lstrip() + foundBegin = True + currentTestName = beginName + passIndex = string.find(line, "[PASS]") + if passIndex != -1: + passName = line[passIndex + 6:].lstrip() + passed = True + if passName != currentTestName: + print >> sys.stderr, "[PASS] name does not match [BEGIN] name for test " + testName + failIndex = string.find(line, "[FAIL]") + if failIndex != -1: + failName = line[failIndex + 6:].lstrip() + failed = True + if failName != currentTestName: + print >> sys.stderr, "[FAIL] name does not match [BEGIN] name for test " + testName + if foundBegin: + if not passed and not failed: + print >> sys.stderr, "[BEGIN] found [PASS] or [FAIL] for test " + testName + else: + print >> sys.stderr, "Missing [BEGIN] for test " + testName + if passed: + print "PASSED: " + testName + passedCount = passedCount + 1 + elif failed: + print "FAILED: " + testName + failedCount = failedCount + 1 + except subprocess.CalledProcessError as e: + print >> sys.stderr, "FAILED: " + testName + " (execution failure)" + print "Total PASS count: " + str(passedCount) + print "Total FAIL count: " + str(failedCount) + + +def alt(): + testsTopDir = "/AppleInternal/CoreOS/tests/dyld/" + for f in os.listdir(testsTopDir): + testRunner = testsTopDir + f + "/run.sh" + if os.path.isfile(testRunner): + try: + runOutput = subprocess.check_output([testRunner],stderr= subprocess.STDOUT) + lines = runOutput.splitlines() + foundBegin = False + passed = False + failed = False + currentTestName = "" + for line in lines: + line = line.lstrip().rstrip() + #print f + ": " + line + beginIndex = string.find(line, "[BEGIN]") + if beginIndex != -1: + beginName = line[beginIndex + 7:].lstrip() + foundBegin = True + currentTestName = beginName + passIndex = string.find(line, "[PASS]") + if passIndex != -1: + passName = line[passIndex + 6:].lstrip() + passed = True + if passName != currentTestName: + print >> sys.stderr, "[PASS] name does not match [BEGIN] name for test " + f + failIndex = string.find(line, "[FAIL]") + if failIndex != -1: + failName = line[failIndex + 6:].lstrip() + failed = True + if failName != currentTestName: + print >> sys.stderr, "[FAIL] name does not match [BEGIN] name for test " + f + if foundBegin: + if not passed and not failed: + print >> sys.stderr, "[BEGIN] found [PASS] or [FAIL] for test " + f + else: + print >> sys.stderr, "Missing [BEGIN] for test " + f + if passed: + print "PASSED: " + f + elif failed: + print "FAILED: " + f + except subprocess.CalledProcessError as e: + print >> sys.stderr, "FAILED: " + f + " (execution failure)" diff --git a/src/dyld-apple/testing/task_for_pid_entitlement.plist b/src/dyld-apple/testing/task_for_pid_entitlement.plist new file mode 100644 index 000000000..2398d67f5 --- /dev/null +++ b/src/dyld-apple/testing/task_for_pid_entitlement.plist @@ -0,0 +1,10 @@ + + + + + com.apple.system-task-ports + + task_for_pid-allow + + + diff --git a/src/dyld-apple/testing/test-cases/NSAddImage-basic.dtest/main.c b/src/dyld-apple/testing/test-cases/NSAddImage-basic.dtest/main.c new file mode 100644 index 000000000..5c870c46a --- /dev/null +++ b/src/dyld-apple/testing/test-cases/NSAddImage-basic.dtest/main.c @@ -0,0 +1,28 @@ +// BUILD_ONLY: MacOSX + +// BUILD: $CC zzz.c -dynamiclib -o $BUILD_DIR/libzzz.dylib -install_name $RUN_DIR/libzzz.dylib +// BUILD: $CC main.c -o $BUILD_DIR/NSAddImage-basic.exe -Wno-deprecated-declarations + +// RUN: ./NSAddImage-basic.exe $RUN_DIR/libzzz.dylib +// RUN: ./NSAddImage-basic.exe libzzz.dylib + + +#include +#include +#include + + +int main(int arg, const char* argv[]) +{ + const char* path = argv[1]; + printf("[BEGIN] NSAddImage-basic %s\n", path); + + const struct mach_header* mh = NSAddImage(path, NSADDIMAGE_OPTION_WITH_SEARCHING); + if ( mh == NULL ) + printf("[FAIL] NSAddImage-basic %s\n", path); + else + printf("[PASS] NSAddImage-basic %s\n", path); + + return 0; +} + diff --git a/src/dyld-apple/testing/test-cases/NSAddImage-basic.dtest/zzz.c b/src/dyld-apple/testing/test-cases/NSAddImage-basic.dtest/zzz.c new file mode 100644 index 000000000..c02be2d95 --- /dev/null +++ b/src/dyld-apple/testing/test-cases/NSAddImage-basic.dtest/zzz.c @@ -0,0 +1 @@ +void zzz() {} diff --git a/src/dyld-apple/testing/test-cases/dlopen-basic.dtest/foo.c b/src/dyld-apple/testing/test-cases/dlopen-basic.dtest/foo.c new file mode 100644 index 000000000..c8e9924b4 --- /dev/null +++ b/src/dyld-apple/testing/test-cases/dlopen-basic.dtest/foo.c @@ -0,0 +1,5 @@ +int foo() +{ + return 10; +} + diff --git a/src/dyld-apple/testing/test-cases/dlopen-basic.dtest/main.c b/src/dyld-apple/testing/test-cases/dlopen-basic.dtest/main.c new file mode 100644 index 000000000..e50623652 --- /dev/null +++ b/src/dyld-apple/testing/test-cases/dlopen-basic.dtest/main.c @@ -0,0 +1,48 @@ + +// BUILD: $CC foo.c -dynamiclib -o $BUILD_DIR/test.dylib +// BUILD: $CC foo.c -bundle -o $BUILD_DIR/test.bundle +// BUILD: $CC main.c -o $BUILD_DIR/dlopen-basic.exe + +// RUN: ./dlopen-basic.exe + +#include +#include + + +static void tryImage(const char* path) +{ + printf("[BEGIN] dlopen-basic %s\n", path); + void* handle = dlopen(path, RTLD_LAZY); + if ( handle == NULL ) { + printf("dlerror(): %s\n", dlerror()); + printf("[FAIL] dlopen-basic %s\n", path); + return; + } + + void* sym = dlsym(handle, "foo"); + if ( sym == NULL ) { + printf("dlerror(): %s\n", dlerror()); + printf("[FAIL] dlopen-basic %s\n", path); + return; + } + + int result = dlclose(handle); + if ( result != 0 ) { + printf("dlclose() returned %c\n", result); + printf("[FAIL] dlopen-basic %s\n", path); + return; + } + + printf("[PASS] dlopen-basic %s\n", path); +} + + + +int main() +{ + tryImage("test.bundle"); + tryImage("test.dylib"); + + return 0; +} + diff --git a/src/dyld-apple/testing/test-cases/dlopen-framework-fallback.dtest/main.c b/src/dyld-apple/testing/test-cases/dlopen-framework-fallback.dtest/main.c new file mode 100644 index 000000000..02c467e83 --- /dev/null +++ b/src/dyld-apple/testing/test-cases/dlopen-framework-fallback.dtest/main.c @@ -0,0 +1,35 @@ + +// BUILD: $CC main.c -o $BUILD_DIR/dlopen-framework-fallback.exe + +// RUN: ./dlopen-framework-fallback.exe + +#include +#include + + + +int main() +{ + printf("[BEGIN] dlopen-framework-fallback\n"); + + // Verify dyld will fallback and look for framework in /System/Library/Frameworks/ + void* handle = dlopen("/System/Library/BadPath/CoreFoundation.framework/CoreFoundation", RTLD_LAZY); + if ( handle == NULL ) { + printf("dlerror(): %s\n", dlerror()); + printf("[FAIL] dlopen-framework-fallback\n"); + return 0; + } + + // validate handle works to find symbols + void* sym = dlsym(handle, "CFRetain"); + if ( sym == NULL ) { + printf("dlerror(): %s\n", dlerror()); + printf("[FAIL] dlopen-framework-fallback\n"); + return 0; + } + + printf("[PASS] dlopen-framework-fallback\n"); + + return 0; +} + diff --git a/src/dyld-apple/testing/test-cases/dlopen-signing.dtest/dylib.c b/src/dyld-apple/testing/test-cases/dlopen-signing.dtest/dylib.c new file mode 100644 index 000000000..9841a540e --- /dev/null +++ b/src/dyld-apple/testing/test-cases/dlopen-signing.dtest/dylib.c @@ -0,0 +1,3 @@ +int foo() { + return 10; +} diff --git a/src/dyld-apple/testing/test-cases/dlopen-signing.dtest/main.c b/src/dyld-apple/testing/test-cases/dlopen-signing.dtest/main.c new file mode 100644 index 000000000..eaaa69bcb --- /dev/null +++ b/src/dyld-apple/testing/test-cases/dlopen-signing.dtest/main.c @@ -0,0 +1,48 @@ +// BUILD: $CC dylib.c -dynamiclib -o $BUILD_DIR/signed.dylib +// BUILD: $CC dylib.c -dynamiclib -o $BUILD_DIR/unsigned.dylib +// BUILD: $CC main.c -o $BUILD_DIR/dlopen-signed.exe +// BUILD: $CC main.c -o $BUILD_DIR/dlopen-unsigned.exe + +// FIXME: add builds that sign the executable and the dylib in in various ways +// At this time we don't have a way to do that, so this test must be run +// manually. + +#include +#include + +int main() { + printf("[BEGIN] dlopen-signing\n"); + void* handle = dlopen("signed.dylib", RTLD_LAZY); + if ( handle == NULL ) { + printf("dlerror(): %s\n", dlerror()); + printf("[FAIL] dlopen-signing (signed loading signed)\n"); + return 0; + } else { + int result = dlclose(handle); + if ( result != 0 ) { + printf("dlclose() returned %c\n", result); + printf("[FAIL] dlopen-signing (signed unloading signed)\n"); + return 0; + } + } + + handle = dlopen("unsigned.dylib", RTLD_LAZY); + if ( handle != NULL ) { + printf("dlerror(): %s\n", dlerror()); + printf("[FAIL] dlopen-signing (signed loading unsigned)\n"); + return 0; + } else { + int result = dlclose(handle); + if ( result != 0 ) { + printf("dlclose() returned %c\n", result); + printf("[FAIL] dlopen-signing (signed unloading signed)\n"); + return 0; + } + } + + printf("[PASS] dlopen-signing\n"); + + return 0; +} + + diff --git a/src/dyld-apple/testing/test-cases/dyld_abort_payload.dtest/defSymbol.c b/src/dyld-apple/testing/test-cases/dyld_abort_payload.dtest/defSymbol.c new file mode 100644 index 000000000..2e575f33f --- /dev/null +++ b/src/dyld-apple/testing/test-cases/dyld_abort_payload.dtest/defSymbol.c @@ -0,0 +1,5 @@ + + +#if HAS_SYMBOL +int slipperySymbol = 5; +#endif diff --git a/src/dyld-apple/testing/test-cases/dyld_abort_payload.dtest/emptyMain.c b/src/dyld-apple/testing/test-cases/dyld_abort_payload.dtest/emptyMain.c new file mode 100644 index 000000000..0ba8a5b34 --- /dev/null +++ b/src/dyld-apple/testing/test-cases/dyld_abort_payload.dtest/emptyMain.c @@ -0,0 +1,7 @@ +#include + +int main(int argc, const char* argv[]) +{ + return 1; +} + diff --git a/src/dyld-apple/testing/test-cases/dyld_abort_payload.dtest/foo.c b/src/dyld-apple/testing/test-cases/dyld_abort_payload.dtest/foo.c new file mode 100644 index 000000000..bd8954848 --- /dev/null +++ b/src/dyld-apple/testing/test-cases/dyld_abort_payload.dtest/foo.c @@ -0,0 +1,5 @@ + +void foo() +{ +} + diff --git a/src/dyld-apple/testing/test-cases/dyld_abort_payload.dtest/main.c b/src/dyld-apple/testing/test-cases/dyld_abort_payload.dtest/main.c new file mode 100644 index 000000000..e01234dd4 --- /dev/null +++ b/src/dyld-apple/testing/test-cases/dyld_abort_payload.dtest/main.c @@ -0,0 +1,173 @@ + +// BUILD: $CC foo.c -dynamiclib -install_name /cant/find/me.dylib -o $BUILD_DIR/libmissing.dylib +// BUILD: $CC emptyMain.c $BUILD_DIR/libmissing.dylib -o $BUILD_DIR/prog_missing_dylib.exe +// BUILD: $CC defSymbol.c -dynamiclib -install_name libMissingSymbols.dylib -o $BUILD_DIR/libMissingSymbols.dylib +// BUILD: $CC defSymbol.c -dynamiclib -install_name libMissingSymbols.dylib -o $BUILD_DIR/libHasSymbols.dylib -DHAS_SYMBOL +// BUILD: $CC useSymbol.c $BUILD_DIR/libHasSymbols.dylib -o $BUILD_DIR/prog_missing_symbol.exe +// BUILD: $CC main.c -o $BUILD_DIR/dyld_abort_tests.exe + +// RUN: ./dyld_abort_tests.exe + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +static bool sSignalCaught = false; +static bool sChildAbortInfoCorrect = false; +static pid_t sChildPid = 0; +static uint64_t sExpectedDyldReason = 0; +static const char* sExpectedDylibPath = NULL; +static const char* sExpectedSymbol = NULL; + + +static void childDied(int sig) +{ + sSignalCaught = true; + //printf("sigchld for pid=%d\n", sChildPid); + + struct proc_exitreasoninfo info; + bzero(&info, sizeof(info)); + uint8_t packReasonData[OS_REASON_BUFFER_MAX_SIZE]; + bzero(packReasonData, OS_REASON_BUFFER_MAX_SIZE); + info.eri_reason_buf_size = OS_REASON_BUFFER_MAX_SIZE; + info.eri_kcd_buf = (user_addr_t)packReasonData; + //fprintf(stderr, "info=%p\n", &info); + if ( proc_pidinfo(sChildPid, PROC_PIDEXITREASONINFO, 1, &info, PROC_PIDEXITREASONINFO_SIZE) != sizeof(struct proc_exitreasoninfo) ) { + printf("bad return size from proc_pidinfo()\n"); + return; + } + if ( info.eri_namespace != OS_REASON_DYLD ) { + printf("eri_namespace != OS_REASON_DYLD\n"); + return; + } + if ( info.eri_code != sExpectedDyldReason ) { + printf("eri_code != %lld\n", sExpectedDyldReason); + return; + } + kcdata_iter_t iter = kcdata_iter(packReasonData, info.eri_reason_buf_size); + + if ( !kcdata_iter_valid(iter) ) { + printf("invalid kcdata iterator from payload data\n"); + return; + } + + if ( kcdata_iter_type(iter) != KCDATA_BUFFER_BEGIN_OS_REASON ){ + printf("first kcdata from payload data is not KCDATA_BUFFER_BEGIN_OS_REASON\n"); + return; + } + + kcdata_iter_t payloadIter = kcdata_iter_find_type(iter, EXIT_REASON_USER_PAYLOAD); + if ( !kcdata_iter_valid(payloadIter) ) { + printf("invalid kcdata payload iterator from payload data\n"); + return; + } + const dyld_abort_payload* dyldInfo = (dyld_abort_payload*)kcdata_iter_payload(payloadIter); + + if ( dyldInfo->version != 1 ) { + printf("dyld payload is not version 1\n"); + return; + } + + if ( (dyldInfo->flags & 1) == 0 ) { + printf("dyld flags should have low bit set to me process terminated at launch\n"); + return; + } + + if ( sExpectedDylibPath != NULL ) { + if ( dyldInfo->targetDylibPathOffset != 0 ) { + const char* targetDylib = (char*)dyldInfo + dyldInfo->targetDylibPathOffset; + if ( strcmp(sExpectedDylibPath, targetDylib) != 0 ) { + printf("dylib path (%s) not what expected (%s)\n", targetDylib, sExpectedDylibPath); + return; + } + } + else { + printf("dylib path (%s) not provided by dyld\n", sExpectedDylibPath); + return; + } + } + + if ( sExpectedSymbol != NULL ) { + if ( dyldInfo->targetDylibPathOffset != 0 ) { + const char* missingSymbol = (char*)dyldInfo + dyldInfo->symbolOffset; + if ( strcmp(sExpectedSymbol, missingSymbol) != 0 ) { + printf("symbol (%s) not what expected (%s)\n", missingSymbol, sExpectedSymbol); + return; + } + } + else { + printf("symbol (%s) not provided by dyld\n", sExpectedSymbol); + return; + } + } + + sChildAbortInfoCorrect = true; +} + + +bool runTest(const char* prog, uint64_t dyldReason, const char* expectedDylibPath, const char* expectedSymbol) +{ + sSignalCaught = false; + sChildAbortInfoCorrect = false; + sExpectedDyldReason = dyldReason; + sExpectedDylibPath = expectedDylibPath; + sExpectedSymbol = expectedSymbol; + + // fork and exec child + sChildPid = fork(); + if ( sChildPid < 0 ) + err(EXIT_FAILURE, "fork"); + if ( sChildPid == 0 ) { + // child side + char* childArgv[] = { (char*)prog, NULL }; + int result = execvp(prog, childArgv); + err(EXIT_FAILURE, "exec(\"%s\",...)", prog); + } + for(int i=0; i < 10; ++i) { + if ( sSignalCaught ) + break; + sleep(1); + } + + return sChildAbortInfoCorrect; +} + + +int main(int argc, const char* argv[]) +{ + bool someTestFailed = false; + printf("[BEGIN] dyld_abort_payload\n"); + + // set up signal handler for catching child terminations + signal(SIGCHLD, childDied); + + // test launch program with missing library + if ( !runTest("./prog_missing_dylib.exe", DYLD_EXIT_REASON_DYLIB_MISSING, "/cant/find/me.dylib", NULL) ) { + printf("[FAIL] dyld_abort_payload DYLD_EXIT_REASON_DYLIB_MISSING\n"); + someTestFailed = true; + } + + // test launch program with missing symbol + if ( !runTest("./prog_missing_symbol.exe", DYLD_EXIT_REASON_SYMBOL_MISSING, "libMissingSymbols.dylib", "_slipperySymbol") ) { + printf("[FAIL] dyld_abort_payload DYLD_EXIT_REASON_SYMBOL_MISSING\n"); + someTestFailed = true; + } + + if ( !someTestFailed ) + printf("[PASS] dyld_abort_payload\n"); + + return 0; +} + diff --git a/src/dyld-apple/testing/test-cases/dyld_abort_payload.dtest/useSymbol.c b/src/dyld-apple/testing/test-cases/dyld_abort_payload.dtest/useSymbol.c new file mode 100644 index 000000000..3aada36f8 --- /dev/null +++ b/src/dyld-apple/testing/test-cases/dyld_abort_payload.dtest/useSymbol.c @@ -0,0 +1,8 @@ + + +extern int slipperySymbol; + +int main() +{ + return slipperySymbol; +} diff --git a/src/dyld-apple/testing/test-cases/dyld_process_info.dtest/linksWithCF.c b/src/dyld-apple/testing/test-cases/dyld_process_info.dtest/linksWithCF.c new file mode 100644 index 000000000..ffbe74e49 --- /dev/null +++ b/src/dyld-apple/testing/test-cases/dyld_process_info.dtest/linksWithCF.c @@ -0,0 +1,8 @@ +#include + +int main() +{ + task_suspend(mach_task_self()); + return 0; +} + diff --git a/src/dyld-apple/testing/test-cases/dyld_process_info.dtest/main.c b/src/dyld-apple/testing/test-cases/dyld_process_info.dtest/main.c new file mode 100644 index 000000000..9e5b41e28 --- /dev/null +++ b/src/dyld-apple/testing/test-cases/dyld_process_info.dtest/main.c @@ -0,0 +1,177 @@ + +// BUILD: $CC linksWithCF.c -o $BUILD_DIR/linksWithCF.exe -framework CoreFoundation +// BUILD: $CC main.c -o $BUILD_DIR/dyld_process_info.exe +// BUILD: $TASK_FOR_PID_ENABLE $BUILD_DIR/dyld_process_info.exe + +// RUN: $SUDO ./dyld_process_info.exe $RUN_DIR/linksWithCF.exe + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +extern char** environ; + +#if __x86_64__ + cpu_type_t otherArch[] = { CPU_TYPE_I386 }; +#elif __i386__ + cpu_type_t otherArch[] = { CPU_TYPE_X86_64 }; +#elif __arm64__ + cpu_type_t otherArch[] = { CPU_TYPE_ARM }; +#elif __arm__ + cpu_type_t otherArch[] = { CPU_TYPE_ARM64 }; +#endif + +static task_t launchTest(const char* testProgPath, bool launchOtherArch, bool launchSuspended) +{ + posix_spawnattr_t attr; + if ( posix_spawnattr_init(&attr) != 0 ) { + printf("[FAIL] dyld_process_info posix_spawnattr_init()\n"); + exit(0); + } + if ( launchSuspended ) { + if ( posix_spawnattr_setflags(&attr, POSIX_SPAWN_START_SUSPENDED) != 0 ) { + printf("[FAIL] dyld_process_info POSIX_SPAWN_START_SUSPENDED\n"); + exit(0); + } + } + if ( launchOtherArch ) { + size_t copied; + if ( posix_spawnattr_setbinpref_np(&attr, 1, otherArch, &copied) != 0 ) { + printf("[FAIL] dyld_process_info posix_spawnattr_setbinpref_np()\n"); + exit(0); + } + } + + pid_t childPid; + const char* argv[] = { testProgPath, NULL }; + int psResult = posix_spawn(&childPid, testProgPath, NULL, &attr, (char**)argv, environ); + if ( psResult != 0 ) { + printf("[FAIL] dyld_process_info posix_spawn(%s) failed, err=%d\n", testProgPath, psResult); + exit(0); + } + //printf("child pid=%d\n", childPid); + + task_t childTask = 0; + if ( task_for_pid(mach_task_self(), childPid, &childTask) != KERN_SUCCESS ) { + printf("[FAIL] dyld_process_info task_for_pid()\n"); + kill(childPid, SIGKILL); + exit(0); + } + + // wait until process is up and has suspended itself + struct task_basic_info info; + do { + unsigned count = TASK_BASIC_INFO_COUNT; + kern_return_t kr = task_info(childTask, TASK_BASIC_INFO, (task_info_t)&info, &count); + sleep(1); + } while ( info.suspend_count == 0 ); + + return childTask; +} + +static bool hasCF(task_t task, bool launchedSuspended) +{ + kern_return_t result; + dyld_process_info info = _dyld_process_info_create(task, 0, &result); + if ( info == NULL ) { + printf("[FAIL] dyld_process_info _dyld_process_info_create(), kern_return_t=%d\n", result); + return false; + } + + dyld_process_state_info stateInfo; + _dyld_process_info_get_state(info, &stateInfo); + bool valueSaysLaunchedSuspended = (stateInfo.dyldState == dyld_process_state_not_started); + if ( valueSaysLaunchedSuspended != launchedSuspended ) { + printf("[FAIL] dyld_process_info suspend state mismatch\n"); + return false; + } + + __block bool foundDyld = false; + _dyld_process_info_for_each_image(info, ^(uint64_t machHeaderAddress, const uuid_t uuid, const char* path) { + //fprintf(stderr, "0x%llX %s\n", machHeaderAddress, path); + if ( strstr(path, "/usr/lib/dyld") != NULL ) + foundDyld = true; + }); + + if ( launchedSuspended ) { + // fprintf(stderr, "launched suspended image list:\n"); + __block bool foundMain = false; + _dyld_process_info_for_each_image(info, ^(uint64_t machHeaderAddress, const uuid_t uuid, const char* path) { + //fprintf(stderr, "0x%llX %s\n", machHeaderAddress, path); + if ( strstr(path, "/linksWithCF.exe") != NULL ) + foundMain = true; + }); + return foundMain && foundDyld; + } + + __block bool foundCF = false; + _dyld_process_info_for_each_image(info, ^(uint64_t machHeaderAddress, const uuid_t uuid, const char* path) { + //fprintf(stderr, "0x%llX %s\n", machHeaderAddress, path); + if ( strstr(path, "/CoreFoundation.framework/") != NULL ) + foundCF = true; + }); + + _dyld_process_info_release(info); + + return foundCF && foundDyld; +} + + +int main(int argc, const char* argv[]) +{ + printf("[BEGIN] dyld_process_info\n"); + + if ( argc < 2 ) { + printf("[FAIL] dyld_process_info missing argument\n"); + exit(0); + } + const char* testProgPath = argv[1]; + task_t childTask; + + // launch test program same arch as this program + childTask = launchTest(testProgPath, false, false); + if ( ! hasCF(childTask, false) ) { + printf("[FAIL] dyld_process_info same arch does not link with CF and dyld\n"); + task_terminate(childTask); + exit(0); + } + task_terminate(childTask); + + // launch test program suspended + childTask = launchTest(testProgPath, false, true); + if ( ! hasCF(childTask, true) ) { + printf("[FAIL] dyld_process_info suspended does not link with CF and dyld\n"); + task_terminate(childTask); + exit(0); + } + task_resume(childTask); + task_terminate(childTask); + +#if !TARGET_OS_WATCH && !TARGET_OS_TV && __LP64__ + // on 64/32 devices, run test program as other arch too + childTask = launchTest(testProgPath, true, false); + if ( ! hasCF(childTask, false) ) { + printf("[FAIL] dyld_process_info other arch does not link with CF and dyld\n"); + task_terminate(childTask); + exit(0); + } + task_terminate(childTask); +#endif + + // verify this program does not use CF + if ( hasCF(mach_task_self(), false) ) { + printf("[FAIL] dyld_process_info self links with CF and dyld\n"); + exit(0); + } + + printf("[PASS] dyld_process_info\n"); + return 0; +} diff --git a/src/dyld-apple/testing/test-cases/dyld_process_info_notify.dtest/foo.c b/src/dyld-apple/testing/test-cases/dyld_process_info_notify.dtest/foo.c new file mode 100644 index 000000000..ec118333f --- /dev/null +++ b/src/dyld-apple/testing/test-cases/dyld_process_info_notify.dtest/foo.c @@ -0,0 +1,4 @@ + +void foo() { +} + diff --git a/src/dyld-apple/testing/test-cases/dyld_process_info_notify.dtest/main.c b/src/dyld-apple/testing/test-cases/dyld_process_info_notify.dtest/main.c new file mode 100644 index 000000000..04d391d2c --- /dev/null +++ b/src/dyld-apple/testing/test-cases/dyld_process_info_notify.dtest/main.c @@ -0,0 +1,294 @@ + +// BUILD: $CC target.c -o $BUILD_DIR/target.exe +// BUILD: $CC foo.c -o $BUILD_DIR/libfoo.dylib -dynamiclib +// BUILD: $CC main.c -o $BUILD_DIR/dyld_process_info_notify.exe +// BUILD: $TASK_FOR_PID_ENABLE $BUILD_DIR/dyld_process_info_notify.exe + +// RUN_TIMEOUT: 2400 +// RUN: $SUDO ./dyld_process_info_notify.exe $RUN_DIR/target.exe + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +extern char** environ; + +#if __x86_64__ + cpu_type_t otherArch[] = { CPU_TYPE_I386 }; +#elif __i386__ + cpu_type_t otherArch[] = { CPU_TYPE_X86_64 }; +#elif __arm64__ + cpu_type_t otherArch[] = { CPU_TYPE_ARM }; +#elif __arm__ + cpu_type_t otherArch[] = { CPU_TYPE_ARM64 }; +#endif + +static task_t launchTest(const char* testProgPath, const char* arg1, bool launchOtherArch, bool launchSuspended) +{ + //fprintf(stderr, "launchTest() launchOtherArch=%d, launchSuspended=%d, arg=%s\n", launchOtherArch, launchSuspended, arg1); + posix_spawnattr_t attr; + if ( posix_spawnattr_init(&attr) != 0 ) { + printf("[FAIL] dyld_process_info_notify posix_spawnattr_init()\n"); + exit(0); + } + if ( launchSuspended ) { + if ( posix_spawnattr_setflags(&attr, POSIX_SPAWN_START_SUSPENDED) != 0 ) { + printf("[FAIL] dyld_process_info_notify POSIX_SPAWN_START_SUSPENDED\n"); + exit(0); + } + } + if ( launchOtherArch ) { + size_t copied; + if ( posix_spawnattr_setbinpref_np(&attr, 1, otherArch, &copied) != 0 ) { + printf("[FAIL] dyld_process_info_notify posix_spawnattr_setbinpref_np()\n"); + exit(0); + } + } + + pid_t childPid; + const char* argv[] = { testProgPath, arg1, NULL }; + int psResult = posix_spawn(&childPid, testProgPath, NULL, &attr, (char**)argv, environ); + if ( psResult != 0 ) { + printf("[FAIL] dyld_process_info_notify posix_spawn(%s) failed, err=%d\n", testProgPath, psResult); + exit(0); + } + //printf("child pid=%d\n", childPid); + + task_t childTask = 0; + if ( task_for_pid(mach_task_self(), childPid, &childTask) != KERN_SUCCESS ) { + printf("[FAIL] dyld_process_info_notify task_for_pid()\n"); + kill(childPid, SIGKILL); + exit(0); + } + + return childTask; +} + +static void wait_util_task_suspended(task_t task) +{ + struct task_basic_info info; + do { + unsigned count = TASK_BASIC_INFO_COUNT; + kern_return_t kr = task_info(task, TASK_BASIC_INFO, (task_info_t)&info, &count); + //fprintf(stderr, "task_info() => %d, suspendCount=%d\n", kr, info.suspend_count); + sleep(1); + } while ( info.suspend_count == 0 ); +} + + +static bool monitor(task_t task, bool disconnectEarly, bool attachLate) +{ + kern_return_t kr; + __block bool sawMainExecutable = false; + __block bool sawlibSystem = false; + __block bool gotTerminationNotice = false; + __block bool gotEarlyNotice = false; + __block int libFooLoadCount = 0; + __block int libFooUnloadCount = 0; + dispatch_semaphore_t taskDone = dispatch_semaphore_create(0); + + dispatch_queue_t serviceQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0); + + unsigned count = 0; + dyld_process_info_notify handle; + do { + handle = _dyld_process_info_notify(task, serviceQueue, + ^(bool unload, uint64_t timestamp, uint64_t machHeader, const uuid_t uuid, const char* path) { + if ( strstr(path, "/target.exe") != NULL ) + sawMainExecutable = true; + if ( strstr(path, "/libSystem") != NULL ) + sawlibSystem = true; + if ( strstr(path, "/libfoo.dylib") != NULL ) { + if ( unload ) + ++libFooUnloadCount; + else + ++libFooLoadCount; + if ( disconnectEarly ) { + gotEarlyNotice = true; + dispatch_semaphore_signal(taskDone); + } + } + //fprintf(stderr, "unload=%d, 0x%012llX <%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X> %s\n", + // unload, machHeader, uuid[0], uuid[1], uuid[2], uuid[3], uuid[4], uuid[5], uuid[6], uuid[7], + // uuid[8], uuid[9], uuid[10], uuid[11], uuid[12], uuid[13], uuid[14], uuid[15], path); + }, + ^{ + //fprintf(stderr, "target exited\n"); + gotTerminationNotice = true; + dispatch_semaphore_signal(taskDone); + }, + &kr); + ++count; + if ( handle == NULL ) + fprintf(stderr, "_dyld_process_info_notify() returned NULL, result=%d, count=%d\n", kr, count); + } while ( (handle == NULL) && (count < 5) ); + + if ( handle == NULL ) { + return false; + } + + // if process suspends itself, wait until it has done so + if ( attachLate ) + wait_util_task_suspended(task); + + // resume from initial suspend + task_resume(task); + + // block waiting for notification that target has exited + bool gotSignal = (dispatch_semaphore_wait(taskDone, dispatch_time(DISPATCH_TIME_NOW, 10LL * NSEC_PER_SEC)) == 0); + _dyld_process_info_notify_release(handle); + + + if ( !gotSignal ) { + fprintf(stderr, "did not get exit signal\n"); + return false; + } + + if ( !attachLate && !sawMainExecutable ) { + fprintf(stderr, "did not get load notification of main executable\n"); + return false; + } + + if ( !attachLate && !sawlibSystem ) { + fprintf(stderr, "did not get load notification of libSystem\n"); + return false; + } + + if ( disconnectEarly ) { + if ( libFooLoadCount != 1 ) { + fprintf(stderr, "got %d load notifications about libFoo instead of 1\n", libFooLoadCount); + return false; + } + if ( libFooUnloadCount != 0 ) { + fprintf(stderr, "got %d unload notifications about libFoo instead of 1\n", libFooUnloadCount); + return false; + } + } + else { + if ( libFooLoadCount != 3 ) { + fprintf(stderr, "got %d load notifications about libFoo instead of 3\n", libFooLoadCount); + return false; + } + if ( libFooUnloadCount != 3 ) { + fprintf(stderr, "got %d unload notifications about libFoo instead of 3\n", libFooUnloadCount); + return false; + } + } + + return true; +} + +static void validateMaxNotifies(task_t task) +{ + dispatch_queue_t serviceQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0); + dyld_process_info_notify handles[10]; + for (int i=0; i < 10; ++i) { + kern_return_t kr; + handles[i] = _dyld_process_info_notify(task, serviceQueue, + ^(bool unload, uint64_t timestamp, uint64_t machHeader, const uuid_t uuid, const char* path) { + //fprintf(stderr, "unload=%d, 0x%012llX <%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X> %s\n", + // unload, machHeader, uuid[0], uuid[1], uuid[2], uuid[3], uuid[4], uuid[5], uuid[6], uuid[7], + // uuid[8], uuid[9], uuid[10], uuid[11], uuid[12], uuid[13], uuid[14], uuid[15], path); + }, + ^{ + //fprintf(stderr, "target exited\n"); + }, + &kr); + if ( handles[i] == NULL ) { + if ( i == 8 ) { + // expected failure, because only 8 simultaneous connections allowed + // release one and try again + _dyld_process_info_notify_release(handles[4]); + handles[4] = NULL; + } + else { + fprintf(stderr, "_dyld_process_info_notify() returned NULL and kern_result=%d, on count=%d\n", kr, i); + task_terminate(task); + exit(0); + } + } + } + // release all + for (int i=0; i < 10; ++i) { + if ( handles[i] != NULL ) { + _dyld_process_info_notify_release(handles[i]); + } + } + dispatch_release(serviceQueue); +} + +int main(int argc, const char* argv[]) +{ + printf("[BEGIN] dyld_process_info_notify\n"); + if ( argc < 2 ) { + printf("[FAIL] dyld_process_info_notify missing argument\n"); + exit(0); + } + const char* testProgPath = argv[1]; + + dispatch_async(dispatch_get_main_queue(), ^{ + task_t childTask; + + // test 1) launch test program suspended in same arch as this program + childTask = launchTest(testProgPath, "", false, true); + if ( ! monitor(childTask, false, false) ) { + printf("[FAIL] dyld_process_info_notify launch suspended missed some notifications\n"); + task_terminate(childTask); + exit(0); + } + task_terminate(childTask); + + // test 2) launch test program in same arch as this program where it sleeps itself + childTask = launchTest(testProgPath, "suspend-in-main", false, false); + validateMaxNotifies(childTask); + if ( ! monitor(childTask, false, true) ) { + printf("[FAIL] dyld_process_info_notify launch suspend-in-main missed some notifications\n"); + task_terminate(childTask); + exit(0); + } + task_terminate(childTask); + +#if !TARGET_OS_WATCH && !TARGET_OS_TV && __LP64__ + // test 3) launch test program suspended in opposite arch as this program + childTask = launchTest(testProgPath, "", true, true); + if ( ! monitor(childTask, false, false) ) { + printf("[FAIL] dyld_process_info_notify launch suspended other arch missed some notifications\n"); + task_terminate(childTask); + exit(0); + } + task_terminate(childTask); + + // test 4) launch test program in opposite arch as this program where it sleeps itself + childTask = launchTest(testProgPath, "suspend-in-main", true, false); + if ( ! monitor(childTask, false, true) ) { + printf("[FAIL] dyld_process_info_notify launch other arch suspend-in-main missed some notifications\n"); + task_terminate(childTask); + exit(0); + } + task_terminate(childTask); +#endif + + // test 5) launch test program where we disconnect from it after first dlopen + childTask = launchTest(testProgPath, "", false, true); + if ( ! monitor(childTask, true, false) ) { + printf("[FAIL] dyld_process_info_notify connect/disconnect missed some notifications\n"); + task_terminate(childTask); + exit(0); + } + task_terminate(childTask); + + printf("[PASS] dyld_process_info_notify\n"); + exit(0); + }); + + dispatch_main(); +} diff --git a/src/dyld-apple/testing/test-cases/dyld_process_info_notify.dtest/target.c b/src/dyld-apple/testing/test-cases/dyld_process_info_notify.dtest/target.c new file mode 100644 index 000000000..f73e5a081 --- /dev/null +++ b/src/dyld-apple/testing/test-cases/dyld_process_info_notify.dtest/target.c @@ -0,0 +1,22 @@ +#include +#include +#include +#include +#include +#include + + + +int main(int argc, const char* argv[]) +{ + if ( (argc > 1) && (strcmp(argv[1], "suspend-in-main") == 0) ) + task_suspend(mach_task_self()); + + for (int i=0; i < 3; ++i) { + void* h = dlopen("./libfoo.dylib", 0); + dlclose(h); + } + + return 0; +} + diff --git a/src/dyld-apple/testing/test-cases/dylib-re-export-old-format.dtest/bar.c b/src/dyld-apple/testing/test-cases/dylib-re-export-old-format.dtest/bar.c new file mode 100644 index 000000000..c78b45dee --- /dev/null +++ b/src/dyld-apple/testing/test-cases/dylib-re-export-old-format.dtest/bar.c @@ -0,0 +1,5 @@ + +int bar() { + return 42; +} + diff --git a/src/dyld-apple/testing/test-cases/dylib-re-export-old-format.dtest/foo.c b/src/dyld-apple/testing/test-cases/dylib-re-export-old-format.dtest/foo.c new file mode 100644 index 000000000..54c3a28a4 --- /dev/null +++ b/src/dyld-apple/testing/test-cases/dylib-re-export-old-format.dtest/foo.c @@ -0,0 +1,3 @@ + +int foo = 1; + diff --git a/src/dyld-apple/testing/test-cases/dylib-re-export-old-format.dtest/main.c b/src/dyld-apple/testing/test-cases/dylib-re-export-old-format.dtest/main.c new file mode 100644 index 000000000..06e73a797 --- /dev/null +++ b/src/dyld-apple/testing/test-cases/dylib-re-export-old-format.dtest/main.c @@ -0,0 +1,26 @@ +// BUILD_ONLY: MacOSX +// BUILD_MIN_OS: 10.5 +// BUILD: $CC bar.c -dynamiclib -install_name $RUN_DIR/libbar.dylib -o $BUILD_DIR/libbar.dylib +// BUILD: $CC foo.c -dynamiclib $BUILD_DIR/libbar.dylib -sub_library libbar -install_name $RUN_DIR/libfoo.dylib -o $BUILD_DIR/libfoo.dylib +// BUILD: $CC main.c -o $BUILD_DIR/dylib-re-export.exe $BUILD_DIR/libfoo.dylib -L$BUILD_DIR + +// RUN: ./dylib-re-export.exe + + +#include + +extern int bar(); + + +int main() +{ + printf("[BEGIN] dylib-re-export\n"); + if ( bar() == 42 ) + printf("[PASS] dylib-re-export\n"); + else + printf("[FAIL] dylib-re-export, wrong value\n"); + + return 0; +} + + diff --git a/src/dyld-apple/testing/test-cases/dylib-re-export.dtest/bar.c b/src/dyld-apple/testing/test-cases/dylib-re-export.dtest/bar.c new file mode 100644 index 000000000..c78b45dee --- /dev/null +++ b/src/dyld-apple/testing/test-cases/dylib-re-export.dtest/bar.c @@ -0,0 +1,5 @@ + +int bar() { + return 42; +} + diff --git a/src/dyld-apple/testing/test-cases/dylib-re-export.dtest/foo.c b/src/dyld-apple/testing/test-cases/dylib-re-export.dtest/foo.c new file mode 100644 index 000000000..54c3a28a4 --- /dev/null +++ b/src/dyld-apple/testing/test-cases/dylib-re-export.dtest/foo.c @@ -0,0 +1,3 @@ + +int foo = 1; + diff --git a/src/dyld-apple/testing/test-cases/dylib-re-export.dtest/main.c b/src/dyld-apple/testing/test-cases/dylib-re-export.dtest/main.c new file mode 100644 index 000000000..879b0b611 --- /dev/null +++ b/src/dyld-apple/testing/test-cases/dylib-re-export.dtest/main.c @@ -0,0 +1,26 @@ + + +// BUILD: $CC bar.c -dynamiclib -o $BUILD_DIR/libbar.dylib -install_name $RUN_DIR/libbar.dylib +// BUILD: $CC foo.c -dynamiclib $BUILD_DIR/libbar.dylib -sub_library libbar -install_name $RUN_DIR/libfoo.dylib -o $BUILD_DIR/libfoo.dylib +// BUILD: $CC main.c -o $BUILD_DIR/dylib-re-export.exe $BUILD_DIR/libfoo.dylib -L$BUILD_DIR + +// RUN: ./dylib-re-export.exe + + +#include + +extern int bar(); + + +int main() +{ + printf("[BEGIN] dylib-re-export\n"); + if ( bar() == 42 ) + printf("[PASS] dylib-re-export\n"); + else + printf("[FAIL] dylib-re-export, wrong value\n"); + + return 0; +} + + diff --git a/src/dyld-apple/testing/test-cases/dylib-static-link.dtest/foo.c b/src/dyld-apple/testing/test-cases/dylib-static-link.dtest/foo.c new file mode 100644 index 000000000..c42b10fc3 --- /dev/null +++ b/src/dyld-apple/testing/test-cases/dylib-static-link.dtest/foo.c @@ -0,0 +1,3 @@ + +int foo = 42; + diff --git a/src/dyld-apple/testing/test-cases/dylib-static-link.dtest/missing.c b/src/dyld-apple/testing/test-cases/dylib-static-link.dtest/missing.c new file mode 100644 index 000000000..6a4a62d48 --- /dev/null +++ b/src/dyld-apple/testing/test-cases/dylib-static-link.dtest/missing.c @@ -0,0 +1,12 @@ +#include +#include + +int main() +{ + printf("[BEGIN] dylib-static-link missing\n"); + printf("[FAIL] dylib-static-link missing, program should not have launched\n"); + + return 0; +} + + diff --git a/src/dyld-apple/testing/test-cases/dylib-static-link.dtest/present.c b/src/dyld-apple/testing/test-cases/dylib-static-link.dtest/present.c new file mode 100644 index 000000000..4e7aa2db0 --- /dev/null +++ b/src/dyld-apple/testing/test-cases/dylib-static-link.dtest/present.c @@ -0,0 +1,28 @@ + + +// BUILD: $CC foo.c -dynamiclib -o $BUILD_DIR/libfoo.dylib -install_name libfoo.dylib +// BUILD: $CC present.c $BUILD_DIR/libfoo.dylib -o $BUILD_DIR/dylib-static-present.exe +// BUILD: $CC foo.c -dynamiclib -o $TEMP_DIR/libfoo2.dylib -install_name libfoomissing.dylib +// BUILD: $CC missing.c $TEMP_DIR/libfoo2.dylib -o $BUILD_DIR/dylib-static-missing.exe + +// RUN: ./dylib-static-present.exe +// RUN: NOCR_TEST_NAME="dylib-static-link missing" $REQUIRE_CRASH ./dylib-static-missing.exe + + +#include + +extern int foo; + + +int main() +{ + printf("[BEGIN] dylib-static-link present\n"); + if ( foo == 42 ) + printf("[PASS] dylib-static-link present\n"); + else + printf("[FAIL] dylib-static-link present, wrong value\n"); + + return 0; +} + + diff --git a/src/dyld-apple/testing/test-cases/dylib-static-weak-link.dtest/foo.c b/src/dyld-apple/testing/test-cases/dylib-static-weak-link.dtest/foo.c new file mode 100644 index 000000000..c42b10fc3 --- /dev/null +++ b/src/dyld-apple/testing/test-cases/dylib-static-weak-link.dtest/foo.c @@ -0,0 +1,3 @@ + +int foo = 42; + diff --git a/src/dyld-apple/testing/test-cases/dylib-static-weak-link.dtest/missing.c b/src/dyld-apple/testing/test-cases/dylib-static-weak-link.dtest/missing.c new file mode 100644 index 000000000..c6330478e --- /dev/null +++ b/src/dyld-apple/testing/test-cases/dylib-static-weak-link.dtest/missing.c @@ -0,0 +1,19 @@ +#include +#include + +extern int foo __attribute__((weak_import)); + + +int main() +{ + printf("[BEGIN] dylib-static-weak-link missing\n"); + // dylib won't be found at runtime, so &foo should be NULL + if ( &foo == NULL ) + printf("[PASS] dylib-static-weak-link missing\n"); + else + printf("[FAIL] dylib-static-weak-link missing, &foo != NULL\n"); + + return 0; +} + + diff --git a/src/dyld-apple/testing/test-cases/dylib-static-weak-link.dtest/present.c b/src/dyld-apple/testing/test-cases/dylib-static-weak-link.dtest/present.c new file mode 100644 index 000000000..1f4a6e207 --- /dev/null +++ b/src/dyld-apple/testing/test-cases/dylib-static-weak-link.dtest/present.c @@ -0,0 +1,33 @@ +// BUILD: $CC foo.c -dynamiclib -o $BUILD_DIR/libfoo.dylib -install_name libfoo.dylib +// BUILD: $CC present.c $BUILD_DIR/libfoo.dylib -o $BUILD_DIR/dylib-static-weak-present.exe +// BUILD: $CC foo.c -dynamiclib -o $TEMP_DIR/libfoo2.dylib -install_name libfoomissing.dylib +// BUILD: $CC missing.c $TEMP_DIR/libfoo2.dylib -o $BUILD_DIR/dylib-static-weak-missing.exe + +// RUN: ./dylib-static-weak-present.exe +// RUN: ./dylib-static-weak-missing.exe + + +#include +#include + +extern int foo __attribute__((weak_import)); + + +int main() +{ + printf("[BEGIN] dylib-static-weak-link present\n"); + // dylib will be found at runtime, so &foo should never be NULL + if ( &foo != NULL ) { + if ( foo == 42 ) + printf("[PASS] dylib-static-weak-link present\n"); + else + printf("[FAIL] dylib-static-weak-link present, wrong value\n"); + } + else { + printf("[FAIL] dylib-static-weak-link present, &foo == NULL\n"); + } + + return 0; +} + + diff --git a/src/dyld-apple/testing/test-cases/interpose-weak.dtest/foo.c b/src/dyld-apple/testing/test-cases/interpose-weak.dtest/foo.c new file mode 100644 index 000000000..ae32a9fc6 --- /dev/null +++ b/src/dyld-apple/testing/test-cases/interpose-weak.dtest/foo.c @@ -0,0 +1,12 @@ + + + +int foo1() { return 1; } + +int foo2() { return 2; } + +#ifndef NO_FOO34 + int foo3() { return 3; } + int foo4() { return 4; } +#endif + diff --git a/src/dyld-apple/testing/test-cases/interpose-weak.dtest/interposer.c b/src/dyld-apple/testing/test-cases/interpose-weak.dtest/interposer.c new file mode 100644 index 000000000..cf92d904b --- /dev/null +++ b/src/dyld-apple/testing/test-cases/interpose-weak.dtest/interposer.c @@ -0,0 +1,15 @@ + +#include + +extern int foo2(); +extern int foo4() __attribute__((weak_import)); + + + +int myfoo2() { return 12; } +int myfoo4() { return 14; } + + + +DYLD_INTERPOSE(myfoo2, foo2) +DYLD_INTERPOSE(myfoo4, foo4) diff --git a/src/dyld-apple/testing/test-cases/interpose-weak.dtest/main.c b/src/dyld-apple/testing/test-cases/interpose-weak.dtest/main.c new file mode 100644 index 000000000..f167a6a9c --- /dev/null +++ b/src/dyld-apple/testing/test-cases/interpose-weak.dtest/main.c @@ -0,0 +1,70 @@ +// BUILD: $CC foo.c -dynamiclib -o $BUILD_DIR/libfoo.dylib -install_name $RUN_DIR/libfoo.dylib +// BUILD: $CC main.c $BUILD_DIR/libfoo.dylib -o $BUILD_DIR/interpose-weak-present.exe +// BUILD: $CC interposer.c -dynamiclib $BUILD_DIR/libfoo.dylib -o $BUILD_DIR/libinterposer.dylib -install_name libinterposer.dylib + +// BUILD: $CC foo.c -dynamiclib -o $TEMP_DIR/libfoo2.dylib -install_name $RUN_DIR/libfoo2.dylib +// BUILD: $CC foo.c -DNO_FOO34 -dynamiclib -o $BUILD_DIR/libfoo2.dylib -install_name $RUN_DIR/libfoo2.dylib +// BUILD: $CC main.c -DNO_FOO34 $TEMP_DIR/libfoo2.dylib -o $BUILD_DIR/interpose-weak-missing.exe +// BUILD: $CC interposer.c -dynamiclib $TEMP_DIR/libfoo2.dylib -o $BUILD_DIR/libinterposer2.dylib -install_name libinterposer.dylib + + +// RUN: DYLD_INSERT_LIBRARIES=libinterposer.dylib ./interpose-weak-present.exe +// RUN: DYLD_INSERT_LIBRARIES=libinterposer2.dylib ./interpose-weak-missing.exe + + + +#include +#include +#include + + +extern int foo1(); +extern int foo2(); +extern int foo3() __attribute__((weak_import)); +extern int foo4() __attribute__((weak_import)); + +#ifndef NO_FOO34 + #define MODE "present" +#else + #define MODE "missing" +#endif + +int main() +{ + printf("[BEGIN] interpose-weak-" MODE "\n"); + + if ( foo1() != 1 ) { + printf("[FAIL] interpose-weak-" MODE ", foo1() != 1\n"); + return 0; + } + + if ( foo2() != 12 ) { + printf("[FAIL] interpose-weak-" MODE ", foo2() != 12\n"); + return 0; + } + +#ifndef NO_FOO34 + if ( foo3() != 3 ) { + printf("[FAIL] interpose-weak-" MODE ", foo3() != 3\n"); + return 0; + } + + if ( foo4() != 14 ) { + printf("[FAIL] interpose-weak-" MODE ", foo4() != 14\n"); + return 0; + } +#else + if ( &foo3 != NULL ) { + printf("[FAIL] interpose-weak-" MODE ", &foo3 != NULL\n"); + return 0; + } + + if ( &foo4 != NULL ) { + printf("[FAIL] interpose-weak-" MODE ", &foo4 != NULL\n"); + return 0; + } +#endif + + printf("[PASS] interpose-weak-" MODE "\n"); + return 0; +} diff --git a/src/dyld-apple/testing/test-cases/restrict-search.dtest/foo.c b/src/dyld-apple/testing/test-cases/restrict-search.dtest/foo.c new file mode 100644 index 000000000..c8e9924b4 --- /dev/null +++ b/src/dyld-apple/testing/test-cases/restrict-search.dtest/foo.c @@ -0,0 +1,5 @@ +int foo() +{ + return 10; +} + diff --git a/src/dyld-apple/testing/test-cases/restrict-search.dtest/main.c b/src/dyld-apple/testing/test-cases/restrict-search.dtest/main.c new file mode 100644 index 000000000..e8355ca67 --- /dev/null +++ b/src/dyld-apple/testing/test-cases/restrict-search.dtest/main.c @@ -0,0 +1,54 @@ +// BUILD_ONLY: MacOSX + +// BUILD: mkdir $BUILD_DIR/lc +// BUILD: $CC foo.c -dynamiclib -o $BUILD_DIR/lc/libfoo.dylib -install_name /blah/libfoo.dylib +// BUILD: $CC main.c $BUILD_DIR/lc/libfoo.dylib -o $BUILD_DIR/restrict-search-lc-find.exe -Wl,-dyld_env,DYLD_LIBRARY_PATH=@loader_path/lc -DMODE=lc-find -DSHOULD_BE_FOUND=1 +// BUILD: $CC main.c $BUILD_DIR/lc/libfoo.dylib -o $BUILD_DIR/restrict-search-lc-no-find.exe -Wl,-dyld_env,DYLD_LIBRARY_PATH=@loader_path/lc -DMODE=lc-no-find -sectcreate __RESTRICT __restrict /dev/null +// BUILD: mkdir $BUILD_DIR/rpath +// BUILD: $CC foo.c -dynamiclib -o $BUILD_DIR/rpath/libfoo.dylib -install_name @rpath/libfoo.dylib +// BUILD: $CC main.c $BUILD_DIR/rpath/libfoo.dylib -o $BUILD_DIR/restrict-search-rpath-find.exe -Wl,-rpath,@loader_path/rpath/ -DMODE=rpath-find -DSHOULD_BE_FOUND=1 +// BUILD: $CC main.c $BUILD_DIR/rpath/libfoo.dylib -o $BUILD_DIR/restrict-search-rpath-no-find.exe -Wl,-rpath,@loader_path/rpath/ -DMODE=rpath-no-find -sectcreate __RESTRICT __restrict /dev/null + + +// RUN: ./restrict-search-lc-find.exe +// RUN: ./restrict-search-lc-no-find.exe +// RUN: ./restrict-search-rpath-find.exe +// RUN: ./restrict-search-rpath-no-find.exe + + + +#include +#include + +// Two ways to find libfoo.dylb: @rpath or DYLD_LIBRARY_PATH (set via LC_DYLD_ENVIRONMENT) +// These should work for non-restrictured programs. +// These should fail for restricted programs. +// By weak linking we can test if libfoo.dylib was found or not. + + +extern int foo() __attribute__((weak_import)); + + +#define STRINGIFY2( x) #x +#define STRINGIFY(x) STRINGIFY2(x) + + +int main(int argc, const char* argv[]) +{ + printf("[BEGIN] restrict-search %s\n", STRINGIFY(MODE)); +#if SHOULD_BE_FOUND + if ( &foo == NULL ) + printf("[FAIL] restrict-search %s\n", STRINGIFY(MODE)); + else + printf("[PASS] restrict-search %s\n", STRINGIFY(MODE)); +#else + // dylib won't be found at runtime, so &foo should be NULL + if ( &foo == NULL ) + printf("[PASS] restrict-search %s\n", STRINGIFY(MODE)); + else + printf("[FAIL] restrict-search %s\n", STRINGIFY(MODE)); +#endif + + return 0; +} + diff --git a/src/dyld-apple/testing/test-cases/thread-local-variables.dtest/foo.c b/src/dyld-apple/testing/test-cases/thread-local-variables.dtest/foo.c new file mode 100644 index 000000000..60bbbb45a --- /dev/null +++ b/src/dyld-apple/testing/test-cases/thread-local-variables.dtest/foo.c @@ -0,0 +1,8 @@ + + + +__thread int a; +__thread int b = 5; + + + diff --git a/src/dyld-apple/testing/test-cases/thread-local-variables.dtest/main.c b/src/dyld-apple/testing/test-cases/thread-local-variables.dtest/main.c new file mode 100644 index 000000000..d7c73b2f8 --- /dev/null +++ b/src/dyld-apple/testing/test-cases/thread-local-variables.dtest/main.c @@ -0,0 +1,115 @@ + + +// BUILD: $CC foo.c -dynamiclib -o $BUILD_DIR/libfoo.dylib -install_name $RUN_DIR/libfoo.dylib +// BUILD: $CC main.c $BUILD_DIR/libfoo.dylib -o $BUILD_DIR/thread-local-variables.exe + + +// RUN: ./thread-local-variables.exe + + +#include +#include +#include +#include +#include +#include + + +extern __thread int a; +extern __thread int b; +static __thread int c; +static __thread int d = 5; + + +static int* sAddr1[4]; +static int* sAddr2[4]; +static int* sAddr3[4]; + +static pthread_t sWorker1; +static pthread_t sWorker2; + + +// verify all initial TLV values are correct +static void checkValues() +{ + assert(a == 0); + assert(b == 5); + assert(c == 0); + assert(d == 5); +} + + +// return addresses of all TLVs +static void getAddresses(int* array[]) +{ + array[0] = &a; + array[1] = &b; + array[2] = &c; + array[3] = &d; +} + +static void* work2(void* arg) +{ + checkValues(); + getAddresses(sAddr2); + + return NULL; +} + +static void* work1(void* arg) +{ + checkValues(); + + if ( pthread_create(&sWorker2, NULL, work2, NULL) != 0 ) { + printf("[FAIL] thread-local-variables, pthread_create\n"); + exit(0); + } + void* dummy; + pthread_join(sWorker2, &dummy); + + getAddresses(sAddr1); + + return NULL; +} + +static bool someMatch(int* t1, int* t2, int* t3) +{ + if ( t1 == t2 ) + return true; + if ( t1 == t3 ) + return true; + if ( t2 == t3 ) + return true; + return false; +} + +int main() +{ + printf("[BEGIN] thread-local-variables\n"); + + checkValues(); + + if ( pthread_create(&sWorker1, NULL, work1, NULL) != 0 ) { + printf("[FAIL] thread-local-variables, pthread_create\n"); + return 0; + } + + getAddresses(sAddr3); + + void* dummy; + pthread_join(sWorker1, &dummy); + + // validate each thread had different addresses for all TLVs + if ( someMatch(sAddr1[0], sAddr2[0], sAddr3[0]) ) + printf("[FAIL] thread-local-variables, &a is same across some threads\n"); + else if ( someMatch(sAddr1[1], sAddr2[1], sAddr3[1]) ) + printf("[FAIL] thread-local-variables, &b is same across some threads\n"); + else if ( someMatch(sAddr1[2], sAddr2[2], sAddr3[2]) ) + printf("[FAIL] thread-local-variables, &c is same across some threads\n"); + else if ( someMatch(sAddr1[3], sAddr2[3], sAddr3[3]) ) + printf("[FAIL] thread-local-variables, &d is same across some threads\n"); + else + printf("[PASS] thread-local-variables\n"); + return 0; +} + diff --git a/src/dyld-apple/unit-tests/bin/build-results-filter.pl b/src/dyld-apple/unit-tests/bin/build-results-filter.pl new file mode 100755 index 000000000..fa6d106b7 --- /dev/null +++ b/src/dyld-apple/unit-tests/bin/build-results-filter.pl @@ -0,0 +1,114 @@ +#!/usr/bin/perl -w + +use strict; +use Data::Dumper; +use File::Find; +use Cwd; + +$Data::Dumper::Terse = 1; + +my $root = undef; +my $entry = ''; +my $pass_count = 0; +my $total_count = 0; + +# first match "root: " + +# a line starting with "cwd:" marks the beginning of a new test case +# call process_entry() on each test case +while(<>) +{ + if(m/^root:\s+(.*?)$/) + { + $root = $1; + } + elsif(m/^cwd:\s+(.*?)$/) + { + if(length($entry)) + { + &process_entry($root, $entry); + $entry = ''; + } + $entry .= $_; + } + else + { + $entry .= $_; + } +} +# don't forget last test case (no cwd: to mark end) +if(length($entry)) +{ + &process_entry($root, $entry); +} + +# show totals +my $percentage = $pass_count * 100 / $total_count; +printf " * * * %d of %d unit-tests passed (%.1f percent) * * *\n", $pass_count, $total_count, $percentage; + + +sub process_entry +{ + my ($root, $lines) = @_; + + # build an associative array of keys to value(s) + my $lines_seq = [split /\n/, $lines]; + #print Dumper($lines_seq); + my $tbl = { 'root' => $root, 'stdout' => [], 'stderr' => [] }; + my $line; + foreach $line (@$lines_seq) + { + if($line =~ m/^(\w+):\s+(.*)$/) + { + my $key = $1; + my $val = $2; + if(!exists($$tbl{$key})) + { $$tbl{$key} = ''; } + + if($key eq 'stdout' || $key eq 'stderr') # if type is @array + { + push @{$$tbl{$key}}, $val; + } + else + { + $$tbl{$key} .= $val; + } + } + else + { + print "ERROR: $line"; + } + } + #print Dumper($tbl); + #return; + + my $test_name = $$tbl{cwd}; + if ($test_name =~ m|.*/([a-zA-Z0-9-+_]+)$|) + { + $test_name = $1; + } + + #if there was any output to stderr, mark this as a failure + my $some_errors = 0; + foreach $line (@{$$tbl{stderr}}) + { + printf "%-40s FAIL spurious stderr failure: %s\n", $test_name, $line; + $total_count++; + $some_errors = 1; + } + if ( $some_errors ) + { + return; + } + + #if make failed (exit was non-zero), mark this as a failure + if(0 ne $$tbl{exit}) + { + printf "%-40s FAIL Makefile failure\n", $test_name; + $total_count++; + return; + } + + $pass_count++; + $total_count++; +} diff --git a/src/dyld-apple/unit-tests/bin/exit-non-zero-pass.pl b/src/dyld-apple/unit-tests/bin/exit-non-zero-pass.pl new file mode 100755 index 000000000..7a09a1b53 --- /dev/null +++ b/src/dyld-apple/unit-tests/bin/exit-non-zero-pass.pl @@ -0,0 +1,44 @@ +#!/usr/bin/perl -w + +use strict; + +sub PASS +{ + my ($format, $args) = @_; + if(!defined $args) + { $args = []; } + printf("PASS \"$format\"\n", @$args); +} + +sub FAIL +{ + my ($format, $args) = @_; + if(!defined $args) + { $args = []; } + printf("FAIL \"$format\"\n", @$args); +} + +my $pass_string = shift @ARGV; +my $fail_string = shift @ARGV; + + +# redirect stderr to stdout +open(STDERR, ">/tmp/exit-non-zero.tmp") || die("$!"); +if(0 != system(@ARGV)) +{ + PASS($pass_string); +} +else +{ + FAIL($fail_string); +} +close(STDERR) || die("$!"); +open(OUT, ") +{ + print $_; +} +close(OUT) || die("$!"); +unlink "/tmp/exit-non-zero.tmp"; +exit 0; + diff --git a/src/dyld-apple/unit-tests/bin/exit-zero-pass.pl b/src/dyld-apple/unit-tests/bin/exit-zero-pass.pl new file mode 100755 index 000000000..bc0a99d85 --- /dev/null +++ b/src/dyld-apple/unit-tests/bin/exit-zero-pass.pl @@ -0,0 +1,43 @@ +#!/usr/bin/perl -w + +use strict; + +sub PASS +{ + my ($format, $args) = @_; + if(!defined $args) + { $args = []; } + printf("PASS \"$format\"\n", @$args); +} + +sub FAIL +{ + my ($format, $args) = @_; + if(!defined $args) + { $args = []; } + printf("FAIL \"$format\"\n", @$args); +} + +my $pass_string = shift @ARGV; +my $fail_string = shift @ARGV; + +# redirect stderr to stdout +open(STDERR, ">/tmp/exit-zero-pass.tmp") || die("$!"); +if(0 == system(@ARGV)) +{ + PASS($pass_string); +} +else +{ + FAIL($fail_string); +} +close(STDERR) || die("$!"); +open(OUT, ") +{ + print $_; +} +close(OUT) || die("$!"); +unlink "/tmp/exit-zero-pass.tmp"; +exit 0; + diff --git a/src/dyld-apple/unit-tests/bin/fail-if-non-zero.pl b/src/dyld-apple/unit-tests/bin/fail-if-non-zero.pl new file mode 100755 index 000000000..f34f3a159 --- /dev/null +++ b/src/dyld-apple/unit-tests/bin/fail-if-non-zero.pl @@ -0,0 +1,14 @@ +#!/usr/bin/perl -w + +use strict; + +# first arg is fail string, rest of args are command line to invoke +my $fail_string = shift @ARGV; + +if(system(@ARGV) != 0) +{ + printf("FAIL \"$fail_string\"\n"); +} + +exit 0; + diff --git a/src/dyld-apple/unit-tests/bin/make-recursive.pl b/src/dyld-apple/unit-tests/bin/make-recursive.pl new file mode 100755 index 000000000..a44135022 --- /dev/null +++ b/src/dyld-apple/unit-tests/bin/make-recursive.pl @@ -0,0 +1,120 @@ +#!/usr/bin/perl + +use strict; +use Data::Dumper; +use File::Find; +use Cwd qw(realpath); + +my @args = @ARGV; + +my $makefiles = +{ + 'makefile' => undef, + 'Makefile' => undef, +}; + +my $find_opts = +{ + 'wanted' => \&find_callback, +}; + +my $keywords = +{ + 'root' => '', + 'cwd' => '', + 'cmd' => '', + 'exit' => '', + 'stdout' => [], + 'stderr' => [], +}; + +my $keyword; +my $max_keyword_len = 0; +foreach $keyword (keys %$keywords) +{ if($max_keyword_len < length($keyword)) { $max_keyword_len = length($keyword); } } +my $delim = ':'; +$max_keyword_len += length($delim) + length(' '); + +my $last_keyword = ''; + +sub print_line +{ + my ($keyword, $val) = @_; + + if(!exists($$keywords{$keyword})) + { + print STDERR "error: keyword $keyword not in \$keywords set\n"; + exit(1); + } + + my $keyword_len = 0; + + if($keyword ne $last_keyword) + { + print("$keyword"); print($delim); + $keyword_len = length($keyword) + length($delim); + } + if($max_keyword_len > $keyword_len) + { + my $num_spaces = $max_keyword_len - $keyword_len; + print(' ' x $num_spaces); + } + print("$val"); + if(0) + { + $last_keyword = $keyword; + } +} + +my $root = '.'; +$root = &realpath($root); +print_line("root", "$root\n"); + +find($find_opts, $root); + +sub find_callback +{ + if(exists($$makefiles{$_})) + { + my $makefile = $_; + my $reldir = $File::Find::dir; + $reldir =~ s|^$root/||; + + &print_line("cwd", "\$root/$reldir\n"); + my $cmd = [ "make" ]; + + my $arg; foreach $arg (@ARGV) { push @$cmd, $arg; } # better way to do this? + &print_line("cmd", "@$cmd\n"); + + open(SAVEOUT, ">&STDOUT") || die("$!"); + open(SAVEERR, ">&STDERR") || die("$!"); + open(STDOUT, ">/tmp/unit-tests-stdout") || die("$!"); + open(STDERR, ">/tmp/unit-tests-stderr") || die("$!"); + + $ENV{UNIT_TEST_NAME} = $reldir; + my $exit = system(@$cmd); + + close(STDOUT) || die("$!"); + close(STDERR) || die("$!"); + open(STDOUT, ">&SAVEOUT") || die("$!"); + open(STDERR, ">&SAVEERR") || die("$!"); + + &print_line("exit", "$exit\n"); + + open(OUT, ") + { + &print_line("stdout", "$_"); + } + close(OUT) || die("$!"); + unlink("/tmp/unit-tests-stdout"); + + open(ERR, ") + { + &print_line("stderr", "$_"); + } + close(ERR) || die("$!"); + } + unlink("/tmp/unit-tests-stderr"); +} diff --git a/src/dyld-apple/unit-tests/bin/pass-iff-exit-zero.pl b/src/dyld-apple/unit-tests/bin/pass-iff-exit-zero.pl new file mode 100755 index 000000000..07854b554 --- /dev/null +++ b/src/dyld-apple/unit-tests/bin/pass-iff-exit-zero.pl @@ -0,0 +1,23 @@ +#!/usr/bin/perl -w + +# +# Usage: +# +# ${PASS_IFF} command +# + +use strict; + +my $test_name = ""; +if ( exists $ENV{UNIT_TEST_NAME} ) { + $test_name = $ENV{UNIT_TEST_NAME}; +} + +if(0 != system(@ARGV)) +{ + printf("FAIL $test_name\n"); + exit 1; +} + +printf("PASS $test_name\n"); +exit 0; diff --git a/src/dyld-apple/unit-tests/bin/result-filter.pl b/src/dyld-apple/unit-tests/bin/result-filter.pl new file mode 100755 index 000000000..7d6007f39 --- /dev/null +++ b/src/dyld-apple/unit-tests/bin/result-filter.pl @@ -0,0 +1,131 @@ +#!/usr/bin/perl -w + +use strict; +use Data::Dumper; +use File::Find; +use Cwd; + +$Data::Dumper::Terse = 1; + +my $root = undef; +my $entry = ''; +my $pass_count = 0; +my $total_count = 0; + +# first match "root: " + +# a line starting with "cwd:" marks the beginning of a new test case +# call process_entry() on each test case +while(<>) +{ + if(m/^root:\s+(.*?)$/) + { + $root = $1; + } + elsif(m/^cwd:\s+(.*?)$/) + { + if(length($entry)) + { + &process_entry($root, $entry); + $entry = ''; + } + $entry .= $_; + } + else + { + $entry .= $_; + } +} +# don't forget last test case (no cwd: to mark end) +if(length($entry)) +{ + &process_entry($root, $entry); +} + +# show totals +my $percentage = $pass_count * 100 / $total_count; +printf " * * * %d of %d unit-tests passed (%.1f percent) * * *\n", $pass_count, $total_count, $percentage; + + +sub process_entry +{ + my ($root, $lines) = @_; + + # build an associative array of keys to value(s) + my $lines_seq = [split /\n/, $lines]; + #print Dumper($lines_seq); + my $tbl = { 'root' => $root, 'stdout' => [], 'stderr' => [] }; + my $line; + foreach $line (@$lines_seq) + { + if($line =~ m/^(\w+):\s+(.*)$/) + { + my $key = $1; + my $val = $2; + if(!exists($$tbl{$key})) + { $$tbl{$key} = ''; } + + if($key eq 'stdout' || $key eq 'stderr') # if type is @array + { + push @{$$tbl{$key}}, $val; + } + else + { + $$tbl{$key} .= $val; + } + } + else + { + print "ERROR: $line"; + } + } + #print Dumper($tbl); + #return; + + my $test_name = $$tbl{cwd}; + if ($test_name =~ m|.*/([a-zA-Z0-9-+_]+)$|) + { + $test_name = $1; + } + + #if make failed (exit was non-zero), mark this as a failure + if(0 ne $$tbl{exit}) + { + printf "%-40s FAIL Makefile failure\n", $test_name; + $total_count++; + return; + } + my $seen_result = 0; + + #if there was any output to stderr, mark this as a failure + foreach $line (@{$$tbl{stderr}}) + { + printf "%-40s FAIL spurious stderr failure: %s\n", $test_name, $line; + $total_count++; + return; + } + + # scan all stdout looking for lines that start with PASS or FAIL + foreach $line (@{$$tbl{stdout}}) + { + if($line =~ m/^(PASS|XPASS|FAIL|XFAIL).+/) + { + $total_count++; + if($line =~ m/^PASS.+/) + { + $pass_count++; + } + else + { + # only print failure lines + printf "%-40s %s\n", $test_name, $line; + } + $seen_result = 1; + } + } + if(!$seen_result) + { + printf "%-40s AMBIGIOUS missing [X]PASS/[X]FAIL\n", $test_name; + $total_count++; + } +} diff --git a/src/dyld-apple/unit-tests/build-and-run-iPhoneOS-unit-tests b/src/dyld-apple/unit-tests/build-and-run-iPhoneOS-unit-tests new file mode 100755 index 000000000..a57291292 --- /dev/null +++ b/src/dyld-apple/unit-tests/build-and-run-iPhoneOS-unit-tests @@ -0,0 +1,14 @@ +#!/bin/sh + +# need to be root to build test suite +sudo ./build-iPhoneOS-unit-tests + +# transfer to device +echo " * * * Transfering to device * * *" +rsync -a /tmp/unpack-and-run-all-tests /tmp/dyld-testing.cpgz rsync://root@localhost:10873/root/tmp + + +# running on device +echo " * * * Running on device * * *" +/Developer/Platforms/iPhoneOS.platform/usr/local/bin/PurpleExec /tmp/unpack-and-run-all-tests + diff --git a/src/dyld-apple/unit-tests/build-iPhoneOS-unit-tests b/src/dyld-apple/unit-tests/build-iPhoneOS-unit-tests new file mode 100755 index 000000000..f597f1366 --- /dev/null +++ b/src/dyld-apple/unit-tests/build-iPhoneOS-unit-tests @@ -0,0 +1,52 @@ +#!/bin/sh + +# cd into test-cases directory +TEST_CASE_DIR=`echo "$0" | sed 's/build-iPhoneOS-unit-tests/test-cases/'` +cd ${TEST_CASE_DIR} +TEST_CASE_DIR=`pwd` + +# make clean +../bin/make-recursive.pl clean > /dev/null + +# clean staging area +rm -rf /var/root/testing +mkdir /var/root/testing + +# create scripts to run test cases on device +echo "#!/bin/sh" > /var/root/testing/run-all-tests +chmod +x /var/root/testing/run-all-tests + +# do every combination of OS version and architectures +for os in "8.0" +do + for arch in armv7 arm64 + do + # make copy of tests + cp -r ${TEST_CASE_DIR}/../../unit-tests /var/root/testing/unit-tests-${arch}-${os} + # build but don't run test cases + echo " * * * Building all unit tests for ${arch} iPhoneOS ${os} * * *" + cd /var/root/testing/unit-tests-${arch}-${os}/test-cases + ../bin/make-recursive.pl ARCH=${arch} OS_VERSION=${os} OS_NAME=iPhoneOS all | ../bin/build-results-filter.pl + # update script + echo "cd /var/root/testing/unit-tests-${arch}-${os}" >> /var/root/testing/run-all-tests + echo "echo \" * * * Running all unit tests for ${arch} iPhoneOS ${os} * * *\"" >> /var/root/testing/run-all-tests + echo "bin/make-recursive.pl OS_NAME=iPhoneOS check | bin/result-filter.pl" >> /var/root/testing/run-all-tests + done +done + +# tar up all test cases +echo " * * * Making archive * * *" +cd /var/root +ditto -c -z testing /tmp/dyld-testing.cpgz + +# create script to unpack on device +echo "#!/bin/sh" > /tmp/unpack-and-run-all-tests +echo "echo \" * * * Unpacking test cases * * *\"" >> /tmp/unpack-and-run-all-tests +echo "/sbin/mount -u /private/var" >> /tmp/unpack-and-run-all-tests +echo "chmod +x /var/root" >> /tmp/unpack-and-run-all-tests +echo "cd /var/root" >> /tmp/unpack-and-run-all-tests +echo "rm -rf /var/root/testing" >> /tmp/unpack-and-run-all-tests +echo "ditto -x /tmp/dyld-testing.cpgz testing" >> /tmp/unpack-and-run-all-tests +echo "/var/root/testing/run-all-tests" >> /tmp/unpack-and-run-all-tests +chmod +x /tmp/unpack-and-run-all-tests + diff --git a/src/dyld-apple/unit-tests/include/common.makefile b/src/dyld-apple/unit-tests/include/common.makefile new file mode 100644 index 000000000..3d6535ad8 --- /dev/null +++ b/src/dyld-apple/unit-tests/include/common.makefile @@ -0,0 +1,79 @@ +# stuff to include in every test Makefile + +SHELL = /bin/sh + +# set default OS to be current Mac OS X +OS_NAME ?= MacOSX +ifeq "$(OS_NAME)" "iPhoneOS" + OS_VERSION ?= 3.1 + ifeq "$(findstring -$(OS_VERSION)-,-3.0-3.1-3.2-4.0-4.1-4.2-4.3-)" "" + OS_LION_FEATURES = 1 + endif + ARCH ?= armv7 + VALID_ARCHS ?= armv7 +else + OS_VERSION ?= 10.7 + ifeq "$(findstring -$(OS_VERSION)-,-10.4-10.5-10.6-)" "" + OS_LION_FEATURES = 1 + endif + ARCH ?= x86_64 + VALID_ARCHS ?= "i386 x86_64" +endif + +IOSROOT = + +ifeq "$(OS_NAME)" "iPhoneOS" + #IOSROOT = $(shell xcodebuild -version -sdk iphoneos.internal Path) + IOSROOT = /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS9.0.Internal.sdk + CC = $(shell xcrun -sdk iphoneos.internal -find cc) -arch ${ARCH} -miphoneos-version-min=$(OS_VERSION) -isysroot $(IOSROOT) + CXX = $(shell xcrun -sdk iphoneos.internal -find c++) -arch ${ARCH} -miphoneos-version-min=$(OS_VERSION) -isysroot $(IOSROOT) + LIPO = $(shell xcrun -sdk iphoneos.internal -find lipo) + STRIP = $(shell xcrun -sdk iphoneos.internal -find strip) + INSTALL_NAME_TOOL = $(shell xcrun -sdk iphoneos.internal -find install_name_tool) +else + ifeq "$(OSX_SDK_ROOT)" "" + OSX_SDK_ROOT = $(shell xcodebuild -version -sdk macosx.internal Path) + endif + CC = $(shell xcrun -find cc) -arch ${ARCH} -mmacosx-version-min=$(OS_VERSION) -isysroot $(OSX_SDK_ROOT) + CXX = $(shell xcrun -find c++) -arch ${ARCH} -mmacosx-version-min=$(OS_VERSION) -isysroot $(OSX_SDK_ROOT) + LIPO = $(shell xcrun -find lipo) + STRIP = $(shell xcrun -find strip) + INSTALL_NAME_TOOL = $(shell xcrun -find install_name_tool) +endif + +CCFLAGS = -Wall -std=c99 +CXXFLAGS = -Wall + +RM = rm +RMFLAGS = -rf + +SAFE_RUN = ${TESTROOT}/bin/fail-if-non-zero.pl +PASS_IFF = ${TESTROOT}/bin/pass-iff-exit-zero.pl +PASS_IFF_FAILURE = $(TESTROOT)/bin/exit-non-zero-pass.pl + +ifeq ($(ARCH),armv7) + CCFLAGS += -mno-thumb + CXXFLAGS += -mno-thumb + override FILEARCH = arm +else + FILEARCH = $(ARCH) +endif + +ifeq ($(ARCH),thumb) + CCFLAGS += -mthumb + CXXFLAGS += -mthumb + override ARCH = armv6 + override FILEARCH = arm +else + FILEARCH = $(ARCH) +endif + +ifeq ($(ARCH),thumb2) + CCFLAGS += -mthumb + CXXFLAGS += -mthumb + override ARCH = armv7 + override FILEARCH = arm +else + FILEARCH = $(ARCH) +endif + diff --git a/src/dyld-apple/unit-tests/include/test.h b/src/dyld-apple/unit-tests/include/test.h new file mode 100644 index 000000000..faca71484 --- /dev/null +++ b/src/dyld-apple/unit-tests/include/test.h @@ -0,0 +1,35 @@ +#include +#include + +#define DEFINE_TEST_FUNC(name) \ + static \ + inline \ + void \ + name(const char *format, ...) \ + { \ + va_list args; \ + va_start(args, format); \ + common(stdout, #name, format, args); \ + va_end(args); \ + return; \ + } + +static +inline +void +common(FILE *file, const char *prefix, const char *format, va_list args) +{ + fprintf(file, "%s \"", prefix); + vfprintf(file, format, args); + fprintf(file, "\"\n"); // should check for trailing newline + return; +} + +DEFINE_TEST_FUNC(PASS); +DEFINE_TEST_FUNC(XPASS); +DEFINE_TEST_FUNC(FAIL); +DEFINE_TEST_FUNC(XFAIL); + +DEFINE_TEST_FUNC(UNTESTED); +DEFINE_TEST_FUNC(UNSUPPORTED); +DEFINE_TEST_FUNC(UNRESOLVED); diff --git a/src/dyld-apple/unit-tests/run-all-unit-tests b/src/dyld-apple/unit-tests/run-all-unit-tests new file mode 100755 index 000000000..397178c60 --- /dev/null +++ b/src/dyld-apple/unit-tests/run-all-unit-tests @@ -0,0 +1,42 @@ +#!/bin/sh + +# cd into test-cases directory +cd `echo "$0" | sed 's/run-all-unit-tests/test-cases/'` + +# save crash reporter state +CRSTATE=`defaults read com.apple.CrashReporter DialogType` +defaults write com.apple.CrashReporter DialogType basic + +SDK=`xcodebuild -version -sdk macosx.internal Path` + +# run test targeting different OS versions +for OSVERSION in 10.9 10.8 10.7 10.6 10.5 10.4 +do + echo "" + echo " * * * Running all unit tests i386 built for $OSVERSION * * *" + + # make clean + ../bin/make-recursive.pl clean > /dev/null + + # build default architecture + ../bin/make-recursive.pl ARCH="i386" OS_VERSION=$OSVERSION OS_NAME=MacOSX OSX_SDK_ROOT=${SDK} | ../bin/result-filter.pl + + # if 64-bit capable Intel, then also run all test cases for 64-bits + if [ `sysctl -n hw.optional.x86_64` = "1" ] + then + echo "" + echo " * * * Running all unit tests x86_64 built for $OSVERSION * * *" + + # make clean + ../bin/make-recursive.pl clean > /dev/null + + # build x86_64 architecture + ../bin/make-recursive.pl ARCH="x86_64" OS_VERSION=$OSVERSION OS_NAME=MacOSX OSX_SDK_ROOT=${SDK} | ../bin/result-filter.pl + fi + +done + +# restore crash reporter state +defaults write com.apple.CrashReporter DialogType ${CRSTATE} + + diff --git a/src/dyld-apple/unit-tests/test-cases/DYLD_LIBRARY_PATH-dyld_env/foo.c b/src/dyld-apple/unit-tests/test-cases/DYLD_LIBRARY_PATH-dyld_env/foo.c new file mode 100644 index 000000000..01c576d96 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/DYLD_LIBRARY_PATH-dyld_env/foo.c @@ -0,0 +1,5 @@ + +int foo() +{ + return RESULT; +} diff --git a/src/dyld-apple/unit-tests/test-cases/DYLD_LIBRARY_PATH-dyld_env/main.c b/src/dyld-apple/unit-tests/test-cases/DYLD_LIBRARY_PATH-dyld_env/main.c new file mode 100644 index 000000000..224d93e85 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/DYLD_LIBRARY_PATH-dyld_env/main.c @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2010 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include // for atoi() + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + + +extern int foo(); + +int main(int argc, const char* argv[]) +{ +#if __MAC_OS_X_VERSION_MIN_REQUIRED + int expectedResult = atoi(argv[1]); + int actualResult = foo(); + //fprintf(stderr, "foo() returned %d, expected %d\n", actualResult, expectedResult); + if ( actualResult != expectedResult ) + FAIL("DYLD_VERSIONED_LIBRARY_PATH-basic using wrong dylib. foo() returned %d, expected %d", actualResult, expectedResult); + else +#endif + PASS("DYLD_VERSIONED_LIBRARY_PATH-basic"); + + return EXIT_SUCCESS; +} + diff --git a/src/dyld-apple/unit-tests/test-cases/DYLD_VERSIONED_FRAMEWORK_PATH-basic/bar.c b/src/dyld-apple/unit-tests/test-cases/DYLD_VERSIONED_FRAMEWORK_PATH-basic/bar.c new file mode 100644 index 000000000..46849211a --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/DYLD_VERSIONED_FRAMEWORK_PATH-basic/bar.c @@ -0,0 +1,5 @@ + +int bar() +{ + return 0; +} diff --git a/src/dyld-apple/unit-tests/test-cases/DYLD_VERSIONED_FRAMEWORK_PATH-basic/foo.c b/src/dyld-apple/unit-tests/test-cases/DYLD_VERSIONED_FRAMEWORK_PATH-basic/foo.c new file mode 100644 index 000000000..01c576d96 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/DYLD_VERSIONED_FRAMEWORK_PATH-basic/foo.c @@ -0,0 +1,5 @@ + +int foo() +{ + return RESULT; +} diff --git a/src/dyld-apple/unit-tests/test-cases/DYLD_VERSIONED_FRAMEWORK_PATH-basic/main.c b/src/dyld-apple/unit-tests/test-cases/DYLD_VERSIONED_FRAMEWORK_PATH-basic/main.c new file mode 100644 index 000000000..26a1e09f8 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/DYLD_VERSIONED_FRAMEWORK_PATH-basic/main.c @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2010 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include // for atoi() +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + + +extern int foo(); + +int main(int argc, const char* argv[]) +{ + if ( argc > 2 ) { + bool found = false; + uint32_t count = _dyld_image_count(); + for(uint32_t i=0; i < count; ++i) { + const char* name = _dyld_get_image_name(i); + if ( strstr(name, argv[2]) != NULL ) + found = true; + //fprintf(stderr, "image[%d]=%s\n", i, name); + } + if ( !found ) { + FAIL("DYLD_VERSIONED_FRAMEWORK_PATH-basic dylib has wrong path"); + return EXIT_SUCCESS; + } + } + + int expectedResult = atoi(argv[1]); + int actualResult = foo(); + //fprintf(stderr, "foo() returned %d, expected %d\n", actualResult, expectedResult); + if ( actualResult != expectedResult ) + FAIL("DYLD_VERSIONED_FRAMEWORK_PATH-basic using wrong dylib. foo() returned %d, expected %d", actualResult, expectedResult); + else + PASS("DYLD_VERSIONED_FRAMEWORK_PATH-basic"); + + return EXIT_SUCCESS; +} + diff --git a/src/dyld-apple/unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-basic/bar.c b/src/dyld-apple/unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-basic/bar.c new file mode 100644 index 000000000..c57608fe5 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-basic/bar.c @@ -0,0 +1,5 @@ + +int bar() +{ + return RESULT; +} diff --git a/src/dyld-apple/unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-basic/foo.c b/src/dyld-apple/unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-basic/foo.c new file mode 100644 index 000000000..01c576d96 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-basic/foo.c @@ -0,0 +1,5 @@ + +int foo() +{ + return RESULT; +} diff --git a/src/dyld-apple/unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-basic/main.c b/src/dyld-apple/unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-basic/main.c new file mode 100644 index 000000000..a903b04b7 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-basic/main.c @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2010 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include // for atoi() + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + + +extern int foo(); + +int main(int argc, const char* argv[]) +{ + int expectedResult = atoi(argv[1]); + int actualResult = foo(); + //fprintf(stderr, "foo() returned %d, expected %d\n", actualResult, expectedResult); + if ( actualResult != expectedResult ) + FAIL("DYLD_VERSIONED_LIBRARY_PATH-basic using wrong dylib. foo() returned %d, expected %d", actualResult, expectedResult); + else + PASS("DYLD_VERSIONED_LIBRARY_PATH-basic"); + + return EXIT_SUCCESS; +} + diff --git a/src/dyld-apple/unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-dyld_env-restrict/foo.c b/src/dyld-apple/unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-dyld_env-restrict/foo.c new file mode 100644 index 000000000..01c576d96 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-dyld_env-restrict/foo.c @@ -0,0 +1,5 @@ + +int foo() +{ + return RESULT; +} diff --git a/src/dyld-apple/unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-dyld_env-restrict/main.c b/src/dyld-apple/unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-dyld_env-restrict/main.c new file mode 100644 index 000000000..a903b04b7 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-dyld_env-restrict/main.c @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2010 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include // for atoi() + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + + +extern int foo(); + +int main(int argc, const char* argv[]) +{ + int expectedResult = atoi(argv[1]); + int actualResult = foo(); + //fprintf(stderr, "foo() returned %d, expected %d\n", actualResult, expectedResult); + if ( actualResult != expectedResult ) + FAIL("DYLD_VERSIONED_LIBRARY_PATH-basic using wrong dylib. foo() returned %d, expected %d", actualResult, expectedResult); + else + PASS("DYLD_VERSIONED_LIBRARY_PATH-basic"); + + return EXIT_SUCCESS; +} + diff --git a/src/dyld-apple/unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-dyld_env/foo.c b/src/dyld-apple/unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-dyld_env/foo.c new file mode 100644 index 000000000..01c576d96 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-dyld_env/foo.c @@ -0,0 +1,5 @@ + +int foo() +{ + return RESULT; +} diff --git a/src/dyld-apple/unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-dyld_env/main.c b/src/dyld-apple/unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-dyld_env/main.c new file mode 100644 index 000000000..a903b04b7 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-dyld_env/main.c @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2010 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include // for atoi() + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + + +extern int foo(); + +int main(int argc, const char* argv[]) +{ + int expectedResult = atoi(argv[1]); + int actualResult = foo(); + //fprintf(stderr, "foo() returned %d, expected %d\n", actualResult, expectedResult); + if ( actualResult != expectedResult ) + FAIL("DYLD_VERSIONED_LIBRARY_PATH-basic using wrong dylib. foo() returned %d, expected %d", actualResult, expectedResult); + else + PASS("DYLD_VERSIONED_LIBRARY_PATH-basic"); + + return EXIT_SUCCESS; +} + diff --git a/src/dyld-apple/unit-tests/test-cases/NSAddImage-MATCH_BY_INSTALLNAME/bar.c b/src/dyld-apple/unit-tests/test-cases/NSAddImage-MATCH_BY_INSTALLNAME/bar.c new file mode 100644 index 000000000..fa1d6fe0b --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/NSAddImage-MATCH_BY_INSTALLNAME/bar.c @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ + diff --git a/src/dyld-apple/unit-tests/test-cases/NSAddImage-MATCH_BY_INSTALLNAME/foo.c b/src/dyld-apple/unit-tests/test-cases/NSAddImage-MATCH_BY_INSTALLNAME/foo.c new file mode 100644 index 000000000..fa1d6fe0b --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/NSAddImage-MATCH_BY_INSTALLNAME/foo.c @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ + diff --git a/src/dyld-apple/unit-tests/test-cases/NSAddImage-MATCH_BY_INSTALLNAME/main.c b/src/dyld-apple/unit-tests/test-cases/NSAddImage-MATCH_BY_INSTALLNAME/main.c new file mode 100644 index 000000000..bcb2bea3a --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/NSAddImage-MATCH_BY_INSTALLNAME/main.c @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2005-2009 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@ + */ +#include +#include +#include +#include + +#include "test.h" + +/// rdar://problem/4058724 + +int main() +{ +// NSAddImage is only available on Mac OS X - not iPhone OS +#if __MAC_OS_X_VERSION_MIN_REQUIRED + // test that image can be found via install path + const struct mach_header * mh1 = NSAddImage("libbar.dylib", NSADDIMAGE_OPTION_RETURN_ON_ERROR | NSADDIMAGE_OPTION_MATCH_FILENAME_BY_INSTALLNAME); + const struct mach_header * mh2 = NSAddImage("libfoo.dylib", NSADDIMAGE_OPTION_RETURN_ON_ERROR | NSADDIMAGE_OPTION_MATCH_FILENAME_BY_INSTALLNAME); + if ( mh2 == NULL ) + FAIL("NSAddImage-MATCH_BY_INSTALLNAME"); + else +#endif + PASS("NSAddImage-MATCH_BY_INSTALLNAME"); + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/NSAddImage-RETURN_ONLY_IF_LOADED/main.c b/src/dyld-apple/unit-tests/test-cases/NSAddImage-RETURN_ONLY_IF_LOADED/main.c new file mode 100644 index 000000000..3f84ea14d --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/NSAddImage-RETURN_ONLY_IF_LOADED/main.c @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2005-2009 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@ + */ +#include +#include +#include +#include + +#include "test.h" + +/// rdar://problem/3748251 + +int main() +{ +// NSAddImage is only available on Mac OS X - not iPhone OS +#if __MAC_OS_X_VERSION_MIN_REQUIRED + // test that NSAddImage() does not crash if image is not loaded + const struct mach_header * mh = NSAddImage("/System/Library/Frameworks/Cocoa.framework/Cocoa", NSADDIMAGE_OPTION_RETURN_ONLY_IF_LOADED); + if ( mh != NULL ) + FAIL("NSAddImage-RETURN_ONLY_IF_LOADED"); + else +#endif + PASS("NSAddImage-RETURN_ONLY_IF_LOADED"); + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/NSAddImage-leafname/main.c b/src/dyld-apple/unit-tests/test-cases/NSAddImage-leafname/main.c new file mode 100644 index 000000000..f1ad6308d --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/NSAddImage-leafname/main.c @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2005-2009 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@ + */ +#include +#include +#include +#include + +#include "test.h" + +/// rdar://problem/3751226 + +int main() +{ +// NSAddImage is only available on Mac OS X - not iPhone OS +#if __MAC_OS_X_VERSION_MIN_REQUIRED + // test that NSAddImage() uses fallback path when given a leaf name + const struct mach_header * mh = NSAddImage("libzzz.dylib", NSADDIMAGE_OPTION_WITH_SEARCHING); + if ( mh == NULL ) + FAIL("NSAddImage-leafname"); + else +#endif + PASS("NSAddImage-leafname"); + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/NSAddImage-leafname/zzz.c b/src/dyld-apple/unit-tests/test-cases/NSAddImage-leafname/zzz.c new file mode 100644 index 000000000..80420c76d --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/NSAddImage-leafname/zzz.c @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +void zzz() {} diff --git a/src/dyld-apple/unit-tests/test-cases/NSAddressOfSymbol-NULL/main.c b/src/dyld-apple/unit-tests/test-cases/NSAddressOfSymbol-NULL/main.c new file mode 100644 index 000000000..09fea7335 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/NSAddressOfSymbol-NULL/main.c @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2008-2009 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include + +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + + +int main() +{ +// NSAddressOfSymbol is only available on Mac OS X - not iPhone OS +#if __MAC_OS_X_VERSION_MIN_REQUIRED + NSAddressOfSymbol(NULL); +#endif + + PASS("NSAddressOfSymbol-NULL"); + return 0; +} diff --git a/src/dyld-apple/unit-tests/test-cases/absolute-symbol/abs.s b/src/dyld-apple/unit-tests/test-cases/absolute-symbol/abs.s new file mode 100644 index 000000000..235252077 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/absolute-symbol/abs.s @@ -0,0 +1,12 @@ + + .global _myAbs1 +_myAbs1 = 0 + + + + .global _myAbs2 +_myAbs2 = 1 + + + +_myLocalAbs = 3 diff --git a/src/dyld-apple/unit-tests/test-cases/absolute-symbol/foo.c b/src/dyld-apple/unit-tests/test-cases/absolute-symbol/foo.c new file mode 100644 index 000000000..8d231e09a --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/absolute-symbol/foo.c @@ -0,0 +1,5 @@ + + +int var = 5; +void func() { } + diff --git a/src/dyld-apple/unit-tests/test-cases/absolute-symbol/main.c b/src/dyld-apple/unit-tests/test-cases/absolute-symbol/main.c new file mode 100644 index 000000000..e6b61f377 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/absolute-symbol/main.c @@ -0,0 +1,31 @@ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +extern int var; +extern void func(); +extern int myAbs1 __attribute__((weak_import)); +extern int myAbs2; + + +int main() +{ + + if (&myAbs1 != 0 ) { + FAIL("absolute-symbol: &myAbs1 != 0"); + return 0; + } + + + if ((uintptr_t)&myAbs2 != 1 ) { + FAIL("absolute-symbol: &myAbs2 != 1"); + return 0; + } + + + PASS("absolute-symbol"); + return 0; +} diff --git a/src/dyld-apple/unit-tests/test-cases/addend/foo.c b/src/dyld-apple/unit-tests/test-cases/addend/foo.c new file mode 100644 index 000000000..efdd301d7 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/addend/foo.c @@ -0,0 +1,7 @@ + + +const char a = 10; +const char b = 11; +const char c = 12; +const char d = 13; +const char e = 14; diff --git a/src/dyld-apple/unit-tests/test-cases/addend/main.c b/src/dyld-apple/unit-tests/test-cases/addend/main.c new file mode 100644 index 000000000..72b371028 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/addend/main.c @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2008 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +extern const char a; +extern const char b; +extern const char c; +extern const char d; +extern const char e; + + +const char* pc = &c; +const char* pd_2 = &d - 2; +const char* pb2 = &b + 2; + +const char* pd_1234567890 = &d - 1234567890; +const char* pd1234567890 = &d + 1234567890; + +#if __LP64__ +const char* pd_12345678901234 = &d - 12345678901234; +const char* pd12345678901234 = &d + 12345678901234; +#endif + +int main() +{ + if (*pc != 12 ) { + FAIL("addend: *pc != 12"); + return 0; + } + + if (*pd_2 != 11 ) { + FAIL("addend: *pd_2 != 11"); + return 0; + } + + if (*pb2 != 13 ) { + FAIL("addend: *pb2 != 13"); + return 0; + } + + if (pd_1234567890[1234567890] != 13 ) { + FAIL("addend: pd_1234567890[1234567890] != 13"); + return 0; + } + + if (pd1234567890[-1234567890] != 13 ) { + FAIL("addend: pd1234567890[-1234567890] != 13"); + return 0; + } + +#if __LP64__ + if (pd_12345678901234[12345678901234] != 13 ) { + FAIL("addend: pd_12345678901234[12345678901234] != 13"); + return 0; + } + + if (pd12345678901234[-12345678901234] != 13 ) { + FAIL("addend: pd12345678901234[-12345678901234] != 13"); + return 0; + } +#endif + + PASS("addend"); + return 0; +} diff --git a/src/dyld-apple/unit-tests/test-cases/all_image_infos-cache-slide/main.c b/src/dyld-apple/unit-tests/test-cases/all_image_infos-cache-slide/main.c new file mode 100644 index 000000000..0a550e45e --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/all_image_infos-cache-slide/main.c @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2010 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +static uintptr_t libSystemSlide() +{ + Dl_info info; + if ( dladdr(&malloc, &info) == 0 ) { + FAIL("all_image_infos-cache-slide: dladdr(&malloc, xx) failed"); + exit(0); + } + + const struct mach_header* mallocMh = (struct mach_header*)info.dli_fbase; + if ( (mallocMh->flags & 0x80000000) == 0 ) { + FAIL("all_image_infos-cache-slide: image containing _malloc not in shared cache"); + exit(0); + } + + int count = _dyld_image_count(); + for(int i=0; i < count; ++i) { + if ( mallocMh == _dyld_get_image_header(i) ) { + return _dyld_get_image_vmaddr_slide(i); + } + } + + FAIL("all_image_infos-cache-slide: slide of image containing _malloc not found"); + exit(0); +} + + +int main(int argc, const char* argv[]) +{ + task_dyld_info_data_t task_dyld_info; + mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT; + + if ( task_info(mach_task_self(), TASK_DYLD_INFO, (task_info_t)&task_dyld_info, &count) ) { + FAIL("all_image_infos-cache-slide: task_info() failed"); + return 0; + } + struct dyld_all_image_infos* infos = (struct dyld_all_image_infos*)(uintptr_t)task_dyld_info.all_image_info_addr; + + if ( infos->version < 12 ) { + FAIL("all_image_infos-cache-slide: version < 12"); + return 0; + } + + if ( libSystemSlide() != infos->sharedCacheSlide ) { + FAIL("all_image_infos-cache-slide: dyld infos sharedCacheSlide (0x%08X) does not match computed slide (0x%08X)", + infos->sharedCacheSlide, libSystemSlide()); + return 0; + } + + PASS("all_image_infos-cache-slide"); + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/all_image_infos-duplicate/foo.c b/src/dyld-apple/unit-tests/test-cases/all_image_infos-duplicate/foo.c new file mode 100644 index 000000000..81f7dcfc9 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/all_image_infos-duplicate/foo.c @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ + + +int foo() +{ + return 10; +} diff --git a/src/dyld-apple/unit-tests/test-cases/all_image_infos-duplicate/main.c b/src/dyld-apple/unit-tests/test-cases/all_image_infos-duplicate/main.c new file mode 100644 index 000000000..b70ac9ce3 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/all_image_infos-duplicate/main.c @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2010 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +struct dyld_all_image_infos* getImageInfosFromKernel() +{ + task_dyld_info_data_t task_dyld_info; + mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT; + + if ( task_info(mach_task_self(), TASK_DYLD_INFO, (task_info_t)&task_dyld_info, &count) ) { + FAIL("all_image_infos: task_info() failed"); + exit(0); + } + return (struct dyld_all_image_infos*)(uintptr_t)task_dyld_info.all_image_info_addr; +} + + +int main(int argc, const char* argv[]) +{ + void* handle = dlopen("libfoo.dylib", RTLD_LAZY); + if ( handle == NULL ) { + FAIL("dlopen(\"%s\") failed with: %s", "libfoo.dylib", dlerror()); + exit(0); + } + + void* handle2 = dlopen("libfoodup.dylib", RTLD_LAZY); + if ( handle2 == NULL ) { + FAIL("dlopen(\"%s\") failed with: %s", "libfoodup.dylib", dlerror()); + exit(0); + } + + struct dyld_all_image_infos* infos = getImageInfosFromKernel(); + for( int i=0; i < infos->infoArrayCount; ++i) { + //fprintf(stderr, "[%d] %s\n", i , infos->infoArray[i].imageFilePath); + if ( strlen(infos->infoArray[i].imageFilePath) < 5 ) { + FAIL("all_image_infos-duplicates: bad entry for address 0x%08X", infos->infoArray[i].imageLoadAddress); + return EXIT_SUCCESS; + } + } + + PASS("all_image_infos-duplicates"); + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/all_image_infos-paths/foo.c b/src/dyld-apple/unit-tests/test-cases/all_image_infos-paths/foo.c new file mode 100644 index 000000000..81f7dcfc9 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/all_image_infos-paths/foo.c @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ + + +int foo() +{ + return 10; +} diff --git a/src/dyld-apple/unit-tests/test-cases/all_image_infos-paths/main.c b/src/dyld-apple/unit-tests/test-cases/all_image_infos-paths/main.c new file mode 100644 index 000000000..7e38b8c27 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/all_image_infos-paths/main.c @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2010 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +struct dyld_all_image_infos* getImageInfosFromKernel() +{ + task_dyld_info_data_t task_dyld_info; + mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT; + + if ( task_info(mach_task_self(), TASK_DYLD_INFO, (task_info_t)&task_dyld_info, &count) ) { + FAIL("all_image_infos: task_info() failed"); + exit(0); + } + return (struct dyld_all_image_infos*)(uintptr_t)task_dyld_info.all_image_info_addr; +} + + +int main(int argc, const char* argv[]) +{ + char path[1024]; + strcpy(path, argv[1]); + + void* handle = dlopen(path, RTLD_LAZY); + if ( handle == NULL ) { + FAIL("dlopen(\"%s\") failed with: %s", path, dlerror()); + exit(0); + } + strcpy(path, "bad"); + + struct dyld_all_image_infos* infos = getImageInfosFromKernel(); + for( int i=0; i < infos->infoArrayCount; ++i) { + //fprintf(stderr, "[%d] %s\n", i , infos->infoArray[i].imageFilePath); + if ( strcmp(infos->infoArray[i].imageFilePath, argv[1]) == 0 ) { + PASS("all_image_infos-path"); + return EXIT_SUCCESS; + } + } + + FAIL("all_image_infos-path"); + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/all_image_infos/foo.c b/src/dyld-apple/unit-tests/test-cases/all_image_infos/foo.c new file mode 100644 index 000000000..c1f5255ba --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/all_image_infos/foo.c @@ -0,0 +1,2 @@ +void foo() {} + diff --git a/src/dyld-apple/unit-tests/test-cases/all_image_infos/main.c b/src/dyld-apple/unit-tests/test-cases/all_image_infos/main.c new file mode 100644 index 000000000..efff2b893 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/all_image_infos/main.c @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2008-2009 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@ + */ +#include // EXIT_SUCCESS +#include +#include +#include +#include +#include + +#include "test.h" + +extern struct mach_header __dso_handle; + + +struct dyld_all_image_infos* getImageInfosFromKernel() +{ + task_dyld_info_data_t task_dyld_info; + mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT; + + if ( task_info(mach_task_self(), TASK_DYLD_INFO, (task_info_t)&task_dyld_info, &count) ) { + FAIL("all_image_infos: task_info() failed"); + exit(0); + } + return (struct dyld_all_image_infos*)(uintptr_t)task_dyld_info.all_image_info_addr; +} + + +int +main() +{ + struct dyld_all_image_infos* infos = getImageInfosFromKernel(); + if ( infos->version < 9 ) { + FAIL("all_image_infos: dyld_all_image_infos is not version >= 9, is %d", infos->version); + exit(0); + } + + if ( infos->infoArrayCount < 2 ) { + FAIL("all_image_infos: dyld_all_image_infos.infoArrayCount is < 2"); + exit(0); + } + + //for( int i=0; i < infos->infoArrayCount; ++i) { + // fprintf(stderr, "infos->infoArray[%d].imageLoadAddress=%p %s\n", i, infos->infoArray[i].imageLoadAddress, infos->infoArray[i].imageFilePath); + //} + + if ( infos->infoArray[0].imageLoadAddress != &__dso_handle ) { + FAIL("all_image_infos: dyld_all_image_infos.infoArray for main executable is wrong, infos->infoArray[0].imageLoadAddress=0x%08lX vs 0x%08X", + infos->infoArray[0].imageLoadAddress, &__dso_handle); + exit(0); + } + +#if __IPHONE_OS_VERSION_MIN_REQUIRED + for (const struct dyld_image_info* p = infos->infoArray; p < &infos->infoArray[infos->infoArrayCount]; ++p) { + //fprintf(stderr, "addr=0x%p, mTime=0x%lX, path=%s\n", p->imageLoadAddress, p->imageFileModDate, p->imageFilePath); + if ( (strncmp(p->imageFilePath, "/usr/lib", 8) == 0) && (p->imageFileModDate != 0) ) { + FAIL("all_image_infos, mTime not zero for cache image %s", p->imageFilePath); + exit(0); + } + } +#endif + + PASS("all_image_infos"); + return EXIT_SUCCESS; +} + + diff --git a/src/dyld-apple/unit-tests/test-cases/always-libSystem/main.c b/src/dyld-apple/unit-tests/test-cases/always-libSystem/main.c new file mode 100644 index 000000000..56267838a --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/always-libSystem/main.c @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2009 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +// +// app crashes when libSystem cannot be found +// + +int main() +{ + // see if libSystem is in list of images + uint32_t count = _dyld_image_count(); + for(uint32_t i=0; i < count; ++i) { + const char* name = _dyld_get_image_name(i); + if ( strstr(name, "/libSystem.") != NULL ) { + PASS("always-libSystem"); + return 0; + } + } + + FAIL("always-libSystem"); + return 0; +} + diff --git a/src/dyld-apple/unit-tests/test-cases/big-jump-table/foo.c b/src/dyld-apple/unit-tests/test-cases/big-jump-table/foo.c new file mode 100644 index 000000000..c1a6c6c08 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/big-jump-table/foo.c @@ -0,0 +1,827 @@ +#include "foo.h" + +void foo000() {} +void foo001() {} +void foo002() {} +void foo003() {} +void foo004() {} +void foo005() {} +void foo006() {} +void foo007() {} +void foo008() {} +void foo009() {} +void foo010() {} +void foo011() {} +void foo012() {} +void foo013() {} +void foo014() {} +void foo015() {} +void foo016() {} +void foo017() {} +void foo018() {} +void foo019() {} +void foo020() {} +void foo021() {} +void foo022() {} +void foo023() {} +void foo024() {} +void foo025() {} +void foo026() {} +void foo027() {} +void foo028() {} +void foo029() {} +void foo030() {} +void foo031() {} +void foo032() {} +void foo033() {} +void foo034() {} +void foo035() {} +void foo036() {} +void foo037() {} +void foo038() {} +void foo039() {} +void foo040() {} +void foo041() {} +void foo042() {} +void foo043() {} +void foo044() {} +void foo045() {} +void foo046() {} +void foo047() {} +void foo048() {} +void foo049() {} +void foo050() {} +void foo051() {} +void foo052() {} +void foo053() {} +void foo054() {} +void foo055() {} +void foo056() {} +void foo057() {} +void foo058() {} +void foo059() {} +void foo060() {} +void foo061() {} +void foo062() {} +void foo063() {} +void foo064() {} +void foo065() {} +void foo066() {} +void foo067() {} +void foo068() {} +void foo069() {} +void foo070() {} +void foo071() {} +void foo072() {} +void foo073() {} +void foo074() {} +void foo075() {} +void foo076() {} +void foo077() {} +void foo078() {} +void foo079() {} +void foo080() {} +void foo081() {} +void foo082() {} +void foo083() {} +void foo084() {} +void foo085() {} +void foo086() {} +void foo087() {} +void foo088() {} +void foo089() {} +void foo090() {} +void foo091() {} +void foo092() {} +void foo093() {} +void foo094() {} +void foo095() {} +void foo096() {} +void foo097() {} +void foo098() {} +void foo099() {} +void foo100() {} +void foo101() {} +void foo102() {} +void foo103() {} +void foo104() {} +void foo105() {} +void foo106() {} +void foo107() {} +void foo108() {} +void foo109() {} +void foo110() {} +void foo111() {} +void foo112() {} +void foo113() {} +void foo114() {} +void foo115() {} +void foo116() {} +void foo117() {} +void foo118() {} +void foo119() {} +void foo120() {} +void foo121() {} +void foo122() {} +void foo123() {} +void foo124() {} +void foo125() {} +void foo126() {} +void foo127() {} +void foo128() {} +void foo129() {} +void foo130() {} +void foo131() {} +void foo132() {} +void foo133() {} +void foo134() {} +void foo135() {} +void foo136() {} +void foo137() {} +void foo138() {} +void foo139() {} +void foo140() {} +void foo141() {} +void foo142() {} +void foo143() {} +void foo144() {} +void foo145() {} +void foo146() {} +void foo147() {} +void foo148() {} +void foo149() {} +void foo150() {} +void foo151() {} +void foo152() {} +void foo153() {} +void foo154() {} +void foo155() {} +void foo156() {} +void foo157() {} +void foo158() {} +void foo159() {} +void foo160() {} +void foo161() {} +void foo162() {} +void foo163() {} +void foo164() {} +void foo165() {} +void foo166() {} +void foo167() {} +void foo168() {} +void foo169() {} +void foo170() {} +void foo171() {} +void foo172() {} +void foo173() {} +void foo174() {} +void foo175() {} +void foo176() {} +void foo177() {} +void foo178() {} +void foo179() {} +void foo180() {} +void foo181() {} +void foo182() {} +void foo183() {} +void foo184() {} +void foo185() {} +void foo186() {} +void foo187() {} +void foo188() {} +void foo189() {} +void foo190() {} +void foo191() {} +void foo192() {} +void foo193() {} +void foo194() {} +void foo195() {} +void foo196() {} +void foo197() {} +void foo198() {} +void foo199() {} +void foo200() {} +void foo201() {} +void foo202() {} +void foo203() {} +void foo204() {} +void foo205() {} +void foo206() {} +void foo207() {} +void foo208() {} +void foo209() {} +void foo210() {} +void foo211() {} +void foo212() {} +void foo213() {} +void foo214() {} +void foo215() {} +void foo216() {} +void foo217() {} +void foo218() {} +void foo219() {} +void foo220() {} +void foo221() {} +void foo222() {} +void foo223() {} +void foo224() {} +void foo225() {} +void foo226() {} +void foo227() {} +void foo228() {} +void foo229() {} +void foo230() {} +void foo231() {} +void foo232() {} +void foo233() {} +void foo234() {} +void foo235() {} +void foo236() {} +void foo237() {} +void foo238() {} +void foo239() {} +void foo240() {} +void foo241() {} +void foo242() {} +void foo243() {} +void foo244() {} +void foo245() {} +void foo246() {} +void foo247() {} +void foo248() {} +void foo249() {} +void foo250() {} +void foo251() {} +void foo252() {} +void foo253() {} +void foo254() {} +void foo255() {} +void foo256() {} +void foo257() {} +void foo258() {} +void foo259() {} +void foo260() {} +void foo261() {} +void foo262() {} +void foo263() {} +void foo264() {} +void foo265() {} +void foo266() {} +void foo267() {} +void foo268() {} +void foo269() {} +void foo270() {} +void foo271() {} +void foo272() {} +void foo273() {} +void foo274() {} +void foo275() {} +void foo276() {} +void foo277() {} +void foo278() {} +void foo279() {} +void foo280() {} +void foo281() {} +void foo282() {} +void foo283() {} +void foo284() {} +void foo285() {} +void foo286() {} +void foo287() {} +void foo288() {} +void foo289() {} +void foo290() {} +void foo291() {} +void foo292() {} +void foo293() {} +void foo294() {} +void foo295() {} +void foo296() {} +void foo297() {} +void foo298() {} +void foo299() {} +void foo300() {} +void foo301() {} +void foo302() {} +void foo303() {} +void foo304() {} +void foo305() {} +void foo306() {} +void foo307() {} +void foo308() {} +void foo309() {} +void foo310() {} +void foo311() {} +void foo312() {} +void foo313() {} +void foo314() {} +void foo315() {} +void foo316() {} +void foo317() {} +void foo318() {} +void foo319() {} +void foo320() {} +void foo321() {} +void foo322() {} +void foo323() {} +void foo324() {} +void foo325() {} +void foo326() {} +void foo327() {} +void foo328() {} +void foo329() {} +void foo330() {} +void foo331() {} +void foo332() {} +void foo333() {} +void foo334() {} +void foo335() {} +void foo336() {} +void foo337() {} +void foo338() {} +void foo339() {} +void foo340() {} +void foo341() {} +void foo342() {} +void foo343() {} +void foo344() {} +void foo345() {} +void foo346() {} +void foo347() {} +void foo348() {} +void foo349() {} +void foo350() {} +void foo351() {} +void foo352() {} +void foo353() {} +void foo354() {} +void foo355() {} +void foo356() {} +void foo357() {} +void foo358() {} +void foo359() {} +void foo360() {} +void foo361() {} +void foo362() {} +void foo363() {} +void foo364() {} +void foo365() {} +void foo366() {} +void foo367() {} +void foo368() {} +void foo369() {} +void foo370() {} +void foo371() {} +void foo372() {} +void foo373() {} +void foo374() {} +void foo375() {} +void foo376() {} +void foo377() {} +void foo378() {} +void foo379() {} +void foo380() {} +void foo381() {} +void foo382() {} +void foo383() {} +void foo384() {} +void foo385() {} +void foo386() {} +void foo387() {} +void foo388() {} +void foo389() {} +void foo390() {} +void foo391() {} +void foo392() {} +void foo393() {} +void foo394() {} +void foo395() {} +void foo396() {} +void foo397() {} +void foo398() {} +void foo399() {} +void foo400() {} +void foo401() {} +void foo402() {} +void foo403() {} +void foo404() {} +void foo405() {} +void foo406() {} +void foo407() {} +void foo408() {} +void foo409() {} +void foo410() {} +void foo411() {} +void foo412() {} +void foo413() {} +void foo414() {} +void foo415() {} +void foo416() {} +void foo417() {} +void foo418() {} +void foo419() {} +void foo420() {} +void foo421() {} +void foo422() {} +void foo423() {} +void foo424() {} +void foo425() {} +void foo426() {} +void foo427() {} +void foo428() {} +void foo429() {} +void foo430() {} +void foo431() {} +void foo432() {} +void foo433() {} +void foo434() {} +void foo435() {} +void foo436() {} +void foo437() {} +void foo438() {} +void foo439() {} +void foo440() {} +void foo441() {} +void foo442() {} +void foo443() {} +void foo444() {} +void foo445() {} +void foo446() {} +void foo447() {} +void foo448() {} +void foo449() {} +void foo450() {} +void foo451() {} +void foo452() {} +void foo453() {} +void foo454() {} +void foo455() {} +void foo456() {} +void foo457() {} +void foo458() {} +void foo459() {} +void foo460() {} +void foo461() {} +void foo462() {} +void foo463() {} +void foo464() {} +void foo465() {} +void foo466() {} +void foo467() {} +void foo468() {} +void foo469() {} +void foo470() {} +void foo471() {} +void foo472() {} +void foo473() {} +void foo474() {} +void foo475() {} +void foo476() {} +void foo477() {} +void foo478() {} +void foo479() {} +void foo480() {} +void foo481() {} +void foo482() {} +void foo483() {} +void foo484() {} +void foo485() {} +void foo486() {} +void foo487() {} +void foo488() {} +void foo489() {} +void foo490() {} +void foo491() {} +void foo492() {} +void foo493() {} +void foo494() {} +void foo495() {} +void foo496() {} +void foo497() {} +void foo498() {} +void foo499() {} +void foo500() {} +void foo501() {} +void foo502() {} +void foo503() {} +void foo504() {} +void foo505() {} +void foo506() {} +void foo507() {} +void foo508() {} +void foo509() {} +void foo510() {} +void foo511() {} +void foo512() {} +void foo513() {} +void foo514() {} +void foo515() {} +void foo516() {} +void foo517() {} +void foo518() {} +void foo519() {} +void foo520() {} +void foo521() {} +void foo522() {} +void foo523() {} +void foo524() {} +void foo525() {} +void foo526() {} +void foo527() {} +void foo528() {} +void foo529() {} +void foo530() {} +void foo531() {} +void foo532() {} +void foo533() {} +void foo534() {} +void foo535() {} +void foo536() {} +void foo537() {} +void foo538() {} +void foo539() {} +void foo540() {} +void foo541() {} +void foo542() {} +void foo543() {} +void foo544() {} +void foo545() {} +void foo546() {} +void foo547() {} +void foo548() {} +void foo549() {} +void foo550() {} +void foo551() {} +void foo552() {} +void foo553() {} +void foo554() {} +void foo555() {} +void foo556() {} +void foo557() {} +void foo558() {} +void foo559() {} +void foo560() {} +void foo561() {} +void foo562() {} +void foo563() {} +void foo564() {} +void foo565() {} +void foo566() {} +void foo567() {} +void foo568() {} +void foo569() {} +void foo570() {} +void foo571() {} +void foo572() {} +void foo573() {} +void foo574() {} +void foo575() {} +void foo576() {} +void foo577() {} +void foo578() {} +void foo579() {} +void foo580() {} +void foo581() {} +void foo582() {} +void foo583() {} +void foo584() {} +void foo585() {} +void foo586() {} +void foo587() {} +void foo588() {} +void foo589() {} +void foo590() {} +void foo591() {} +void foo592() {} +void foo593() {} +void foo594() {} +void foo595() {} +void foo596() {} +void foo597() {} +void foo598() {} +void foo599() {} +void foo600() {} +void foo601() {} +void foo602() {} +void foo603() {} +void foo604() {} +void foo605() {} +void foo606() {} +void foo607() {} +void foo608() {} +void foo609() {} +void foo610() {} +void foo611() {} +void foo612() {} +void foo613() {} +void foo614() {} +void foo615() {} +void foo616() {} +void foo617() {} +void foo618() {} +void foo619() {} +void foo620() {} +void foo621() {} +void foo622() {} +void foo623() {} +void foo624() {} +void foo625() {} +void foo626() {} +void foo627() {} +void foo628() {} +void foo629() {} +void foo630() {} +void foo631() {} +void foo632() {} +void foo633() {} +void foo634() {} +void foo635() {} +void foo636() {} +void foo637() {} +void foo638() {} +void foo639() {} +void foo640() {} +void foo641() {} +void foo642() {} +void foo643() {} +void foo644() {} +void foo645() {} +void foo646() {} +void foo647() {} +void foo648() {} +void foo649() {} +void foo650() {} +void foo651() {} +void foo652() {} +void foo653() {} +void foo654() {} +void foo655() {} +void foo656() {} +void foo657() {} +void foo658() {} +void foo659() {} +void foo660() {} +void foo661() {} +void foo662() {} +void foo663() {} +void foo664() {} +void foo665() {} +void foo666() {} +void foo667() {} +void foo668() {} +void foo669() {} +void foo670() {} +void foo671() {} +void foo672() {} +void foo673() {} +void foo674() {} +void foo675() {} +void foo676() {} +void foo677() {} +void foo678() {} +void foo679() {} +void foo680() {} +void foo681() {} +void foo682() {} +void foo683() {} +void foo684() {} +void foo685() {} +void foo686() {} +void foo687() {} +void foo688() {} +void foo689() {} +void foo690() {} +void foo691() {} +void foo692() {} +void foo693() {} +void foo694() {} +void foo695() {} +void foo696() {} +void foo697() {} +void foo698() {} +void foo699() {} +void foo700() {} +void foo701() {} +void foo702() {} +void foo703() {} +void foo704() {} +void foo705() {} +void foo706() {} +void foo707() {} +void foo708() {} +void foo709() {} +void foo710() {} +void foo711() {} +void foo712() {} +void foo713() {} +void foo714() {} +void foo715() {} +void foo716() {} +void foo717() {} +void foo718() {} +void foo719() {} +void foo720() {} +void foo721() {} +void foo722() {} +void foo723() {} +void foo724() {} +void foo725() {} +void foo726() {} +void foo727() {} +void foo728() {} +void foo729() {} +void foo730() {} +void foo731() {} +void foo732() {} +void foo733() {} +void foo734() {} +void foo735() {} +void foo736() {} +void foo737() {} +void foo738() {} +void foo739() {} +void foo740() {} +void foo741() {} +void foo742() {} +void foo743() {} +void foo744() {} +void foo745() {} +void foo746() {} +void foo747() {} +void foo748() {} +void foo749() {} +void foo750() {} +void foo751() {} +void foo752() {} +void foo753() {} +void foo754() {} +void foo755() {} +void foo756() {} +void foo757() {} +void foo758() {} +void foo759() {} +void foo760() {} +void foo761() {} +void foo762() {} +void foo763() {} +void foo764() {} +void foo765() {} +void foo766() {} +void foo767() {} +void foo768() {} +void foo769() {} +void foo770() {} +void foo771() {} +void foo772() {} +void foo773() {} +void foo774() {} +void foo775() {} +void foo776() {} +void foo777() {} +void foo778() {} +void foo779() {} +void foo780() {} +void foo781() {} +void foo782() {} +void foo783() {} +void foo784() {} +void foo785() {} +void foo786() {} +void foo787() {} +void foo788() {} +void foo789() {} +void foo790() {} +void foo791() {} +void foo792() {} +void foo793() {} +void foo794() {} +void foo795() {} +void foo796() {} +void foo797() {} +void foo798() {} +void foo799() {} +void foo800() {} +void foo801() {} +void foo802() {} +void foo803() {} +void foo804() {} +void foo805() {} +void foo806() {} +void foo807() {} +void foo808() {} +void foo809() {} +void foo810() {} +void foo811() {} +void foo812() {} +void foo813() {} +void foo814() {} +void foo815() {} +void foo816() {} +void foo817() {} +void foo818() {} +void foo819() {} +void foo820() {} +void foo821() {} +void foo822() {} +void foo823() {} +void foo824() {} diff --git a/src/dyld-apple/unit-tests/test-cases/big-jump-table/foo.h b/src/dyld-apple/unit-tests/test-cases/big-jump-table/foo.h new file mode 100644 index 000000000..e9b155b72 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/big-jump-table/foo.h @@ -0,0 +1,825 @@ +extern void foo000(); +extern void foo001(); +extern void foo002(); +extern void foo003(); +extern void foo004(); +extern void foo005(); +extern void foo006(); +extern void foo007(); +extern void foo008(); +extern void foo009(); +extern void foo010(); +extern void foo011(); +extern void foo012(); +extern void foo013(); +extern void foo014(); +extern void foo015(); +extern void foo016(); +extern void foo017(); +extern void foo018(); +extern void foo019(); +extern void foo020(); +extern void foo021(); +extern void foo022(); +extern void foo023(); +extern void foo024(); +extern void foo025(); +extern void foo026(); +extern void foo027(); +extern void foo028(); +extern void foo029(); +extern void foo030(); +extern void foo031(); +extern void foo032(); +extern void foo033(); +extern void foo034(); +extern void foo035(); +extern void foo036(); +extern void foo037(); +extern void foo038(); +extern void foo039(); +extern void foo040(); +extern void foo041(); +extern void foo042(); +extern void foo043(); +extern void foo044(); +extern void foo045(); +extern void foo046(); +extern void foo047(); +extern void foo048(); +extern void foo049(); +extern void foo050(); +extern void foo051(); +extern void foo052(); +extern void foo053(); +extern void foo054(); +extern void foo055(); +extern void foo056(); +extern void foo057(); +extern void foo058(); +extern void foo059(); +extern void foo060(); +extern void foo061(); +extern void foo062(); +extern void foo063(); +extern void foo064(); +extern void foo065(); +extern void foo066(); +extern void foo067(); +extern void foo068(); +extern void foo069(); +extern void foo070(); +extern void foo071(); +extern void foo072(); +extern void foo073(); +extern void foo074(); +extern void foo075(); +extern void foo076(); +extern void foo077(); +extern void foo078(); +extern void foo079(); +extern void foo080(); +extern void foo081(); +extern void foo082(); +extern void foo083(); +extern void foo084(); +extern void foo085(); +extern void foo086(); +extern void foo087(); +extern void foo088(); +extern void foo089(); +extern void foo090(); +extern void foo091(); +extern void foo092(); +extern void foo093(); +extern void foo094(); +extern void foo095(); +extern void foo096(); +extern void foo097(); +extern void foo098(); +extern void foo099(); +extern void foo100(); +extern void foo101(); +extern void foo102(); +extern void foo103(); +extern void foo104(); +extern void foo105(); +extern void foo106(); +extern void foo107(); +extern void foo108(); +extern void foo109(); +extern void foo110(); +extern void foo111(); +extern void foo112(); +extern void foo113(); +extern void foo114(); +extern void foo115(); +extern void foo116(); +extern void foo117(); +extern void foo118(); +extern void foo119(); +extern void foo120(); +extern void foo121(); +extern void foo122(); +extern void foo123(); +extern void foo124(); +extern void foo125(); +extern void foo126(); +extern void foo127(); +extern void foo128(); +extern void foo129(); +extern void foo130(); +extern void foo131(); +extern void foo132(); +extern void foo133(); +extern void foo134(); +extern void foo135(); +extern void foo136(); +extern void foo137(); +extern void foo138(); +extern void foo139(); +extern void foo140(); +extern void foo141(); +extern void foo142(); +extern void foo143(); +extern void foo144(); +extern void foo145(); +extern void foo146(); +extern void foo147(); +extern void foo148(); +extern void foo149(); +extern void foo150(); +extern void foo151(); +extern void foo152(); +extern void foo153(); +extern void foo154(); +extern void foo155(); +extern void foo156(); +extern void foo157(); +extern void foo158(); +extern void foo159(); +extern void foo160(); +extern void foo161(); +extern void foo162(); +extern void foo163(); +extern void foo164(); +extern void foo165(); +extern void foo166(); +extern void foo167(); +extern void foo168(); +extern void foo169(); +extern void foo170(); +extern void foo171(); +extern void foo172(); +extern void foo173(); +extern void foo174(); +extern void foo175(); +extern void foo176(); +extern void foo177(); +extern void foo178(); +extern void foo179(); +extern void foo180(); +extern void foo181(); +extern void foo182(); +extern void foo183(); +extern void foo184(); +extern void foo185(); +extern void foo186(); +extern void foo187(); +extern void foo188(); +extern void foo189(); +extern void foo190(); +extern void foo191(); +extern void foo192(); +extern void foo193(); +extern void foo194(); +extern void foo195(); +extern void foo196(); +extern void foo197(); +extern void foo198(); +extern void foo199(); +extern void foo200(); +extern void foo201(); +extern void foo202(); +extern void foo203(); +extern void foo204(); +extern void foo205(); +extern void foo206(); +extern void foo207(); +extern void foo208(); +extern void foo209(); +extern void foo210(); +extern void foo211(); +extern void foo212(); +extern void foo213(); +extern void foo214(); +extern void foo215(); +extern void foo216(); +extern void foo217(); +extern void foo218(); +extern void foo219(); +extern void foo220(); +extern void foo221(); +extern void foo222(); +extern void foo223(); +extern void foo224(); +extern void foo225(); +extern void foo226(); +extern void foo227(); +extern void foo228(); +extern void foo229(); +extern void foo230(); +extern void foo231(); +extern void foo232(); +extern void foo233(); +extern void foo234(); +extern void foo235(); +extern void foo236(); +extern void foo237(); +extern void foo238(); +extern void foo239(); +extern void foo240(); +extern void foo241(); +extern void foo242(); +extern void foo243(); +extern void foo244(); +extern void foo245(); +extern void foo246(); +extern void foo247(); +extern void foo248(); +extern void foo249(); +extern void foo250(); +extern void foo251(); +extern void foo252(); +extern void foo253(); +extern void foo254(); +extern void foo255(); +extern void foo256(); +extern void foo257(); +extern void foo258(); +extern void foo259(); +extern void foo260(); +extern void foo261(); +extern void foo262(); +extern void foo263(); +extern void foo264(); +extern void foo265(); +extern void foo266(); +extern void foo267(); +extern void foo268(); +extern void foo269(); +extern void foo270(); +extern void foo271(); +extern void foo272(); +extern void foo273(); +extern void foo274(); +extern void foo275(); +extern void foo276(); +extern void foo277(); +extern void foo278(); +extern void foo279(); +extern void foo280(); +extern void foo281(); +extern void foo282(); +extern void foo283(); +extern void foo284(); +extern void foo285(); +extern void foo286(); +extern void foo287(); +extern void foo288(); +extern void foo289(); +extern void foo290(); +extern void foo291(); +extern void foo292(); +extern void foo293(); +extern void foo294(); +extern void foo295(); +extern void foo296(); +extern void foo297(); +extern void foo298(); +extern void foo299(); +extern void foo300(); +extern void foo301(); +extern void foo302(); +extern void foo303(); +extern void foo304(); +extern void foo305(); +extern void foo306(); +extern void foo307(); +extern void foo308(); +extern void foo309(); +extern void foo310(); +extern void foo311(); +extern void foo312(); +extern void foo313(); +extern void foo314(); +extern void foo315(); +extern void foo316(); +extern void foo317(); +extern void foo318(); +extern void foo319(); +extern void foo320(); +extern void foo321(); +extern void foo322(); +extern void foo323(); +extern void foo324(); +extern void foo325(); +extern void foo326(); +extern void foo327(); +extern void foo328(); +extern void foo329(); +extern void foo330(); +extern void foo331(); +extern void foo332(); +extern void foo333(); +extern void foo334(); +extern void foo335(); +extern void foo336(); +extern void foo337(); +extern void foo338(); +extern void foo339(); +extern void foo340(); +extern void foo341(); +extern void foo342(); +extern void foo343(); +extern void foo344(); +extern void foo345(); +extern void foo346(); +extern void foo347(); +extern void foo348(); +extern void foo349(); +extern void foo350(); +extern void foo351(); +extern void foo352(); +extern void foo353(); +extern void foo354(); +extern void foo355(); +extern void foo356(); +extern void foo357(); +extern void foo358(); +extern void foo359(); +extern void foo360(); +extern void foo361(); +extern void foo362(); +extern void foo363(); +extern void foo364(); +extern void foo365(); +extern void foo366(); +extern void foo367(); +extern void foo368(); +extern void foo369(); +extern void foo370(); +extern void foo371(); +extern void foo372(); +extern void foo373(); +extern void foo374(); +extern void foo375(); +extern void foo376(); +extern void foo377(); +extern void foo378(); +extern void foo379(); +extern void foo380(); +extern void foo381(); +extern void foo382(); +extern void foo383(); +extern void foo384(); +extern void foo385(); +extern void foo386(); +extern void foo387(); +extern void foo388(); +extern void foo389(); +extern void foo390(); +extern void foo391(); +extern void foo392(); +extern void foo393(); +extern void foo394(); +extern void foo395(); +extern void foo396(); +extern void foo397(); +extern void foo398(); +extern void foo399(); +extern void foo400(); +extern void foo401(); +extern void foo402(); +extern void foo403(); +extern void foo404(); +extern void foo405(); +extern void foo406(); +extern void foo407(); +extern void foo408(); +extern void foo409(); +extern void foo410(); +extern void foo411(); +extern void foo412(); +extern void foo413(); +extern void foo414(); +extern void foo415(); +extern void foo416(); +extern void foo417(); +extern void foo418(); +extern void foo419(); +extern void foo420(); +extern void foo421(); +extern void foo422(); +extern void foo423(); +extern void foo424(); +extern void foo425(); +extern void foo426(); +extern void foo427(); +extern void foo428(); +extern void foo429(); +extern void foo430(); +extern void foo431(); +extern void foo432(); +extern void foo433(); +extern void foo434(); +extern void foo435(); +extern void foo436(); +extern void foo437(); +extern void foo438(); +extern void foo439(); +extern void foo440(); +extern void foo441(); +extern void foo442(); +extern void foo443(); +extern void foo444(); +extern void foo445(); +extern void foo446(); +extern void foo447(); +extern void foo448(); +extern void foo449(); +extern void foo450(); +extern void foo451(); +extern void foo452(); +extern void foo453(); +extern void foo454(); +extern void foo455(); +extern void foo456(); +extern void foo457(); +extern void foo458(); +extern void foo459(); +extern void foo460(); +extern void foo461(); +extern void foo462(); +extern void foo463(); +extern void foo464(); +extern void foo465(); +extern void foo466(); +extern void foo467(); +extern void foo468(); +extern void foo469(); +extern void foo470(); +extern void foo471(); +extern void foo472(); +extern void foo473(); +extern void foo474(); +extern void foo475(); +extern void foo476(); +extern void foo477(); +extern void foo478(); +extern void foo479(); +extern void foo480(); +extern void foo481(); +extern void foo482(); +extern void foo483(); +extern void foo484(); +extern void foo485(); +extern void foo486(); +extern void foo487(); +extern void foo488(); +extern void foo489(); +extern void foo490(); +extern void foo491(); +extern void foo492(); +extern void foo493(); +extern void foo494(); +extern void foo495(); +extern void foo496(); +extern void foo497(); +extern void foo498(); +extern void foo499(); +extern void foo500(); +extern void foo501(); +extern void foo502(); +extern void foo503(); +extern void foo504(); +extern void foo505(); +extern void foo506(); +extern void foo507(); +extern void foo508(); +extern void foo509(); +extern void foo510(); +extern void foo511(); +extern void foo512(); +extern void foo513(); +extern void foo514(); +extern void foo515(); +extern void foo516(); +extern void foo517(); +extern void foo518(); +extern void foo519(); +extern void foo520(); +extern void foo521(); +extern void foo522(); +extern void foo523(); +extern void foo524(); +extern void foo525(); +extern void foo526(); +extern void foo527(); +extern void foo528(); +extern void foo529(); +extern void foo530(); +extern void foo531(); +extern void foo532(); +extern void foo533(); +extern void foo534(); +extern void foo535(); +extern void foo536(); +extern void foo537(); +extern void foo538(); +extern void foo539(); +extern void foo540(); +extern void foo541(); +extern void foo542(); +extern void foo543(); +extern void foo544(); +extern void foo545(); +extern void foo546(); +extern void foo547(); +extern void foo548(); +extern void foo549(); +extern void foo550(); +extern void foo551(); +extern void foo552(); +extern void foo553(); +extern void foo554(); +extern void foo555(); +extern void foo556(); +extern void foo557(); +extern void foo558(); +extern void foo559(); +extern void foo560(); +extern void foo561(); +extern void foo562(); +extern void foo563(); +extern void foo564(); +extern void foo565(); +extern void foo566(); +extern void foo567(); +extern void foo568(); +extern void foo569(); +extern void foo570(); +extern void foo571(); +extern void foo572(); +extern void foo573(); +extern void foo574(); +extern void foo575(); +extern void foo576(); +extern void foo577(); +extern void foo578(); +extern void foo579(); +extern void foo580(); +extern void foo581(); +extern void foo582(); +extern void foo583(); +extern void foo584(); +extern void foo585(); +extern void foo586(); +extern void foo587(); +extern void foo588(); +extern void foo589(); +extern void foo590(); +extern void foo591(); +extern void foo592(); +extern void foo593(); +extern void foo594(); +extern void foo595(); +extern void foo596(); +extern void foo597(); +extern void foo598(); +extern void foo599(); +extern void foo600(); +extern void foo601(); +extern void foo602(); +extern void foo603(); +extern void foo604(); +extern void foo605(); +extern void foo606(); +extern void foo607(); +extern void foo608(); +extern void foo609(); +extern void foo610(); +extern void foo611(); +extern void foo612(); +extern void foo613(); +extern void foo614(); +extern void foo615(); +extern void foo616(); +extern void foo617(); +extern void foo618(); +extern void foo619(); +extern void foo620(); +extern void foo621(); +extern void foo622(); +extern void foo623(); +extern void foo624(); +extern void foo625(); +extern void foo626(); +extern void foo627(); +extern void foo628(); +extern void foo629(); +extern void foo630(); +extern void foo631(); +extern void foo632(); +extern void foo633(); +extern void foo634(); +extern void foo635(); +extern void foo636(); +extern void foo637(); +extern void foo638(); +extern void foo639(); +extern void foo640(); +extern void foo641(); +extern void foo642(); +extern void foo643(); +extern void foo644(); +extern void foo645(); +extern void foo646(); +extern void foo647(); +extern void foo648(); +extern void foo649(); +extern void foo650(); +extern void foo651(); +extern void foo652(); +extern void foo653(); +extern void foo654(); +extern void foo655(); +extern void foo656(); +extern void foo657(); +extern void foo658(); +extern void foo659(); +extern void foo660(); +extern void foo661(); +extern void foo662(); +extern void foo663(); +extern void foo664(); +extern void foo665(); +extern void foo666(); +extern void foo667(); +extern void foo668(); +extern void foo669(); +extern void foo670(); +extern void foo671(); +extern void foo672(); +extern void foo673(); +extern void foo674(); +extern void foo675(); +extern void foo676(); +extern void foo677(); +extern void foo678(); +extern void foo679(); +extern void foo680(); +extern void foo681(); +extern void foo682(); +extern void foo683(); +extern void foo684(); +extern void foo685(); +extern void foo686(); +extern void foo687(); +extern void foo688(); +extern void foo689(); +extern void foo690(); +extern void foo691(); +extern void foo692(); +extern void foo693(); +extern void foo694(); +extern void foo695(); +extern void foo696(); +extern void foo697(); +extern void foo698(); +extern void foo699(); +extern void foo700(); +extern void foo701(); +extern void foo702(); +extern void foo703(); +extern void foo704(); +extern void foo705(); +extern void foo706(); +extern void foo707(); +extern void foo708(); +extern void foo709(); +extern void foo710(); +extern void foo711(); +extern void foo712(); +extern void foo713(); +extern void foo714(); +extern void foo715(); +extern void foo716(); +extern void foo717(); +extern void foo718(); +extern void foo719(); +extern void foo720(); +extern void foo721(); +extern void foo722(); +extern void foo723(); +extern void foo724(); +extern void foo725(); +extern void foo726(); +extern void foo727(); +extern void foo728(); +extern void foo729(); +extern void foo730(); +extern void foo731(); +extern void foo732(); +extern void foo733(); +extern void foo734(); +extern void foo735(); +extern void foo736(); +extern void foo737(); +extern void foo738(); +extern void foo739(); +extern void foo740(); +extern void foo741(); +extern void foo742(); +extern void foo743(); +extern void foo744(); +extern void foo745(); +extern void foo746(); +extern void foo747(); +extern void foo748(); +extern void foo749(); +extern void foo750(); +extern void foo751(); +extern void foo752(); +extern void foo753(); +extern void foo754(); +extern void foo755(); +extern void foo756(); +extern void foo757(); +extern void foo758(); +extern void foo759(); +extern void foo760(); +extern void foo761(); +extern void foo762(); +extern void foo763(); +extern void foo764(); +extern void foo765(); +extern void foo766(); +extern void foo767(); +extern void foo768(); +extern void foo769(); +extern void foo770(); +extern void foo771(); +extern void foo772(); +extern void foo773(); +extern void foo774(); +extern void foo775(); +extern void foo776(); +extern void foo777(); +extern void foo778(); +extern void foo779(); +extern void foo780(); +extern void foo781(); +extern void foo782(); +extern void foo783(); +extern void foo784(); +extern void foo785(); +extern void foo786(); +extern void foo787(); +extern void foo788(); +extern void foo789(); +extern void foo790(); +extern void foo791(); +extern void foo792(); +extern void foo793(); +extern void foo794(); +extern void foo795(); +extern void foo796(); +extern void foo797(); +extern void foo798(); +extern void foo799(); +extern void foo800(); +extern void foo801(); +extern void foo802(); +extern void foo803(); +extern void foo804(); +extern void foo805(); +extern void foo806(); +extern void foo807(); +extern void foo808(); +extern void foo809(); +extern void foo810(); +extern void foo811(); +extern void foo812(); +extern void foo813(); +extern void foo814(); +extern void foo815(); +extern void foo816(); +extern void foo817(); +extern void foo818(); +extern void foo819(); +extern void foo820(); +extern void foo821(); +extern void foo822(); +extern void foo823(); +extern void foo824(); diff --git a/src/dyld-apple/unit-tests/test-cases/big-jump-table/funcs.c b/src/dyld-apple/unit-tests/test-cases/big-jump-table/funcs.c new file mode 100644 index 000000000..64cd71a19 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/big-jump-table/funcs.c @@ -0,0 +1,859 @@ +/* + * Copyright (c) 2006 Apple Computer, 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@ + */ +#include +#include +#include "foo.h" + +static void __attribute__((constructor)) myinit() +{ +// uint64_t t1 = mach_absolute_time(); + foo002(); + foo003(); + foo004(); + foo005(); + foo006(); + foo007(); + foo008(); + foo009(); + foo010(); + foo011(); + foo012(); + foo013(); + foo014(); + foo015(); + foo016(); + foo017(); + foo018(); + foo019(); + foo020(); + foo021(); + foo022(); + foo023(); + foo024(); + foo025(); + foo026(); + foo027(); + foo028(); + foo029(); + foo030(); + foo031(); + foo032(); + foo033(); + foo034(); + foo035(); + foo036(); + foo037(); + foo038(); + foo039(); + foo040(); + foo041(); + foo042(); + foo043(); + foo044(); + foo045(); + foo046(); + foo047(); + foo048(); + foo049(); + foo050(); + foo051(); + foo052(); + foo053(); + foo054(); + foo055(); + foo056(); + foo057(); + foo058(); + foo059(); + foo060(); + foo061(); + foo062(); + foo063(); + foo064(); + foo065(); + foo066(); + foo067(); + foo068(); + foo069(); + foo070(); + foo071(); + foo072(); + foo073(); + foo074(); + foo075(); + foo076(); + foo077(); + foo078(); + foo079(); + foo080(); + foo081(); + foo082(); + foo083(); + foo084(); + foo085(); + foo086(); + foo087(); + foo088(); + foo089(); + foo090(); + foo091(); + foo092(); + foo093(); + foo094(); + foo095(); + foo096(); + foo097(); + foo098(); + foo099(); + foo100(); + foo101(); + foo102(); + foo103(); + foo104(); + foo105(); + foo106(); + foo107(); + foo108(); + foo109(); + foo110(); + foo111(); + foo112(); + foo113(); + foo114(); + foo115(); + foo116(); + foo117(); + foo118(); + foo119(); + foo120(); + foo121(); + foo122(); + foo123(); + foo124(); + foo125(); + foo126(); + foo127(); + foo128(); + foo129(); + foo130(); + foo131(); + foo132(); + foo133(); + foo134(); + foo135(); + foo136(); + foo137(); + foo138(); + foo139(); + foo140(); + foo141(); + foo142(); + foo143(); + foo144(); + foo145(); + foo146(); + foo147(); + foo148(); + foo149(); + foo150(); + foo151(); + foo152(); + foo153(); + foo154(); + foo155(); + foo156(); + foo157(); + foo158(); + foo159(); + foo160(); + foo161(); + foo162(); + foo163(); + foo164(); + foo165(); + foo166(); + foo167(); + foo168(); + foo169(); + foo170(); + foo171(); + foo172(); + foo173(); + foo174(); + foo175(); + foo176(); + foo177(); + foo178(); + foo179(); + foo180(); + foo181(); + foo182(); + foo183(); + foo184(); + foo185(); + foo186(); + foo187(); + foo188(); + foo189(); + foo190(); + foo191(); + foo192(); + foo193(); + foo194(); + foo195(); + foo196(); + foo197(); + foo198(); + foo199(); + foo200(); + foo201(); + foo202(); + foo203(); + foo204(); + foo205(); + foo206(); + foo207(); + foo208(); + foo209(); + foo210(); + foo211(); + foo212(); + foo213(); + foo214(); + foo215(); + foo216(); + foo217(); + foo218(); + foo219(); + foo220(); + foo221(); + foo222(); + foo223(); + foo224(); + foo225(); + foo226(); + foo227(); + foo228(); + foo229(); + foo230(); + foo231(); + foo232(); + foo233(); + foo234(); + foo235(); + foo236(); + foo237(); + foo238(); + foo239(); + foo240(); + foo241(); + foo242(); + foo243(); + foo244(); + foo245(); + foo246(); + foo247(); + foo248(); + foo249(); + foo250(); + foo251(); + foo252(); + foo253(); + foo254(); + foo255(); + foo256(); + foo257(); + foo258(); + foo259(); + foo260(); + foo261(); + foo262(); + foo263(); + foo264(); + foo265(); + foo266(); + foo267(); + foo268(); + foo269(); + foo270(); + foo271(); + foo272(); + foo273(); + foo274(); + foo275(); + foo276(); + foo277(); + foo278(); + foo279(); + foo280(); + foo281(); + foo282(); + foo283(); + foo284(); + foo285(); + foo286(); + foo287(); + foo288(); + foo289(); + foo290(); + foo291(); + foo292(); + foo293(); + foo294(); + foo295(); + foo296(); + foo297(); + foo298(); + foo299(); + foo300(); + foo301(); + foo302(); + foo303(); + foo304(); + foo305(); + foo306(); + foo307(); + foo308(); + foo309(); + foo310(); + foo311(); + foo312(); + foo313(); + foo314(); + foo315(); + foo316(); + foo317(); + foo318(); + foo319(); + foo320(); + foo321(); + foo322(); + foo323(); + foo324(); + foo325(); + foo326(); + foo327(); + foo328(); + foo329(); + foo330(); + foo331(); + foo332(); + foo333(); + foo334(); + foo335(); + foo336(); + foo337(); + foo338(); + foo339(); + foo340(); + foo341(); + foo342(); + foo343(); + foo344(); + foo345(); + foo346(); + foo347(); + foo348(); + foo349(); + foo350(); + foo351(); + foo352(); + foo353(); + foo354(); + foo355(); + foo356(); + foo357(); + foo358(); + foo359(); + foo360(); + foo361(); + foo362(); + foo363(); + foo364(); + foo365(); + foo366(); + foo367(); + foo368(); + foo369(); + foo370(); + foo371(); + foo372(); + foo373(); + foo374(); + foo375(); + foo376(); + foo377(); + foo378(); + foo379(); + foo380(); + foo381(); + foo382(); + foo383(); + foo384(); + foo385(); + foo386(); + foo387(); + foo388(); + foo389(); + foo390(); + foo391(); + foo392(); + foo393(); + foo394(); + foo395(); + foo396(); + foo397(); + foo398(); + foo399(); + foo400(); + foo401(); + foo402(); + foo403(); + foo404(); + foo405(); + foo406(); + foo407(); + foo408(); + foo409(); + foo410(); + foo411(); + foo412(); + foo413(); + foo414(); + foo415(); + foo416(); + foo417(); + foo418(); + foo419(); + foo420(); + foo421(); + foo422(); + foo423(); + foo424(); + foo425(); + foo426(); + foo427(); + foo428(); + foo429(); + foo430(); + foo431(); + foo432(); + foo433(); + foo434(); + foo435(); + foo436(); + foo437(); + foo438(); + foo439(); + foo440(); + foo441(); + foo442(); + foo443(); + foo444(); + foo445(); + foo446(); + foo447(); + foo448(); + foo449(); + foo450(); + foo451(); + foo452(); + foo453(); + foo454(); + foo455(); + foo456(); + foo457(); + foo458(); + foo459(); + foo460(); + foo461(); + foo462(); + foo463(); + foo464(); + foo465(); + foo466(); + foo467(); + foo468(); + foo469(); + foo470(); + foo471(); + foo472(); + foo473(); + foo474(); + foo475(); + foo476(); + foo477(); + foo478(); + foo479(); + foo480(); + foo481(); + foo482(); + foo483(); + foo484(); + foo485(); + foo486(); + foo487(); + foo488(); + foo489(); + foo490(); + foo491(); + foo492(); + foo493(); + foo494(); + foo495(); + foo496(); + foo497(); + foo498(); + foo499(); + foo500(); + foo501(); + foo502(); + foo503(); + foo504(); + foo505(); + foo506(); + foo507(); + foo508(); + foo509(); + foo510(); + foo511(); + foo512(); + foo513(); + foo514(); + foo515(); + foo516(); + foo517(); + foo518(); + foo519(); + foo520(); + foo521(); + foo522(); + foo523(); + foo524(); + foo525(); + foo526(); + foo527(); + foo528(); + foo529(); + foo530(); + foo531(); + foo532(); + foo533(); + foo534(); + foo535(); + foo536(); + foo537(); + foo538(); + foo539(); + foo540(); + foo541(); + foo542(); + foo543(); + foo544(); + foo545(); + foo546(); + foo547(); + foo548(); + foo549(); + foo550(); + foo551(); + foo552(); + foo553(); + foo554(); + foo555(); + foo556(); + foo557(); + foo558(); + foo559(); + foo560(); + foo561(); + foo562(); + foo563(); + foo564(); + foo565(); + foo566(); + foo567(); + foo568(); + foo569(); + foo570(); + foo571(); + foo572(); + foo573(); + foo574(); + foo575(); + foo576(); + foo577(); + foo578(); + foo579(); + foo580(); + foo581(); + foo582(); + foo583(); + foo584(); + foo585(); + foo586(); + foo587(); + foo588(); + foo589(); + foo590(); + foo591(); + foo592(); + foo593(); + foo594(); + foo595(); + foo596(); + foo597(); + foo598(); + foo599(); + foo600(); + foo601(); + foo602(); + foo603(); + foo604(); + foo605(); + foo606(); + foo607(); + foo608(); + foo609(); + foo610(); + foo611(); + foo612(); + foo613(); + foo614(); + foo615(); + foo616(); + foo617(); + foo618(); + foo619(); + foo620(); + foo621(); + foo622(); + foo623(); + foo624(); + foo625(); + foo626(); + foo627(); + foo628(); + foo629(); + foo630(); + foo631(); + foo632(); + foo633(); + foo634(); + foo635(); + foo636(); + foo637(); + foo638(); + foo639(); + foo640(); + foo641(); + foo642(); + foo643(); + foo644(); + foo645(); + foo646(); + foo647(); + foo648(); + foo649(); + foo650(); + foo651(); + foo652(); + foo653(); + foo654(); + foo655(); + foo656(); + foo657(); + foo658(); + foo659(); + foo660(); + foo661(); + foo662(); + foo663(); + foo664(); + foo665(); + foo666(); + foo667(); + foo668(); + foo669(); + foo670(); + foo671(); + foo672(); + foo673(); + foo674(); + foo675(); + foo676(); + foo677(); + foo678(); + foo679(); + foo680(); + foo681(); + foo682(); + foo683(); + foo684(); + foo685(); + foo686(); + foo687(); + foo688(); + foo689(); + foo690(); + foo691(); + foo692(); + foo693(); + foo694(); + foo695(); + foo696(); + foo697(); + foo698(); + foo699(); + foo700(); + foo701(); + foo702(); + foo703(); + foo704(); + foo705(); + foo706(); + foo707(); + foo708(); + foo709(); + foo710(); + foo711(); + foo712(); + foo713(); + foo714(); + foo715(); + foo716(); + foo717(); + foo718(); + foo719(); + foo720(); + foo721(); + foo722(); + foo723(); + foo724(); + foo725(); + foo726(); + foo727(); + foo728(); + foo729(); + foo730(); + foo731(); + foo732(); + foo733(); + foo734(); + foo735(); + foo736(); + foo737(); + foo738(); + foo739(); + foo740(); + foo741(); + foo742(); + foo743(); + foo744(); + foo745(); + foo746(); + foo747(); + foo748(); + foo749(); + foo750(); + foo751(); + foo752(); + foo753(); + foo754(); + foo755(); + foo756(); + foo757(); + foo758(); + foo759(); + foo760(); + foo761(); + foo762(); + foo763(); + foo764(); + foo765(); + foo766(); + foo767(); + foo768(); + foo769(); + foo770(); + foo771(); + foo772(); + foo773(); + foo774(); + foo775(); + foo776(); + foo777(); + foo778(); + foo779(); + foo780(); + foo781(); + foo782(); + foo783(); + foo784(); + foo785(); + foo786(); + foo787(); + foo788(); + foo789(); + foo790(); + foo791(); + foo792(); + foo793(); + foo794(); + foo795(); + foo796(); + foo797(); + foo798(); + foo799(); + foo800(); + foo801(); + foo802(); + foo803(); + foo804(); + foo805(); + foo806(); + foo807(); + foo808(); + foo809(); + foo810(); + foo811(); + foo812(); + foo813(); +#if CASE <= 4 + foo814(); +#endif +#if CASE <= 3 + foo815(); +#endif +#if CASE <= 2 + foo816(); +#endif +#if CASE <= 1 + foo817(); +#endif + +// uint64_t t2 = mach_absolute_time(); +// fprintf(stderr, "total time = %lld\n", t2-t1); + +} + diff --git a/src/dyld-apple/unit-tests/test-cases/big-jump-table/main.c b/src/dyld-apple/unit-tests/test-cases/big-jump-table/main.c new file mode 100644 index 000000000..6241ef450 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/big-jump-table/main.c @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2006 Apple Computer, 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + + +int main() +{ + PASS("big-jump-table"); + return EXIT_SUCCESS; +} + + diff --git a/src/dyld-apple/unit-tests/test-cases/big-jump-table/pointers.c b/src/dyld-apple/unit-tests/test-cases/big-jump-table/pointers.c new file mode 100644 index 000000000..5484be0d2 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/big-jump-table/pointers.c @@ -0,0 +1,23 @@ + +#include "foo.h" + +long useNonLazy() +{ + long result = 0; + result += (long)&foo001; + result += (long)&foo002; + result += (long)&foo003; +#if CASE > 0 + result += (long)&foo818; +#endif +#if CASE > 1 + result += (long)&foo817; +#endif +#if CASE > 2 + result += (long)&foo816; +#endif +#if CASE > 3 + result += (long)&foo815; +#endif + return result; +} diff --git a/src/dyld-apple/unit-tests/test-cases/big-stack/main.c b/src/dyld-apple/unit-tests/test-cases/big-stack/main.c new file mode 100644 index 000000000..2c83e2d64 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/big-stack/main.c @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2006-2012 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@ + */ +#include // EXIT_SUCCESS +#include +#include +#include + +#include "test.h" + +// +// This builds an executable that needs > 2GB of stack +// + + +char* keepAlive; // to keep compiler from optimizing away stack variable + +// keep recursing until desired stack size achieved +void foo(unsigned long long stackSize, char* stackStart) +{ + char buffer[32*1024*1024]; + keepAlive = buffer; + // only recursive if there is enough room for next buffer + intptr_t freeStackSpace = (buffer - sizeof(buffer)) - (stackStart - stackSize); + //fprintf(stderr, "&buffer=%p, stackStart=%p, freeStackSpace=0x%lx\n", buffer, stackStart, freeStackSpace); + if ( freeStackSpace < sizeof(buffer) ) + return; + else + foo(stackSize, stackStart); +} + + +int +main() +{ + char start; + foo(STACK_SIZE, &start); + return EXIT_SUCCESS; +} + + diff --git a/src/dyld-apple/unit-tests/test-cases/branch-islands/extra.c b/src/dyld-apple/unit-tests/test-cases/branch-islands/extra.c new file mode 100644 index 000000000..c90affa80 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/branch-islands/extra.c @@ -0,0 +1,8 @@ +#include + + +bool test1() +{ + return false; +} + diff --git a/src/dyld-apple/unit-tests/test-cases/branch-islands/main.c b/src/dyld-apple/unit-tests/test-cases/branch-islands/main.c new file mode 100644 index 000000000..9a265a03a --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/branch-islands/main.c @@ -0,0 +1,26 @@ +#include +#include // exit(), EXIT_SUCCESS +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +// returns false on success +extern bool test1(); + +const char* str = "hello"; + + +int main() +{ + if ( test1() ) + FAIL("branch-islands: test1"); + + if ( strncmp(str, "he", 2) != 0 ) + FAIL("branch-islands: strncmp stub"); + + + PASS("branch-islands"); + return EXIT_SUCCESS; +} + diff --git a/src/dyld-apple/unit-tests/test-cases/branch-islands/space.s b/src/dyld-apple/unit-tests/test-cases/branch-islands/space.s new file mode 100644 index 000000000..4fdd630be --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/branch-islands/space.s @@ -0,0 +1,77 @@ + +#if __ppc__ + + .text + +_prejunk: + mr r3,r5 + mr r3,r4 + blr + + +_space1: + .space 15*1024*1024 + 2 + + .align 5 +_junk: + mr r3,r5 + mr r3,r4 + blr + + +_space2: + .space 2*1024*1024 + +#endif + + +#if __arm__ + + .text +_prejunk: + mov r0, #1 + nop + +#if __thumb2__ + // thumb2 branches are +/- 16MB +_space1: + .space 12*1024*1024 +_space2: + .space 12*1024*1024 +_space3: + .space 12*1024*1024 + + +#elif __thumb__ + // thumb1 branches are +/- 4MB +_space1: + .space 3*1024*1024 +_space2: + .space 3*1024*1024 +_space3: + .space 3*1024*1024 + +#else + + // ARM branches are +/- 32MB +_space1: + .space 24*1024*1024 +_space2: + .space 24*1024*1024 +_space3: + .space 24*1024*1024 + +#endif + + .align 5 +_junk: + mov r0, #1 + nop + + +_space4: + .space 2*1024*1024 +#endif + + .subsections_via_symbols + \ No newline at end of file diff --git a/src/dyld-apple/unit-tests/test-cases/bundle-basic/bundle.c b/src/dyld-apple/unit-tests/test-cases/bundle-basic/bundle.c new file mode 100644 index 000000000..64232df88 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/bundle-basic/bundle.c @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include + +// test to see if bss section is properly expanded + +static int mydata[1000000]; + +bool checkdata() +{ + return ( mydata[500000] == 0 ); +} diff --git a/src/dyld-apple/unit-tests/test-cases/bundle-basic/main.c b/src/dyld-apple/unit-tests/test-cases/bundle-basic/main.c new file mode 100644 index 000000000..49d30c37d --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/bundle-basic/main.c @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include +#include +#include +#include + +#include "test.h" // PASS(), FAIL() + +typedef bool (*CheckFunc)(); + +int main() +{ +// these APIs are only available on Mac OS X - not iPhone OS +#if __MAC_OS_X_VERSION_MIN_REQUIRED + NSObjectFileImage ofi; + if ( NSCreateObjectFileImageFromFile("test.bundle", &ofi) != NSObjectFileImageSuccess ) { + FAIL("NSCreateObjectFileImageFromFile failed"); + return 1; + } + + NSModule mod = NSLinkModule(ofi, "test.bundle", NSLINKMODULE_OPTION_NONE); + if ( mod == NULL ) { + FAIL("NSLinkModule failed"); + return 1; + } + + NSSymbol sym = NSLookupSymbolInModule(mod, "_checkdata"); + if ( sym == NULL ) { + FAIL("NSLookupSymbolInModule failed"); + return 1; + } + + CheckFunc func = NSAddressOfSymbol(sym); + if ( !func() ) { + FAIL("NSAddressOfSymbol failed"); + return 1; + } + + if ( !NSUnLinkModule(mod, NSUNLINKMODULE_OPTION_NONE) ) { + FAIL("NSUnLinkModule failed"); + return 1; + } + + if ( !NSDestroyObjectFileImage(ofi) ) { + FAIL("NSDestroyObjectFileImage failed"); + return 1; + } +#endif + PASS("bundle-basic"); + return 0; +} \ No newline at end of file diff --git a/src/dyld-apple/unit-tests/test-cases/bundle-dont-gc/bar.c b/src/dyld-apple/unit-tests/test-cases/bundle-dont-gc/bar.c new file mode 100644 index 000000000..3aee7ab2f --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/bundle-dont-gc/bar.c @@ -0,0 +1,3 @@ + +void bar() {} + diff --git a/src/dyld-apple/unit-tests/test-cases/bundle-dont-gc/foo.c b/src/dyld-apple/unit-tests/test-cases/bundle-dont-gc/foo.c new file mode 100644 index 000000000..6924ac69d --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/bundle-dont-gc/foo.c @@ -0,0 +1,3 @@ + +void foo() {} + diff --git a/src/dyld-apple/unit-tests/test-cases/bundle-dont-gc/main.c b/src/dyld-apple/unit-tests/test-cases/bundle-dont-gc/main.c new file mode 100644 index 000000000..c79afe0fb --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/bundle-dont-gc/main.c @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2006 Apple Computer, 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@ + */ +#include // fprintf(), NULL +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +int main(int argc, const char* argv[]) +{ +// NSObjectFile* APIs are only available on Mac OS X - not iPhone OS +#if __MAC_OS_X_VERSION_MIN_REQUIRED + // load foo.bundle with old API + NSObjectFileImage ofi; + if ( NSCreateObjectFileImageFromFile("foo.bundle", &ofi) != NSObjectFileImageSuccess ) { + FAIL("bundle-dont-gc: NSCreateObjectFileImageFromFile failed"); + return 1; + } + + NSModule mod = NSLinkModule(ofi, "foo.bundle", NSLINKMODULE_OPTION_NONE); + if ( mod == NULL ) { + FAIL("bundle-dont-gc: NSLinkModule failed"); + return 1; + } + + NSSymbol sym = NSLookupSymbolInModule(mod, "_foo"); + if ( sym == NULL ) { + FAIL("bundle-dont-gc: NSLookupSymbolInModule failed"); + return 1; + } + + void* fooAddr = NSAddressOfSymbol(sym); + if ( fooAddr == NULL ) { + FAIL("bundle-dont-gc: NSAddressOfSymbol failed"); + return 1; + } + + if ( !NSDestroyObjectFileImage(ofi) ) { + FAIL("bundle-dont-gc: NSDestroyObjectFileImage failed"); + return 1; + } + + // open and close bar.bundle with new API, which causes a gc of images + void* h1 = dlopen("bar.bundle", RTLD_LAZY); + if ( h1 == NULL ) { + FAIL("bundle-dont-gc: can't dlopen bar.bundle: %s", dlerror()); + return EXIT_SUCCESS; + } + dlclose(h1); + + // make sure foo is still loaded + Dl_info info; + if ( dladdr(fooAddr, &info) == 0 ) { + FAIL("bundle-dont-gc: dladdr() failed"); + exit(0); + } + + // unload foo.bundle with old API + if ( !NSUnLinkModule(mod, NSUNLINKMODULE_OPTION_NONE) ) { + FAIL("NSUnLinkModule failed"); + return 1; + } +#endif + + PASS("bundle-dont-gc"); + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/bundle-memory-load-all-infos/bundle.c b/src/dyld-apple/unit-tests/test-cases/bundle-memory-load-all-infos/bundle.c new file mode 100644 index 000000000..64232df88 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/bundle-memory-load-all-infos/bundle.c @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include + +// test to see if bss section is properly expanded + +static int mydata[1000000]; + +bool checkdata() +{ + return ( mydata[500000] == 0 ); +} diff --git a/src/dyld-apple/unit-tests/test-cases/bundle-memory-load-all-infos/main.c b/src/dyld-apple/unit-tests/test-cases/bundle-memory-load-all-infos/main.c new file mode 100644 index 000000000..87eacca12 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/bundle-memory-load-all-infos/main.c @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2010 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@ + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "test.h" // PASS(), FAIL() + + +struct dyld_all_image_infos* getImageInfosFromKernel() +{ + task_dyld_info_data_t task_dyld_info; + mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT; + + if ( task_info(mach_task_self(), TASK_DYLD_INFO, (task_info_t)&task_dyld_info, &count) ) { + FAIL("all_image_infos: task_info() failed"); + exit(0); + } + return (struct dyld_all_image_infos*)(uintptr_t)task_dyld_info.all_image_info_addr; +} + + +int main() +{ +// NSObjectFileImage APIs are only available on Mac OS X - not iPhone OS +#if __MAC_OS_X_VERSION_MIN_REQUIRED + int fd = open("test.bundle", O_RDONLY, 0); + if ( fd == -1 ) { + FAIL("open() failed"); + return 1; + } + + struct stat stat_buf; + if ( fstat(fd, &stat_buf) == -1) { + FAIL("fstat() failed"); + return 0; + } + + void* loadAddress = mmap(NULL, stat_buf.st_size, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0); + if ( loadAddress == ((void*)(-1)) ) { + FAIL("mmap() failed"); + return 0; + } + + close(fd); + + NSObjectFileImage ofi; + if ( NSCreateObjectFileImageFromMemory(loadAddress, stat_buf.st_size, &ofi) != NSObjectFileImageSuccess ) { + FAIL("NSCreateObjectFileImageFromMemory failed"); + return 0; + } + + NSModule mod = NSLinkModule(ofi, "he_he", NSLINKMODULE_OPTION_NONE); + if ( mod == NULL ) { + FAIL("NSLinkModule failed"); + return 0; + } + + // look for he_he string in list of images loaded + struct dyld_all_image_infos* infos = getImageInfosFromKernel(); + + if ( infos->infoArrayCount < 2 ) { + FAIL("bundle-memory-load-all-images: dyld_all_image_infos.infoArrayCount is < 2"); + return 0; + } + + bool found = false; + for( int i=0; i < infos->infoArrayCount; ++i) { + //fprintf(stderr, "infos->infoArray[%d].imageLoadAddress=%p %s\n", i, infos->infoArray[i].imageLoadAddress, infos->infoArray[i].imageFilePath); + if ( infos->infoArray[i].imageFilePath == NULL ) { + FAIL("bundle-memory-load-all-images: NULL image path found"); + exit(0); + } + if ( strcmp(infos->infoArray[i].imageFilePath, "he_he") == 0 ) + found = true; + } + + if ( !found ) { + FAIL("bundle-memory-load-all-images: loaded memory bundle 'he_he' nout found"); + return 0; + } + + if ( !NSUnLinkModule(mod, NSUNLINKMODULE_OPTION_NONE) ) { + FAIL("NSUnLinkModule failed"); + return 0; + } + + if ( !NSDestroyObjectFileImage(ofi) ) { + FAIL("NSDestroyObjectFileImage failed"); + return 0; + } + + // Should check that loadAddress is unmmaped now (by call to NSDestroyObjectFileImage) +#endif + + PASS("bundle-memory-load-all-images"); + return 0; +} \ No newline at end of file diff --git a/src/dyld-apple/unit-tests/test-cases/bundle-memory-load-bad/bundle.c b/src/dyld-apple/unit-tests/test-cases/bundle-memory-load-bad/bundle.c new file mode 100644 index 000000000..755613881 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/bundle-memory-load-bad/bundle.c @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2006 Apple Computer, 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@ + */ + + +int main() +{ + return 0; +} diff --git a/src/dyld-apple/unit-tests/test-cases/bundle-memory-load-bad/main.c b/src/dyld-apple/unit-tests/test-cases/bundle-memory-load-bad/main.c new file mode 100644 index 000000000..7e8c93c84 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/bundle-memory-load-bad/main.c @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2006 Apple Computer, 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@ + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "test.h" // PASS(), FAIL() + +int main() +{ +// NSAddImage is only available on Mac OS X - not iPhone OS +#if __MAC_OS_X_VERSION_MIN_REQUIRED + int fd = open("test.bundle", O_RDONLY, 0); + if ( fd == -1 ) { + FAIL("open() failed"); + return 1; + } + + struct stat stat_buf; + if ( fstat(fd, &stat_buf) == -1) { + FAIL("fstat() failed"); + return 1; + } + + void* loadAddress = mmap(NULL, stat_buf.st_size, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0); + if ( loadAddress == ((void*)(-1)) ) { + FAIL("mmap() failed"); + return 1; + } + + close(fd); + + // we are using a file not of type MH_BUNDLE, so NSCreateObjectFileImageFromMemory should fail + NSObjectFileImage ofi; + if ( NSCreateObjectFileImageFromMemory(loadAddress, stat_buf.st_size, &ofi) == NSObjectFileImageSuccess ) + FAIL("bundle-memory-load-bad"); + else +#endif + PASS("bundle-memory-load-bad"); + + return 0; +} \ No newline at end of file diff --git a/src/dyld-apple/unit-tests/test-cases/bundle-memory-load-fat/bundle.c b/src/dyld-apple/unit-tests/test-cases/bundle-memory-load-fat/bundle.c new file mode 100644 index 000000000..3f96d53cd --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/bundle-memory-load-fat/bundle.c @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include + +// test to see if bss section is properly expanded + + +void check() +{ + +} diff --git a/src/dyld-apple/unit-tests/test-cases/bundle-memory-load-fat/main.c b/src/dyld-apple/unit-tests/test-cases/bundle-memory-load-fat/main.c new file mode 100644 index 000000000..05ef670e2 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/bundle-memory-load-fat/main.c @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "test.h" // PASS(), FAIL() + +typedef bool (*CheckFunc)(); + +int main() +{ +// NSObjectFileImage APIs only available on Mac OS X - not iPhone OS +#if __MAC_OS_X_VERSION_MIN_REQUIRED + int fd = open("test.bundle", O_RDONLY, 0); + if ( fd == -1 ) { + FAIL("open() failed"); + return 1; + } + + struct stat stat_buf; + if ( fstat(fd, &stat_buf) == -1) { + FAIL("fstat() failed"); + return 1; + } + + void* loadAddress = mmap(NULL, stat_buf.st_size, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0); + if ( loadAddress == ((void*)(-1)) ) { + FAIL("mmap() failed"); + return 1; + } + + close(fd); + + NSObjectFileImage ofi; + if ( NSCreateObjectFileImageFromMemory(loadAddress, stat_buf.st_size, &ofi) != NSObjectFileImageSuccess ) { + FAIL("NSCreateObjectFileImageFromMemory failed"); + return 1; + } + + NSModule mod = NSLinkModule(ofi, "he_he", NSLINKMODULE_OPTION_NONE); + if ( mod == NULL ) { + FAIL("NSLinkModule failed"); + return 1; + } + + NSSymbol sym = NSLookupSymbolInModule(mod, "_check"); + if ( sym == NULL ) { + FAIL("NSLookupSymbolInModule failed"); + return 1; + } + + CheckFunc func = NSAddressOfSymbol(sym); + if ( func == NULL ) { + FAIL("NSAddressOfSymbol failed"); + return 1; + } + + if ( !NSUnLinkModule(mod, NSUNLINKMODULE_OPTION_NONE) ) { + FAIL("NSUnLinkModule failed"); + return 1; + } + + if ( !NSDestroyObjectFileImage(ofi) ) { + FAIL("NSDestroyObjectFileImage failed"); + return 1; + } + + // Should check that loadAddress is unmmaped now (by call to NSDestroyObjectFileImage) +#endif + + PASS("bundle-memory-load-fat"); + return 0; +} \ No newline at end of file diff --git a/src/dyld-apple/unit-tests/test-cases/bundle-memory-load-malloc/bundle.c b/src/dyld-apple/unit-tests/test-cases/bundle-memory-load-malloc/bundle.c new file mode 100644 index 000000000..64232df88 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/bundle-memory-load-malloc/bundle.c @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include + +// test to see if bss section is properly expanded + +static int mydata[1000000]; + +bool checkdata() +{ + return ( mydata[500000] == 0 ); +} diff --git a/src/dyld-apple/unit-tests/test-cases/bundle-memory-load-malloc/main.c b/src/dyld-apple/unit-tests/test-cases/bundle-memory-load-malloc/main.c new file mode 100644 index 000000000..47e87924e --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/bundle-memory-load-malloc/main.c @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2005-2009 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@ + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include "test.h" // PASS(), FAIL() + +typedef bool (*CheckFunc)(); + +int main() +{ +// NSCreateObjectFileImageFromMemory is only available on Mac OS X - not iPhone OS +#if __MAC_OS_X_VERSION_MIN_REQUIRED + int fd = open("test.bundle", O_RDONLY, 0); + if ( fd == -1 ) { + FAIL("open() failed"); + return 1; + } + + struct stat stat_buf; + if ( fstat(fd, &stat_buf) == -1) { + FAIL("fstat() failed"); + return 1; + } + + void* loadAddress = malloc((stat_buf.st_size+4095) & (-4096)); + if ( loadAddress == NULL ) { + FAIL("malloc failed"); + return 1; + } + + if ( pread(fd, loadAddress, stat_buf.st_size, 0) != stat_buf.st_size ) { + FAIL("pread() failed"); + return 1; + } + + //void* loadAddress2 = mmap(NULL, stat_buf.st_size, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0); + close(fd); + + NSObjectFileImage ofi; + if ( NSCreateObjectFileImageFromMemory(loadAddress, stat_buf.st_size, &ofi) != NSObjectFileImageSuccess ) { + FAIL("NSCreateObjectFileImageFromMemory failed"); + return 1; + } + + NSModule mod = NSLinkModule(ofi, "he_he", NSLINKMODULE_OPTION_NONE); + if ( mod == NULL ) { + FAIL("NSLinkModule failed"); + return 1; + } + + NSSymbol sym = NSLookupSymbolInModule(mod, "_checkdata"); + if ( sym == NULL ) { + FAIL("NSLookupSymbolInModule failed"); + return 1; + } + + CheckFunc func = NSAddressOfSymbol(sym); + if ( !func() ) { + FAIL("NSAddressOfSymbol failed"); + return 1; + } + + if ( !NSUnLinkModule(mod, NSUNLINKMODULE_OPTION_NONE) ) { + FAIL("NSUnLinkModule failed"); + return 1; + } + + if ( !NSDestroyObjectFileImage(ofi) ) { + FAIL("NSDestroyObjectFileImage failed"); + return 1; + } + //fprintf(stderr, "loadAddress=%p\n", loadAddress); + //fprintf(stderr, "malloc_size(loadAddress) => 0x%08X\n", malloc_size(loadAddress)); + //fprintf(stderr, "loadAddress2=%p\n", loadAddress2); + //fprintf(stderr, "malloc_size(loadAddress2) => 0x%08X\n", malloc_size(loadAddress2)); + + + //free(loadAddress); + //fprintf(stderr, "malloc_size(loadAddress) => 0x%08X\n", malloc_size(loadAddress)); + if ( malloc_size(loadAddress) != 0 ) { + FAIL("malloc_size(loadAddress) => 0x%08X", malloc_size(loadAddress)); + FAIL("malloc still thinks it owns this block"); + return 1; + } + + // Should check that loadAddress is unmmaped now (by call to NSDestroyObjectFileImage) +#endif + + PASS("bundle-memory-load-malloc"); + return 0; +} \ No newline at end of file diff --git a/src/dyld-apple/unit-tests/test-cases/bundle-memory-load/bundle.c b/src/dyld-apple/unit-tests/test-cases/bundle-memory-load/bundle.c new file mode 100644 index 000000000..64232df88 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/bundle-memory-load/bundle.c @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include + +// test to see if bss section is properly expanded + +static int mydata[1000000]; + +bool checkdata() +{ + return ( mydata[500000] == 0 ); +} diff --git a/src/dyld-apple/unit-tests/test-cases/bundle-memory-load/main.c b/src/dyld-apple/unit-tests/test-cases/bundle-memory-load/main.c new file mode 100644 index 000000000..7af9854f4 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/bundle-memory-load/main.c @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "test.h" // PASS(), FAIL() + +typedef bool (*CheckFunc)(); + +int main() +{ +// NSObjectFileImage APIs are only available on Mac OS X - not iPhone OS +#if __MAC_OS_X_VERSION_MIN_REQUIRED + int fd = open("test.bundle", O_RDONLY, 0); + if ( fd == -1 ) { + FAIL("open() failed"); + return 1; + } + + struct stat stat_buf; + if ( fstat(fd, &stat_buf) == -1) { + FAIL("fstat() failed"); + return 1; + } + + void* loadAddress = mmap(NULL, stat_buf.st_size, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0); + if ( loadAddress == ((void*)(-1)) ) { + FAIL("mmap() failed"); + return 1; + } + + close(fd); + + NSObjectFileImage ofi; + if ( NSCreateObjectFileImageFromMemory(loadAddress, stat_buf.st_size, &ofi) != NSObjectFileImageSuccess ) { + FAIL("NSCreateObjectFileImageFromMemory failed"); + return 1; + } + + NSModule mod = NSLinkModule(ofi, "he_he", NSLINKMODULE_OPTION_NONE); + if ( mod == NULL ) { + FAIL("NSLinkModule failed"); + return 1; + } + + NSSymbol sym = NSLookupSymbolInModule(mod, "_checkdata"); + if ( sym == NULL ) { + FAIL("NSLookupSymbolInModule failed"); + return 1; + } + + CheckFunc func = NSAddressOfSymbol(sym); + if ( !func() ) { + FAIL("NSAddressOfSymbol failed"); + return 1; + } + + if ( !NSUnLinkModule(mod, NSUNLINKMODULE_OPTION_NONE) ) { + FAIL("NSUnLinkModule failed"); + return 1; + } + + if ( !NSDestroyObjectFileImage(ofi) ) { + FAIL("NSDestroyObjectFileImage failed"); + return 1; + } + + // Should check that loadAddress is unmmaped now (by call to NSDestroyObjectFileImage) +#endif + + PASS("bundle-memory-load"); + return 0; +} \ No newline at end of file diff --git a/src/dyld-apple/unit-tests/test-cases/bundle-multi-link/bundle.c b/src/dyld-apple/unit-tests/test-cases/bundle-multi-link/bundle.c new file mode 100644 index 000000000..9871eea71 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/bundle-multi-link/bundle.c @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ + +static int value = 0; + +int getValue() +{ + return value; +} + +void setValue(int v) +{ + value = v; +} + diff --git a/src/dyld-apple/unit-tests/test-cases/bundle-multi-link/main.c b/src/dyld-apple/unit-tests/test-cases/bundle-multi-link/main.c new file mode 100644 index 000000000..c15bd031a --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/bundle-multi-link/main.c @@ -0,0 +1,184 @@ +/* + * Copyright (c) 2005-2009 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@ + */ +#include +#include +#include + +#include "test.h" // PASS(), FAIL() + + +/// +/// The point of this test case is to "load" a bundle once, but link it multiple times. +/// Each link creats a new instantiation of the bundle (e.g. new base address and new globals). +/// +/// + +typedef void (*setter)(int); +typedef int (*getter)(void); + +int main() +{ +// NSCreateObjectFileImageFromMemory is only available on Mac OS X - not iPhone OS +#if __MAC_OS_X_VERSION_MIN_REQUIRED + NSObjectFileImage ofi; + if ( NSCreateObjectFileImageFromFile("test.bundle", &ofi) != NSObjectFileImageSuccess ) { + FAIL("NSCreateObjectFileImageFromFile failed"); + return 0; + } + + NSModule mod = NSLinkModule(ofi, "test.bundle", NSLINKMODULE_OPTION_NONE); + if ( mod == NULL ) { + FAIL("NSLinkModule failed"); + return 0; + } + + NSSymbol sym = NSLookupSymbolInModule(mod, "_setValue"); + if ( sym == NULL ) { + FAIL("NSLookupSymbolInModule failed"); + return 0; + } + + setter func = NSAddressOfSymbol(sym); + (*func)(1); + //fprintf(stderr, "address of foo() = %p in bundle first load %p\n", func, mod); + + + NSModule mod2 = NSLinkModule(ofi, "test2.bundle", NSLINKMODULE_OPTION_NONE); + if ( mod2 == NULL ) { + NSLinkEditErrors c; int errorNumber; const char* fileName; const char* errorString; + NSLinkEditError(&c, &errorNumber, &fileName, &errorString); + FAIL("2nd NSLinkModule failed: %s", errorString); + return 0; + } + if ( mod == mod2 ) { + FAIL("2nd NSLinkModule return same function address as first"); + return 0; + } + + NSSymbol sym2getter = NSLookupSymbolInModule(mod2, "_getValue"); + if ( sym2getter == NULL ) { + FAIL("2nd NSLookupSymbolInModule failed"); + return 0; + } + getter func2getter = NSAddressOfSymbol(sym2getter); + if ( (*func2getter)() != 0 ) { + FAIL("_getValue() on second link returned non-zero"); + return 0; + } + + NSSymbol sym2 = NSLookupSymbolInModule(mod2, "_setValue"); + if ( sym2 == NULL ) { + FAIL("2nd NSLookupSymbolInModule failed"); + return 0; + } + setter func2 = NSAddressOfSymbol(sym2); + (*func2)(2); + + //fprintf(stderr, "address of foo() = %p in bundle second load %p\n", func2, mod2); + if ( func == func2 ) { + FAIL("2nd NSAddressOfSymbol return same function address as 1st"); + return 0; + } + + + NSModule mod3 = NSLinkModule(ofi, "test3.bundle", NSLINKMODULE_OPTION_NONE); + if ( mod3 == NULL ) { + FAIL("3rd NSLinkModule failed"); + return 0; + } + if ( mod3 == mod ) { + FAIL("3rd NSLinkModule return same function address as 1st"); + return 0; + } + if ( mod3 == mod2 ) { + FAIL("3rd NSLinkModule return same function address as 2nd"); + return 0; + } + + NSSymbol sym3 = NSLookupSymbolInModule(mod3, "_setValue"); + if ( sym3 == NULL ) { + FAIL("3rd NSLookupSymbolInModule failed"); + return 0; + } + setter func3 = NSAddressOfSymbol(sym3); + (*func3)(3); + //fprintf(stderr, "address of foo() = %p in bundle third load %p\n", func3, mod3); + if ( func3 == func ) { + FAIL("3rd NSAddressOfSymbol return same function address as 1st"); + return 0; + } + if ( func3 == func2 ) { + FAIL("3rd NSAddressOfSymbol return same function address as 2nd"); + return 0; + } + + if ( !NSUnLinkModule(mod, NSUNLINKMODULE_OPTION_NONE) ) { + FAIL("NSUnLinkModule failed"); + return 0; + } + + if ( !NSUnLinkModule(mod3, NSUNLINKMODULE_OPTION_NONE) ) { + FAIL("3rd NSUnLinkModule failed"); + return 0; + } + + if ( !NSUnLinkModule(mod2, NSUNLINKMODULE_OPTION_NONE) ) { + FAIL("2nd NSUnLinkModule failed"); + return 0; + } + + // now link again after unlinking everything + NSModule mod4 = NSLinkModule(ofi, "test4.bundle", NSLINKMODULE_OPTION_NONE); + if ( mod4 == NULL ) { + FAIL("4th NSLinkModule failed"); + return 0; + } + + // check that this is really a new copy by verifying the getValue() returns zero + NSSymbol sym4getter = NSLookupSymbolInModule(mod4, "_getValue"); + if ( sym4getter == NULL ) { + FAIL("4th NSLookupSymbolInModule failed"); + return 0; + } + getter func4getter = NSAddressOfSymbol(sym4getter); + if ( (*func4getter)() != 0 ) { + FAIL("_getValue() on fourth link returned non-zero"); + return 0; + } + + if ( !NSUnLinkModule(mod4, NSUNLINKMODULE_OPTION_NONE) ) { + FAIL("4th NSUnLinkModule failed"); + return 0; + } + + + if ( !NSDestroyObjectFileImage(ofi) ) { + FAIL("NSDestroyObjectFileImage failed"); + return 0; + } +#endif + + PASS("bundle-multi-link"); + return 0; +} + diff --git a/src/dyld-apple/unit-tests/test-cases/bundle-multi-load/bundle.c b/src/dyld-apple/unit-tests/test-cases/bundle-multi-load/bundle.c new file mode 100644 index 000000000..84955c456 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/bundle-multi-load/bundle.c @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +int foo() +{ + return 4; +} + diff --git a/src/dyld-apple/unit-tests/test-cases/bundle-multi-load/main.c b/src/dyld-apple/unit-tests/test-cases/bundle-multi-load/main.c new file mode 100644 index 000000000..ab0382a40 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/bundle-multi-load/main.c @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include +#include +#include + +#include "test.h" // PASS(), FAIL() + +/// +/// The point of this test is to load the same bundle file multiple times and +/// verify each time it is linked is a new instantiations (new globals, etc) +/// + +int main() +{ +// NSCreateObjectFileImageFromMemory is only available on Mac OS X - not iPhone OS +#if __MAC_OS_X_VERSION_MIN_REQUIRED + NSObjectFileImage ofi; + if ( NSCreateObjectFileImageFromFile("test.bundle", &ofi) != NSObjectFileImageSuccess ) { + FAIL("NSCreateObjectFileImageFromFile failed"); + return 0; + } + + NSModule mod = NSLinkModule(ofi, "test.bundle", NSLINKMODULE_OPTION_NONE); + if ( mod == NULL ) { + FAIL("NSLinkModule failed"); + return 0; + } + + NSSymbol sym = NSLookupSymbolInModule(mod, "_foo"); + if ( sym == NULL ) { + FAIL("NSLookupSymbolInModule failed"); + return 0; + } + + void* func = NSAddressOfSymbol(sym); + //fprintf(stderr, "1st address of foo() = %p in module %p in OFI %p\n", func, mod, ofi); + + + NSObjectFileImage ofi2; + if ( NSCreateObjectFileImageFromFile("test.bundle", &ofi2) != NSObjectFileImageSuccess ) { + FAIL("2nd NSCreateObjectFileImageFromFile failed"); + return 0; + } + + NSModule mod2 = NSLinkModule(ofi2, "test2.bundle", NSLINKMODULE_OPTION_NONE); + if ( mod2 == NULL ) { + FAIL("2nd NSLookupSymbolInModule failed"); + return 0; + } + if ( mod == mod2 ) { + FAIL("2nd NSLinkModule return same function address as first\n"); + return 0; + } + + NSSymbol sym2 = NSLookupSymbolInModule(mod2, "_foo"); + if ( sym2 == NULL ) { + FAIL("2nd NSLookupSymbolInModule failed\n"); + return 0; + } + + void* func2 = NSAddressOfSymbol(sym2); + //fprintf(stderr, "2nd address of foo() = %p in module %p in OFI %p\n", func2, mod2, ofi2); + if ( func == func2 ) { + FAIL("2nd NSAddressOfSymbol return same function address as 1st\n"); + return 0; + } + + + NSObjectFileImage ofi3; + if ( NSCreateObjectFileImageFromFile("test.bundle", &ofi3) != NSObjectFileImageSuccess ) { + FAIL("3rd NSCreateObjectFileImageFromFile failed"); + return 0; + } + NSModule mod3 = NSLinkModule(ofi3, "test3.bundle", NSLINKMODULE_OPTION_NONE); + if ( mod3 == NULL ) { + FAIL("3rd NSLinkModule failed\n"); + return 0; + } + if ( mod3 == mod ) { + FAIL("3rd NSLinkModule return same function address as 1st\n"); + return 0; + } + if ( mod3 == mod2 ) { + FAIL("3rd NSLinkModule return same function address as 2nd\n"); + return 0; + } + + NSSymbol sym3 = NSLookupSymbolInModule(mod3, "_foo"); + if ( sym3 == NULL ) { + FAIL("3rd NSLookupSymbolInModule failed\n"); + return 0; + } + void* func3 = NSAddressOfSymbol(sym3); + //fprintf(stderr, "3rd address of foo() = %p in module %p in OFI %p\n", func3, mod3, ofi3); + if ( func3 == func ) { + FAIL("3rd NSAddressOfSymbol return same function address as 1st\n"); + return 0; + } + if ( func3 == func2 ) { + FAIL("3rd NSAddressOfSymbol return same function address as 2nd\n"); + return 0; + } + + if ( !NSUnLinkModule(mod, NSUNLINKMODULE_OPTION_NONE) ) { + FAIL("NSUnLinkModule failed"); + return 0; + } + + if ( !NSUnLinkModule(mod3, NSUNLINKMODULE_OPTION_NONE) ) { + FAIL("3rd NSUnLinkModule failed"); + return 0; + } + + // note, we are calling NSDestroyObjectFileImage() before NSUnLinkModule() + if ( !NSDestroyObjectFileImage(ofi2) ) { + FAIL("2nd NSDestroyObjectFileImage failed"); + return 0; + } + if ( !NSUnLinkModule(mod2, NSUNLINKMODULE_OPTION_NONE) ) { + FAIL("2nd NSUnLinkModule failed"); + return 0; + } + + if ( !NSDestroyObjectFileImage(ofi) ) { + FAIL("1st NSDestroyObjectFileImage failed"); + return 0; + } + if ( !NSDestroyObjectFileImage(ofi3) ) { + FAIL("3rd NSDestroyObjectFileImage failed"); + return 0; + } +#endif + PASS("bundle-multi-load"); + return 0; +} + diff --git a/src/dyld-apple/unit-tests/test-cases/bundle-name-ownership/bundle.c b/src/dyld-apple/unit-tests/test-cases/bundle-name-ownership/bundle.c new file mode 100644 index 000000000..64232df88 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/bundle-name-ownership/bundle.c @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include + +// test to see if bss section is properly expanded + +static int mydata[1000000]; + +bool checkdata() +{ + return ( mydata[500000] == 0 ); +} diff --git a/src/dyld-apple/unit-tests/test-cases/bundle-name-ownership/main.c b/src/dyld-apple/unit-tests/test-cases/bundle-name-ownership/main.c new file mode 100644 index 000000000..15c556351 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/bundle-name-ownership/main.c @@ -0,0 +1,85 @@ +/* + * 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@ + */ +#include +#include +#include +#include + +#include "test.h" // PASS(), FAIL() + +typedef bool (*CheckFunc)(); + +int main() +{ +// NSCreateObjectFileImageFromMemory is only available on Mac OS X - not iPhone OS +#if __MAC_OS_X_VERSION_MIN_REQUIRED + const char* path = "test.bundle"; + + NSObjectFileImage ofi; + if ( NSCreateObjectFileImageFromFile(path, &ofi) != NSObjectFileImageSuccess ) { + FAIL("NSCreateObjectFileImageFromFile failed"); + return 0; + } + + NSModule mod = NSLinkModule(ofi, path, NSLINKMODULE_OPTION_NONE); + if ( mod == NULL ) { + FAIL("NSLinkModule failed"); + return 0; + } + + NSSymbol sym = NSLookupSymbolInModule(mod, "_checkdata"); + if ( sym == NULL ) { + FAIL("NSLookupSymbolInModule failed"); + return 0; + } + + CheckFunc func = NSAddressOfSymbol(sym); + if ( !func() ) { + FAIL("NSAddressOfSymbol failed"); + return 0; + } + + Dl_info info; + if ( dladdr(func, &info) == 0 ) { + FAIL("dladdr(func, &info) failed"); + return 0; + } + + if ( info.dli_fname == path ) { + FAIL("NSLinkModule() did not make a copy of the path"); + return 0; + } + + if ( !NSUnLinkModule(mod, NSUNLINKMODULE_OPTION_NONE) ) { + FAIL("NSUnLinkModule failed"); + return 0; + } + + if ( !NSDestroyObjectFileImage(ofi) ) { + FAIL("NSDestroyObjectFileImage failed"); + return 0; + } +#endif + PASS("bundle-name-ownership"); + return 0; +} \ No newline at end of file diff --git a/src/dyld-apple/unit-tests/test-cases/bundle-private/bundle.c b/src/dyld-apple/unit-tests/test-cases/bundle-private/bundle.c new file mode 100644 index 000000000..b7cdc4f44 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/bundle-private/bundle.c @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ + +// test to see if NSLINKMODULE_OPTION_PRIVATE works + +void findme() +{ +} diff --git a/src/dyld-apple/unit-tests/test-cases/bundle-private/main.c b/src/dyld-apple/unit-tests/test-cases/bundle-private/main.c new file mode 100644 index 000000000..2a35990c2 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/bundle-private/main.c @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2005-2009 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@ + */ +#include +#include +#include +#include + +#include "test.h" // PASS(), FAIL() + +typedef bool (*CheckFunc)(); + +int main() +{ +// NSCreateObjectFileImageFromMemory is only available on Mac OS X - not iPhone OS +#if __MAC_OS_X_VERSION_MIN_REQUIRED + NSObjectFileImage ofi; + if ( NSCreateObjectFileImageFromFile("test.bundle", &ofi) != NSObjectFileImageSuccess ) { + FAIL("NSCreateObjectFileImageFromFile failed"); + return 1; + } + + NSModule modPriv = NSLinkModule(ofi, "test.bundle-private", NSLINKMODULE_OPTION_PRIVATE); + if ( modPriv == NULL ) { + FAIL("NSLinkModule failed"); + return 1; + } + + NSSymbol symPriv = NSLookupSymbolInModule(modPriv, "_findme"); + if ( symPriv == NULL ) { + FAIL("NSLookupSymbolInModule failed"); + return 1; + } + + if ( NSIsSymbolNameDefined("_findme") ) { + FAIL("NSIsSymbolNameDefined (incorrectly) found symbol in private bundle"); + return 1; + } + + NSModule modPublic = NSLinkModule(ofi, "test.bundle-public", NSLINKMODULE_OPTION_NONE); + if ( modPublic == NULL ) { + FAIL("NSLinkModule failed"); + return 1; + } + + NSSymbol symPublic = NSLookupSymbolInModule(modPublic, "_findme"); + if ( symPublic == NULL ) { + FAIL("NSLookupSymbolInModule failed"); + return 1; + } + + if ( !NSIsSymbolNameDefined("_findme") ) { + FAIL("NSIsSymbolNameDefined did not found symbol in public bundle"); + return 1; + } + + if ( !NSUnLinkModule(modPriv, NSUNLINKMODULE_OPTION_NONE) ) { + FAIL("NSUnLinkModule failed"); + return 1; + } + + if ( !NSUnLinkModule(modPublic, NSUNLINKMODULE_OPTION_NONE) ) { + FAIL("NSUnLinkModule failed"); + return 1; + } + + if ( NSIsSymbolNameDefined("_findme") ) { + FAIL("NSIsSymbolNameDefined found unlinked symbol in public bundle"); + return 1; + } + + if ( !NSDestroyObjectFileImage(ofi) ) { + FAIL("NSDestroyObjectFileImage failed"); + return 1; + } +#endif + PASS("bundle-private"); + return 0; +} \ No newline at end of file diff --git a/src/dyld-apple/unit-tests/test-cases/bundle-reload/bundle.cxx b/src/dyld-apple/unit-tests/test-cases/bundle-reload/bundle.cxx new file mode 100644 index 000000000..a8651c4b8 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/bundle-reload/bundle.cxx @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include + +extern "C" +void foo() +{ +} + +int __attribute__((weak)) blah = 10; diff --git a/src/dyld-apple/unit-tests/test-cases/bundle-reload/main.c b/src/dyld-apple/unit-tests/test-cases/bundle-reload/main.c new file mode 100644 index 000000000..9f0f0c124 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/bundle-reload/main.c @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include +#include +#include +#include + +#include "test.h" // PASS(), FAIL() + +/// +/// The point of this test is to load and unload a bundle and +/// verify that the address->image cache is properly invalidated. rdar://problem/4212667 +/// + +typedef void (*fooProc)(); + +// test.bundle +void doit() +{ +// NSCreateObjectFileImageFromMemory is only available on Mac OS X - not iPhone OS +#if __MAC_OS_X_VERSION_MIN_REQUIRED + NSObjectFileImage ofi; + if ( NSCreateObjectFileImageFromFile("test.bundle", &ofi) != NSObjectFileImageSuccess ) { + FAIL("NSCreateObjectFileImageFromFile failed"); + exit(0); + } + + NSModule mod = NSLinkModule(ofi,"test.bundle", NSLINKMODULE_OPTION_PRIVATE | NSLINKMODULE_OPTION_BINDNOW | NSLINKMODULE_OPTION_RETURN_ON_ERROR); + if ( mod == NULL ) { + FAIL("NSLinkModule failed"); + exit(0); + } + + NSSymbol sym = NSLookupSymbolInModule(mod, "_foo"); + if ( sym == NULL ) { + FAIL("NSLookupSymbolInModule failed"); + exit(0); + } + + fooProc func = (fooProc)NSAddressOfSymbol(sym); + (*func)(); + + if ( !NSUnLinkModule(mod, NSUNLINKMODULE_OPTION_NONE) ) { + FAIL("NSUnLinkModule failed"); + exit(0); + } + + if ( !NSDestroyObjectFileImage(ofi) ) { + FAIL("NSDestroyObjectFileImage failed"); + exit(0); + } +#endif +} + + +static void myRemoveImage(const struct mach_header *mh, intptr_t vmaddr_slide) +{ +#if __MAC_OS_X_VERSION_MIN_REQUIRED + // calling _dyld_get_image_header_containing_address() during the remove image hook + // could cause dyld to not flush the address->image cache + _dyld_get_image_header_containing_address(mh); +#endif +} + + +int main() +{ + _dyld_register_func_for_remove_image(&myRemoveImage); + + doit(); + doit(); + doit(); + + PASS("bundle-reload"); + return 0; +} + diff --git a/src/dyld-apple/unit-tests/test-cases/bundle-terminator/bundle.cxx b/src/dyld-apple/unit-tests/test-cases/bundle-terminator/bundle.cxx new file mode 100644 index 000000000..b94ced24c --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/bundle-terminator/bundle.cxx @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2013 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@ + */ +#include + + +class Foo { +public: + Foo() { buffer = malloc(100); } + ~Foo() { free(buffer); } + void* get() { return buffer; } +private: + void* buffer; + +}; + + +Foo myObject; + diff --git a/src/dyld-apple/unit-tests/test-cases/bundle-terminator/main.c b/src/dyld-apple/unit-tests/test-cases/bundle-terminator/main.c new file mode 100644 index 000000000..d7fd70762 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/bundle-terminator/main.c @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2013 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@ + */ +#include +#include +#include +#include + +#include "test.h" // PASS(), FAIL() + +typedef bool (*CheckFunc)(); + +int main() +{ +// NSCreateObjectFileImageFromMemory is only available on Mac OS X - not iPhone OS +#if __MAC_OS_X_VERSION_MIN_REQUIRED + NSObjectFileImage ofi; + if ( NSCreateObjectFileImageFromFile("test.bundle", &ofi) != NSObjectFileImageSuccess ) { + FAIL("NSCreateObjectFileImageFromFile failed"); + return 1; + } + + NSModule mod = NSLinkModule(ofi, "test.bundle", NSLINKMODULE_OPTION_NONE); + if ( mod == NULL ) { + FAIL("NSLinkModule failed"); + return 1; + } + + + if ( !NSUnLinkModule(mod, NSUNLINKMODULE_OPTION_KEEP_MEMORY_MAPPED) ) { + FAIL("NSUnLinkModule failed"); + return 1; + } + + if ( !NSDestroyObjectFileImage(ofi) ) { + FAIL("NSDestroyObjectFileImage failed"); + return 1; + } + +#endif + + PASS("bundle-terminator"); + return 0; +} \ No newline at end of file diff --git a/src/dyld-apple/unit-tests/test-cases/bundle-unlinkable/bundle.c b/src/dyld-apple/unit-tests/test-cases/bundle-unlinkable/bundle.c new file mode 100644 index 000000000..8f91e1149 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/bundle-unlinkable/bundle.c @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +extern int bar(); + + +int foo() +{ + return bar(); +} + diff --git a/src/dyld-apple/unit-tests/test-cases/bundle-unlinkable/lib.c b/src/dyld-apple/unit-tests/test-cases/bundle-unlinkable/lib.c new file mode 100644 index 000000000..541b96423 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/bundle-unlinkable/lib.c @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ + + + +int bar() +{ + return 0; +} \ No newline at end of file diff --git a/src/dyld-apple/unit-tests/test-cases/bundle-unlinkable/main.c b/src/dyld-apple/unit-tests/test-cases/bundle-unlinkable/main.c new file mode 100644 index 000000000..bccdf8d6a --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/bundle-unlinkable/main.c @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2005-2009 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@ + */ +#include +#include +#include +#include + +#include "test.h" // PASS(), FAIL() + +typedef bool (*CheckFunc)(); + +int main() +{ +// NSCreateObjectFileImageFromMemory is only available on Mac OS X - not iPhone OS +#if __MAC_OS_X_VERSION_MIN_REQUIRED + NSObjectFileImage ofi; + if ( NSCreateObjectFileImageFromFile("test.bundle", &ofi) != NSObjectFileImageSuccess ) { + FAIL("NSCreateObjectFileImageFromFile failed"); + return 1; + } + + // make sure not-yet-linked-ofi is not visible through _dyld_get_image_name + int count = _dyld_image_count(); + for(int i=0; i < count; ++i) { + const char* name = _dyld_get_image_name(i); + if ( strcmp(name, "test.bundle") == 0 ) { + FAIL("unlinked test.bundle found via _dyld_get_image_name()"); + return 1; + } + } + + NSModule mod = NSLinkModule(ofi, "test.bundle", NSLINKMODULE_OPTION_RETURN_ON_ERROR); + if ( mod != NULL ) { + FAIL("NSLinkModule succeeded but should have failed"); + return 1; + } + + // make sure link-failed-ofi is not visible through _dyld_get_image_name + count = _dyld_image_count(); + for(int i=0; i < count; ++i) { + const char* name = _dyld_get_image_name(i); + if ( strcmp(name, "test.bundle") == 0 ) { + FAIL("failed linked test.bundle found via _dyld_get_image_name()"); + return 1; + } + } +#endif + PASS("bundle-unlinkable"); + return 0; +} \ No newline at end of file diff --git a/src/dyld-apple/unit-tests/test-cases/bundle-unload-keep-mapped/bundle.c b/src/dyld-apple/unit-tests/test-cases/bundle-unload-keep-mapped/bundle.c new file mode 100644 index 000000000..56690a786 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/bundle-unload-keep-mapped/bundle.c @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include + +// test to see if bss section is properly expanded + +static int mydata[10]; + +bool checkdata() +{ + return ( mydata[9] == 0 ); +} diff --git a/src/dyld-apple/unit-tests/test-cases/bundle-unload-keep-mapped/main.c b/src/dyld-apple/unit-tests/test-cases/bundle-unload-keep-mapped/main.c new file mode 100644 index 000000000..018ee9e7e --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/bundle-unload-keep-mapped/main.c @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2005-2009 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@ + */ +#include +#include +#include +#include + +#include "test.h" // PASS(), FAIL() + +typedef bool (*CheckFunc)(); + +int main() +{ +// NSCreateObjectFileImageFromMemory is only available on Mac OS X - not iPhone OS +#if __MAC_OS_X_VERSION_MIN_REQUIRED + NSObjectFileImage ofi; + if ( NSCreateObjectFileImageFromFile("test.bundle", &ofi) != NSObjectFileImageSuccess ) { + FAIL("NSCreateObjectFileImageFromFile failed"); + return 1; + } + + NSModule mod = NSLinkModule(ofi, "test.bundle", NSLINKMODULE_OPTION_NONE); + if ( mod == NULL ) { + FAIL("NSLinkModule failed"); + return 1; + } + + NSSymbol sym = NSLookupSymbolInModule(mod, "_checkdata"); + if ( sym == NULL ) { + FAIL("NSLookupSymbolInModule failed"); + return 1; + } + + CheckFunc func = NSAddressOfSymbol(sym); + if ( !func() ) { + FAIL("NSAddressOfSymbol failed"); + return 1; + } + + if ( !NSUnLinkModule(mod, NSUNLINKMODULE_OPTION_KEEP_MEMORY_MAPPED) ) { + FAIL("NSUnLinkModule failed"); + return 1; + } + + if ( !NSDestroyObjectFileImage(ofi) ) { + FAIL("NSDestroyObjectFileImage failed"); + return 1; + } + + // call function again, even though bundle is unloaded + func(); +#endif + + PASS("bundle-basic"); + return 0; +} \ No newline at end of file diff --git a/src/dyld-apple/unit-tests/test-cases/bundle-v-dylib/bar.c b/src/dyld-apple/unit-tests/test-cases/bundle-v-dylib/bar.c new file mode 100644 index 000000000..65f9d64ce --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/bundle-v-dylib/bar.c @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include + + +void bar() +{ + +} diff --git a/src/dyld-apple/unit-tests/test-cases/bundle-v-dylib/foo.c b/src/dyld-apple/unit-tests/test-cases/bundle-v-dylib/foo.c new file mode 100644 index 000000000..91fbe467b --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/bundle-v-dylib/foo.c @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include + + +void foo() +{ + +} diff --git a/src/dyld-apple/unit-tests/test-cases/bundle-v-dylib/main.c b/src/dyld-apple/unit-tests/test-cases/bundle-v-dylib/main.c new file mode 100644 index 000000000..aea5c6f6c --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/bundle-v-dylib/main.c @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2005-2009 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@ + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "test.h" // PASS(), FAIL() + +extern void bar(); + + +// NSCreateObjectFileImageFromMemory is only available on Mac OS X - not iPhone OS +#if __MAC_OS_X_VERSION_MIN_REQUIRED + +void loadAsBundleFromMemory(const char* path) +{ + int fd = open(path, O_RDONLY, 0); + if ( fd == -1 ) { + FAIL("bundle-v-dylib: open() failed"); + exit(0); + } + + struct stat stat_buf; + if ( fstat(fd, &stat_buf) == -1) { + FAIL("bundle-v-dylib: fstat() failed"); + exit(0); + } + + void* loadAddress = mmap(NULL, stat_buf.st_size, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0); + if ( loadAddress == ((void*)(-1)) ) { + FAIL("bundle-v-dylib: mmap() failed"); + exit(0); + } + + close(fd); + + NSObjectFileImage ofi; + if ( NSCreateObjectFileImageFromMemory(loadAddress, stat_buf.st_size, &ofi) == NSObjectFileImageSuccess ) { + FAIL("bundle-v-dylib: NSCreateObjectFileImageFromMemory() incorrectly allowed %s to be loaded", path); + exit(0); + } +} + +void loadAsBundle(const char* path) +{ + NSObjectFileImage ofi; + if ( NSCreateObjectFileImageFromFile(path, &ofi) == NSObjectFileImageSuccess ) { + FAIL("bundle-v-dylib: NSCreateObjectFileImageFromFile() incorrectly allowed %s to be loaded", path); + exit(0); + } +} + +void loadAsDylib(const char* path) +{ + if ( NSAddImage(path, NSADDIMAGE_OPTION_RETURN_ON_ERROR) != NULL ) { + FAIL("bundle-v-dylib: NSAddImage() incorrectly allowed %s to be loaded", path); + exit(0); + } +} + +#endif + +int main() +{ +#if __MAC_OS_X_VERSION_MIN_REQUIRED + int dummy; + + // verify that NSAddImage fails to load MH_BUNDLE + loadAsDylib("foo.bundle"); + + // verify that NSCreateObjectFileImageFromFile fails to load MH_DYLIB + loadAsBundle("foo.dylib"); + + // verify that NSCreateObjectFileImageFromFile fails to load MH_DYLIB already linked against main + loadAsBundle("bar.dylib"); + + // verify that bar.dylib was not unloaded when above failed + bar(); + + // try loading a dylib from memory using bundle API's + loadAsBundleFromMemory("foo2.dylib"); + + // verify that dyld data structures are not wanked by scanning all images + _dyld_get_image_header_containing_address(&dummy); +#endif + PASS("bundle-v-dylib"); + return 0; +} \ No newline at end of file diff --git a/src/dyld-apple/unit-tests/test-cases/bundle-weak/bundle.cxx b/src/dyld-apple/unit-tests/test-cases/bundle-weak/bundle.cxx new file mode 100644 index 000000000..fb3687187 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/bundle-weak/bundle.cxx @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include +#include +#include +#include + +bool test() +{ + try { + std::vector().at(1); + } + catch(const std::out_of_range&) { + //fprintf(stderr, "caught out_of_range\n"); + return true; + } + catch(...) { + //fprintf(stderr, "caught something\n"); + } + return false; +} diff --git a/src/dyld-apple/unit-tests/test-cases/bundle-weak/main.c b/src/dyld-apple/unit-tests/test-cases/bundle-weak/main.c new file mode 100644 index 000000000..814c4064d --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/bundle-weak/main.c @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include +#include +#include +#include + +#include "test.h" // PASS(), FAIL() + +typedef bool (*CheckFunc)(); + +int main() +{ + void* handle = dlopen("test.bundle", RTLD_LAZY); + if ( handle == NULL ) { + FAIL("dlopen(\"test.bundle\") failed: %s", dlerror()); + return 0; + } + + CheckFunc func = (CheckFunc)dlsym(handle, "_Z4testv"); + if ( func == NULL ) { + FAIL("dlsym(handle, \"__Z4testv\") failed"); + return 0; + } + + if ( func() ) + PASS("bundle-weak"); + else + FAIL("bundle-weak"); + + return 0; +} \ No newline at end of file diff --git a/src/dyld-apple/unit-tests/test-cases/concurrent-dlopen-initializers/foo.c b/src/dyld-apple/unit-tests/test-cases/concurrent-dlopen-initializers/foo.c new file mode 100644 index 000000000..3f2cbaf44 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/concurrent-dlopen-initializers/foo.c @@ -0,0 +1,4 @@ + +void foo() {} +void bar() {} +void baz() {} diff --git a/src/dyld-apple/unit-tests/test-cases/concurrent-dlopen-initializers/main.c b/src/dyld-apple/unit-tests/test-cases/concurrent-dlopen-initializers/main.c new file mode 100644 index 000000000..d5199c981 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/concurrent-dlopen-initializers/main.c @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2010 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include + +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +static void* work(void* libName) +{ + for (int i=0; i < 500; ++i) { + void* handle = dlopen((char*)libName, 0); + if ( handle == NULL ) { + FAIL("dlopen failed: %s", dlerror()); + exit(0); + } + dlclose(handle); + } + + return NULL; +} + +static const char* batchMappedHandler(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info info[]) +{ + usleep(10000); + return NULL; +} + + +int main() +{ + // tell dyld we want to know when images are mapped + dyld_register_image_state_change_handler(dyld_image_state_initialized, false, batchMappedHandler); + + // other thread dlopens and closes libfoo.dylib 500 times + pthread_t t1; + if ( pthread_create(&t1, NULL, work, "libfoo1.dylib") != 0 ) { + FAIL("pthread_create failed"); + exit(0); + } + + pthread_t t2; + if ( pthread_create(&t2, NULL, work, "libfoo2.dylib") != 0 ) { + FAIL("pthread_create failed"); + exit(0); + } + + pthread_t t3; + if ( pthread_create(&t3, NULL, work, "libfoo3.dylib") != 0 ) { + FAIL("pthread_create failed"); + exit(0); + } + + pthread_t t4; + if ( pthread_create(&t4, NULL, work, "libfoo4.dylib") != 0 ) { + FAIL("pthread_create failed"); + exit(0); + } + + void* result; + pthread_join(t1, &result); + pthread_join(t2, &result); + pthread_join(t3, &result); + pthread_join(t4, &result); + + PASS("concurrent-dlopen-initializers"); + return 0; +} diff --git a/src/dyld-apple/unit-tests/test-cases/coreSymbolication-notify/bar.c b/src/dyld-apple/unit-tests/test-cases/coreSymbolication-notify/bar.c new file mode 100644 index 000000000..63c34e06c --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/coreSymbolication-notify/bar.c @@ -0,0 +1,2 @@ + +int bar = 10; diff --git a/src/dyld-apple/unit-tests/test-cases/coreSymbolication-notify/foo.c b/src/dyld-apple/unit-tests/test-cases/coreSymbolication-notify/foo.c new file mode 100644 index 000000000..6924ac69d --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/coreSymbolication-notify/foo.c @@ -0,0 +1,3 @@ + +void foo() {} + diff --git a/src/dyld-apple/unit-tests/test-cases/coreSymbolication-notify/main.c b/src/dyld-apple/unit-tests/test-cases/coreSymbolication-notify/main.c new file mode 100644 index 000000000..56ca5f4b2 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/coreSymbolication-notify/main.c @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2009 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@ + */ +#include // fprintf(), NULL +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +int main(int argc, const char* argv[]) +{ + + if ( ! dlopen_preflight("foo.bundle") ) { + FAIL("coreSymbolication-notify foo.bundle should not be loadable"); + exit(0); + } + + void* h = dlopen("foo.bundle", RTLD_LAZY); + if ( h == NULL ) { + FAIL("coreSymbolication-notify foo.bundle failed: %s", dlerror()); + exit(0); + } + + dlclose(h); + + + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/crt-apple/main.c b/src/dyld-apple/unit-tests/test-cases/crt-apple/main.c new file mode 100644 index 000000000..73a192221 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/crt-apple/main.c @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2007-2010 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +#include <_simple.h> + +/// +/// verify parameters passed to initializer are same as passed to main() +/// verify that apple[0] parameter is correct by comparing to argv[1] +/// + +static int initializer_argc = 0; +static const char** initializer_argv = NULL; +static const char** initializer_env = NULL; +static const char** initializer_apple = NULL; + + +__attribute__((constructor)) +void init(int argc, const char* argv[], const char* env[], const char* apple[]) +{ + initializer_argc = argc; + initializer_argv = argv; + initializer_env = env; + initializer_apple = apple; +} + +int +main(int argc, const char* argv[], const char* env[], const char* apple[]) +{ + if ( argc != initializer_argc ) { + FAIL("crt-apple argc changed"); + exit(EXIT_SUCCESS); + } + + if ( argv != initializer_argv ) { + FAIL("crt-apple argv changed"); + exit(EXIT_SUCCESS); + } + + if ( env != initializer_env ) { + FAIL("crt-apple envp changed"); + exit(EXIT_SUCCESS); + } + + if ( apple != initializer_apple ) { + FAIL("crt-apple apple changed"); + exit(EXIT_SUCCESS); + } + + const char* execPath = _simple_getenv(apple, "executable_path"); + if ( execPath == NULL ) + FAIL("crt-apple apple[] missing executable_path="); + else if ( strcmp(execPath, argv[1]) == 0 ) + PASS("crt-apple %s", execPath); + else + FAIL("crt-apple %s", execPath); + + return EXIT_SUCCESS; +} + diff --git a/src/dyld-apple/unit-tests/test-cases/crt-argv-NULL/main.c b/src/dyld-apple/unit-tests/test-cases/crt-argv-NULL/main.c new file mode 100644 index 000000000..1adc975ef --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/crt-argv-NULL/main.c @@ -0,0 +1,14 @@ + + #include + + +int main(int argc, const char* argv[]) +{ + if ( argv[0] != NULL ) { + // re-exec with empty argv[] array + char* const emptyArgv[] = { NULL }; + execv(argv[0], emptyArgv); + } + + return 0; +} diff --git a/src/dyld-apple/unit-tests/test-cases/crt-custom/main.c b/src/dyld-apple/unit-tests/test-cases/crt-custom/main.c new file mode 100644 index 000000000..29c79cf8c --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/crt-custom/main.c @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2007-2009 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +// the value of flag is altered by mystart +int flag = 0; + + +#if __LP64__ + // for 64-bit binaries initializers are always called before entry point + #define ENTRY_BEFORE_INIT 0 +#else + #if __MAC_OS_X_VERSION_MIN_REQUIRED + // for pre 10.5, 32-bit binaries, entry point is called which then calls initializers + #if __MAC_OS_X_VERSION_MIN_REQUIRED < __MAC_10_5 + #define ENTRY_BEFORE_INIT 1 + #else + #define ENTRY_BEFORE_INIT 0 + #endif + #else + // for iPhoneOS, initializers are always called before entry point + #define ENTRY_BEFORE_INIT 0 + #endif +#endif + + +void __attribute__((constructor)) myinit() +{ +#if ENTRY_BEFORE_INIT + if ( flag != 2 ) { + FAIL("crt-custom entry point not called before initializer"); + exit(0); + } +#endif + flag = 1; +} + + +int main() +{ +#if ENTRY_BEFORE_INIT + if ( flag != 1 ) { + FAIL("crt-custom initializer not called"); + exit(0); + } +#else + if ( flag != 2 ) { + FAIL("crt-custom entry not called"); + exit(0); + } +#endif + + PASS("crt-custom"); + + return 0; +} diff --git a/src/dyld-apple/unit-tests/test-cases/crt-custom/mystart.s b/src/dyld-apple/unit-tests/test-cases/crt-custom/mystart.s new file mode 100644 index 000000000..c507706d6 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/crt-custom/mystart.s @@ -0,0 +1,44 @@ +# Built output for (null) +# Generated at (null) +# Using (null) configuration, (null) architecture for (null) target of (null) project + + + + + .text + .align 2 + .globl _mystart +_mystart: +#if __i386__ + call L1 +L1: popl %eax + movl L_flag$non_lazy_ptr-L1(%eax), %eax + movl $2, (%eax) + jmp start +#elif __x86_64__ + movl $2, _flag(%rip) + jmp start +#elif __ppc__ || __ppc64__ + li r0,2 + lis r2,ha16(_flag) + stw r0,lo16(_flag)(r2) + b start +#elif __arm__ + ldr r3, L4 + mov r2, #2 + add r3, pc + ldr r3, [r3] + str r2, [r3, #0] + b start +L4: .long L_flag$non_lazy_ptr-(L4-8) +#endif + +#if __i386__ || __arm__ + .section __DATA,__nl_symbol_ptr,non_lazy_symbol_pointers + .align 2 +L_flag$non_lazy_ptr: +.indirect_symbol _flag + .long 0 +#endif + + diff --git a/src/dyld-apple/unit-tests/test-cases/crt-libSystem/main.c b/src/dyld-apple/unit-tests/test-cases/crt-libSystem/main.c new file mode 100644 index 000000000..b7027df41 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/crt-libSystem/main.c @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2006 Apple Computer, 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include +#include +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +struct ProgramVars +{ + const void* mh; + int* NXArgcPtr; + char*** NXArgvPtr; + char*** environPtr; + char** __prognamePtr; +}; +static const struct ProgramVars* sVars; + +// global variables defeined in crt1.o +extern char** NXArgv; +extern int NXArgc; +extern char** environ; +extern char* __progname; + + +int +main(int argc, const char* argv[]) +{ + bool success = true; + + if ( _NSGetArgv() != &NXArgv ) { + FAIL("crt-libSystem _NSGetArgv() != &NXArgv (%p!=%p) for %s", _NSGetArgv(), &NXArgv, argv[0]); + success = false; + } + + if ( _NSGetArgc() != &NXArgc ) { + FAIL("crt-libSystem _NSGetArgc() != &NXArgc (%p!=%p) for %s", _NSGetArgc(), &NXArgc, argv[0]); + success = false; + } + + if ( _NSGetEnviron() != &environ ) { + FAIL("crt-libSystem _NSGetEnviron() != &environv (%p!=%p) for %s", _NSGetEnviron(), &environ, argv[0]); + success = false; + } + + if ( _NSGetProgname() != &__progname ) { + FAIL("crt-libSystem _NSGetProgname() != &__progname (%p!=%p) for %s", _NSGetProgname(), &__progname, argv[0]); + success = false; + } + + if ( _NSGetMachExecuteHeader() != &_mh_execute_header ) { + FAIL("crt-libSystem _NSGetMachExecuteHeader() != &_mh_execute_headerv (%p!=%p) for %s", _NSGetMachExecuteHeader(), &_mh_execute_header, argv[0]); + success = false; + } + + if ( sVars->NXArgvPtr != &NXArgv ) { + FAIL("crt-libSystem sVars->NXArgvPtr != &NXArg (%p!=%p) for %s", sVars->NXArgvPtr, &NXArgv, argv[0]); + success = false; + } + + if ( sVars->NXArgcPtr != &NXArgc ) { + FAIL("crt-libSystem sVars->NXArgcPtr != &NXArgc (%p!=%p) for %s", sVars->NXArgcPtr, &NXArgc, argv[0]); + success = false; + } + + if ( sVars->environPtr != &environ ) { + FAIL("crt-libSystem sVars->environPtr != &environ (%p!=%p) for %s", sVars->environPtr, &environ, argv[0]); + success = false; + } + + if ( sVars->__prognamePtr != &__progname ) { + FAIL("crt-libSystem sVars->__prognamePtr != &__progname (%p!=%p) for %s", sVars->__prognamePtr, &__progname, argv[0]); + success = false; + } + + if ( sVars->mh != &_mh_execute_header ) { + FAIL("crt-libSystem sVars->mh != &_mh_execute_header (%p!=%p) for %s", sVars->mh, &_mh_execute_header, argv[0]); + success = false; + } + + if ( success ) + PASS("crt-libSystem"); + + return EXIT_SUCCESS; +} + + + +void __attribute__((constructor)) +myInit(int argc, const char* argv[], const char* envp[], const char* apple[], const struct ProgramVars* vars) +{ + sVars = vars; +} + + + + + diff --git a/src/dyld-apple/unit-tests/test-cases/crt-result/bad.c b/src/dyld-apple/unit-tests/test-cases/crt-result/bad.c new file mode 100644 index 000000000..40cbb5427 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/crt-result/bad.c @@ -0,0 +1 @@ +int main() { return 1; } diff --git a/src/dyld-apple/unit-tests/test-cases/crt-result/good.c b/src/dyld-apple/unit-tests/test-cases/crt-result/good.c new file mode 100644 index 000000000..76e819701 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/crt-result/good.c @@ -0,0 +1 @@ +int main() { return 0; } diff --git a/src/dyld-apple/unit-tests/test-cases/cxa_finalize/foo.cxx b/src/dyld-apple/unit-tests/test-cases/cxa_finalize/foo.cxx new file mode 100644 index 000000000..e0731709e --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/cxa_finalize/foo.cxx @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2009 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@ + */ + +#include + +class A +{ +public: + A() { f = 10; } + ~A() { if ( f == 0 ) abort(); f = 0; } + int get() { return f; } +private: + int f; +}; + +A a; + + +int test() +{ + return a.get(); +} diff --git a/src/dyld-apple/unit-tests/test-cases/cxa_finalize/main.c b/src/dyld-apple/unit-tests/test-cases/cxa_finalize/main.c new file mode 100644 index 000000000..d1b62d5fc --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/cxa_finalize/main.c @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + + +int main() +{ + void* handle = dlopen("libfoo.dylib", RTLD_LAZY); + if ( handle == NULL ) { + FAIL("dlopen(\"%s\") failed with: %s", "libfoo.dylib", dlerror()); + return EXIT_SUCCESS; + } + + dlclose(handle); + + PASS("cxa_finalize"); + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/deadlock/bar.c b/src/dyld-apple/unit-tests/test-cases/deadlock/bar.c new file mode 100644 index 000000000..ea63b5f27 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/deadlock/bar.c @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ + + +void bar() +{ + +} diff --git a/src/dyld-apple/unit-tests/test-cases/deadlock/foo.c b/src/dyld-apple/unit-tests/test-cases/deadlock/foo.c new file mode 100644 index 000000000..8b9c637a0 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/deadlock/foo.c @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ + + +void foo() +{ + +} diff --git a/src/dyld-apple/unit-tests/test-cases/deadlock/main.c b/src/dyld-apple/unit-tests/test-cases/deadlock/main.c new file mode 100644 index 000000000..21e64462c --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/deadlock/main.c @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2005-2009 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@ + */ +#include +#include +#include +#include +#include +#include + +#include "test.h" + +/// rdar://problem/3811777 + +// barrier thread 1 thread 2 +// add image +// 1 +// acquire sMyLock +// 2 +// in callback acquire sMyLock call lazy pointer +// release sMyLock release sMyLock + +extern void foo(); + +#if __MAC_OS_X_VERSION_MIN_REQUIRED + +static volatile int sBarrier = 0; +static pthread_mutex_t sBarrierMutex; +static pthread_cond_t sBarrierFree; + +static void blockUntilBarrier(int n) +{ + pthread_mutex_lock(&sBarrierMutex); + while ( sBarrier < n ) + pthread_cond_wait(&sBarrierFree, &sBarrierMutex); + pthread_mutex_unlock(&sBarrierMutex); +} + +static void advanceToBarrier(int n) +{ + pthread_mutex_lock(&sBarrierMutex); + sBarrier = n; + pthread_cond_broadcast(&sBarrierFree); + pthread_mutex_unlock(&sBarrierMutex); +} + + + + + +static pthread_mutex_t sMyLock; + +static void* thread2(void* arg) +{ + // thread 2 + blockUntilBarrier(1); + pthread_mutex_lock(&sMyLock); + advanceToBarrier(2); + foo(); + pthread_mutex_unlock(&sMyLock); + return NULL; +} + + + +static void myImageHandler(const struct mach_header *mh, intptr_t vmaddr_slide) +{ + // thread 1 + if ( NSLookupSymbolInImage(mh, "_bar", 0) != NULL ) { + advanceToBarrier(1); + blockUntilBarrier(2); + pthread_mutex_lock(&sMyLock); + pthread_mutex_unlock(&sMyLock); + } +} +#endif + +int main() +{ +#if __MAC_OS_X_VERSION_MIN_REQUIRED + pthread_mutex_init(&sBarrierMutex, NULL); + pthread_cond_init(&sBarrierFree, NULL); + pthread_mutex_init(&sMyLock, NULL); + + // self-terminate this process if it locks up for two seconds + alarm(2); + + advanceToBarrier(0); + + pthread_t pthread2; + if ( pthread_create(&pthread2, NULL, thread2, NULL) != 0 ) { + FAIL("pthread_create failed"); + exit(0); + } + + // thread 1 + _dyld_register_func_for_add_image(&myImageHandler); + NSAddImage("bar.dylib", 0); +#endif + + PASS("deadlock"); + return 0; +} diff --git a/src/dyld-apple/unit-tests/test-cases/dladdr-stripped/main.c b/src/dyld-apple/unit-tests/test-cases/dladdr-stripped/main.c new file mode 100644 index 000000000..4f8901a4f --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dladdr-stripped/main.c @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2009 Apple Computer, 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +/// +/// verify dladdr() returns NULL for a symbol name in a fully stripped +/// main executable (and not _mh_execute_header+nnn). +/// + +int main() +{ + Dl_info info; + if ( dladdr(&main, &info) == 0 ) { + FAIL("dladdr(&main, xx) failed"); + exit(0); + } + + if ( info.dli_sname != NULL ){ + FAIL("dladdr() returned: \"%s\" instead of NULL", info.dli_sname); + exit(0); + } + + PASS("dladdr-stripped"); + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/dladdr/main.c b/src/dyld-apple/unit-tests/test-cases/dladdr/main.c new file mode 100644 index 000000000..c75c621a1 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dladdr/main.c @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2005-2008 Apple Computer, 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + + +int bar() +{ + return 2; +} + +static int foo() +{ + return 3; +} + +__attribute__((visibility("hidden"))) int hide() +{ + return 4; +} + +// checks global symbol +static void verifybar() +{ + Dl_info info; + if ( dladdr(&bar, &info) == 0 ) { + FAIL("dladdr(&bar, xx) failed"); + exit(0); + } + if ( strcmp(info.dli_sname, "bar") != 0 ) { + if ( strcmp(info.dli_sname, "_bar") == 0 ) { + XFAIL("dladdr()->dli_sname is \"%s\" instead of \"bar\"", info.dli_sname); + } + else { + FAIL("dladdr()->dli_sname is \"%s\" instead of \"bar\"", info.dli_sname); + exit(0); + } + } + if ( info.dli_saddr != &bar) { + FAIL("dladdr()->dli_saddr is not &bar"); + exit(0); + } +#if __MAC_OS_X_VERSION_MIN_REQUIRED + if ( info.dli_fbase != _dyld_get_image_header_containing_address(&bar) ) { + FAIL("dladdr()->dli_fbase is not image that contains &bar"); + exit(0); + } +#endif +} + +// checks local symbol +static void verifyfoo() +{ + Dl_info info; + if ( dladdr(&foo, &info) == 0 ) { + FAIL("dladdr(&foo, xx) failed"); + exit(0); + } + if ( strcmp(info.dli_sname, "foo") != 0 ) { + FAIL("dladdr()->dli_sname is \"%s\" instead of \"foo\"", info.dli_sname); + exit(0); + } + if ( info.dli_saddr != &foo) { + FAIL("dladdr()->dli_saddr is not &foo"); + exit(0); + } +#if __MAC_OS_X_VERSION_MIN_REQUIRED + if ( info.dli_fbase != _dyld_get_image_header_containing_address(&foo) ) { + FAIL("dladdr()->dli_fbase is not image that contains &foo"); + exit(0); + } +#endif +} + +// checks hidden symbol +static void verifyhide() +{ + Dl_info info; + if ( dladdr(&hide, &info) == 0 ) { + FAIL("dladdr(&hide, xx) failed"); + exit(0); + } + if ( strcmp(info.dli_sname, "hide") != 0 ) { + FAIL("dladdr()->dli_sname is \"%s\" instead of \"hide\"", info.dli_sname); + exit(0); + } + if ( info.dli_saddr != &hide) { + FAIL("dladdr()->dli_saddr is not &hide"); + exit(0); + } +#if __MAC_OS_X_VERSION_MIN_REQUIRED + if ( info.dli_fbase != _dyld_get_image_header_containing_address(&hide) ) { + FAIL("dladdr()->dli_fbase is not image that contains &hide"); + exit(0); + } +#endif +} + + +int main() +{ + verifybar(); + verifyhide(); + verifyfoo(); + + + PASS("dladdr"); + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlclose-basic/foo.c b/src/dyld-apple/unit-tests/test-cases/dlclose-basic/foo.c new file mode 100644 index 000000000..39ab8be54 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlclose-basic/foo.c @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +int foo() +{ + return 1; +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlclose-basic/main.c b/src/dyld-apple/unit-tests/test-cases/dlclose-basic/main.c new file mode 100644 index 000000000..ee7c12261 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlclose-basic/main.c @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + + +int main() +{ + // regular open + void* handle = dlopen("test.bundle", RTLD_LAZY); + if ( handle == NULL ) { + FAIL("dlopen(\"test.bundle\", RTLD_LAZY) failed"); + exit(1); + } + + // regular close + int result = dlclose(handle); + if ( result != 0 ) { + FAIL("dlclose() failed"); + exit(1); + } + + // now try to close again (expect to fail) + result = dlclose(handle); + if ( result != -1 ) { + FAIL("dlclose() on released handle should have returned -1, but returned %d", result); + exit(1); + } + + // now try to close a bad handle value (expect to fail) + result = dlclose((void*)0x12345); + if ( result != -1 ) { + FAIL("dlclose() on bogus handle should have returned -1, but returned %d", result); + exit(1); + } + + PASS("dlclose-basic"); + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlclose-bundle-unload/foo.c b/src/dyld-apple/unit-tests/test-cases/dlclose-bundle-unload/foo.c new file mode 100644 index 000000000..81f7dcfc9 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlclose-bundle-unload/foo.c @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ + + +int foo() +{ + return 10; +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlclose-bundle-unload/main.c b/src/dyld-apple/unit-tests/test-cases/dlclose-bundle-unload/main.c new file mode 100644 index 000000000..3e0798aeb --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlclose-bundle-unload/main.c @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +int main() +{ + // open same bundle three times + void* handle1 = dlopen("test.bundle", RTLD_LAZY); + if ( handle1 == NULL ) { + FAIL("dlclose-bundle-unload: dlopen(\"test.bundle\", RTLD_LAZY) failed with dlerror()=%s", dlerror()); + exit(0); + } + + void* handle2 = dlopen("test.bundle", RTLD_LAZY); + if ( handle2 == NULL ) { + FAIL("dlclose-bundle-unload: dlopen(\"test.bundle\", RTLD_LAZY) failed with dlerror()=%s", dlerror()); + exit(0); + } + + void* handle3 = dlopen("test.bundle", RTLD_LAZY); + if ( handle3 == NULL ) { + FAIL("dlclose-bundle-unload: dlopen(\"test.bundle\", RTLD_LAZY) failed with dlerror()=%s", dlerror()); + exit(0); + } + + // get symbol + void* sym = dlsym(handle1, "foo"); + if ( sym == NULL ) { + FAIL("dlclose-bundle-unload: dlsym(handle1, \"foo\") failed"); + exit(0); + } + + // close same bundle three times + if ( dlclose(handle3) != 0 ) { + FAIL("dlclose-bundle-unload: dlclose(handle3) != 0, dlerrr()=%s", dlerror()); + exit(0); + } + + if ( dlclose(handle2) != 0 ) { + FAIL("dlclose-bundle-unload: dlclose(handle2) != 0, dlerrr()=%s", dlerror()); + exit(0); + } + + if ( dlclose(handle1) != 0 ) { + FAIL("dlclose-bundle-unload: dlclose(handle1) != 0, dlerrr()=%s", dlerror()); + exit(0); + } + + + // extra close should fail + if ( dlclose(handle1) == 0 ) { + FAIL("dlclose-bundle-unload: dlclose(handle4) == 0, but should have failed"); + exit(0); + } + + + + PASS("dlclose-bundle-unload"); + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlclose-dylib-dynamic-ref/bar.c b/src/dyld-apple/unit-tests/test-cases/dlclose-dylib-dynamic-ref/bar.c new file mode 100644 index 000000000..d0fd23c3a --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlclose-dylib-dynamic-ref/bar.c @@ -0,0 +1,8 @@ +__attribute__((weak)) +int mydata; + + +int bar() +{ + return mydata; +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlclose-dylib-dynamic-ref/baz.c b/src/dyld-apple/unit-tests/test-cases/dlclose-dylib-dynamic-ref/baz.c new file mode 100644 index 000000000..a3a1b44dd --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlclose-dylib-dynamic-ref/baz.c @@ -0,0 +1,8 @@ +__attribute__((weak)) +int mydata; + + +int baz() +{ + return mydata; +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlclose-dylib-dynamic-ref/foo.c b/src/dyld-apple/unit-tests/test-cases/dlclose-dylib-dynamic-ref/foo.c new file mode 100644 index 000000000..899bb443c --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlclose-dylib-dynamic-ref/foo.c @@ -0,0 +1,8 @@ +__attribute__((weak)) +int mydata; + + +int foo() +{ + return mydata; +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlclose-dylib-dynamic-ref/main.c b/src/dyld-apple/unit-tests/test-cases/dlclose-dylib-dynamic-ref/main.c new file mode 100644 index 000000000..9c39733a9 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlclose-dylib-dynamic-ref/main.c @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2013 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + + + +int main() +{ + Dl_info info; + + // load foo + void* handleFoo = dlopen("libfoo.dylib", RTLD_LAZY); + if ( handleFoo == NULL ) { + FAIL("dlclose-dylib-dynamic-ref: dlopen(\"libfoo.dylib\", RTLD_LAZY) failed with dlerror()=%s", dlerror()); + exit(0); + } + + // load bar + void* handleBar = dlopen("libbar.dylib", RTLD_LAZY); + if ( handleBar == NULL ) { + FAIL("dlclose-dylib-dynamic-ref: dlopen(\"libbar.dylib\", RTLD_LAZY) failed with dlerror()=%s", dlerror()); + exit(0); + } + + // load baz + void* handleBaz = dlopen("libbaz.dylib", RTLD_LAZY); + if ( handleBaz == NULL ) { + FAIL("dlclose-dylib-dynamic-ref: dlopen(\"libbaz.dylib\", RTLD_LAZY) failed with dlerror()=%s", dlerror()); + exit(0); + } + + + void* sym_foo = dlsym(handleFoo, "foo"); + if ( sym_foo == NULL ) { + FAIL("dlclose-dylib-dynamic-ref: dlsym(handleFoo, \"foo\") failed"); + exit(0); + } + + void* sym_bar = dlsym(handleBar, "bar"); + if ( sym_bar == NULL ) { + FAIL("dlclose-dylib-dynamic-ref: dlsym(handleBar, \"bar\") failed"); + exit(0); + } + + void* sym_baz = dlsym(handleBaz, "baz"); + if ( sym_baz == NULL ) { + FAIL("dlclose-dylib-dynamic-ref: dlsym(handleBaz, \"baz\") failed"); + exit(0); + } + + // since foo loaded first, bar and baz should have a dynamic reference to foo because all + // the weak mydata symbols were coaleseced to the one in foo. + + + if ( dlclose(handleFoo) != 0 ) { + FAIL("dlclose-dylib-dynamic-ref: dlclose(handleBar) != 0, dlerrr()=%s", dlerror()); + exit(0); + } + + // sym_foo should still be accessible via dladdr() because libbar and libbaz + // have dynamic references to libfoo. + if ( dladdr(sym_foo, &info) == 0 ) { + FAIL("dlclose-dylib-dynamic-ref: dladdr(sym_base) == 0, but should have succeeded"); + exit(0); + } + + + if ( dlclose(handleBar) != 0 ) { + FAIL("dlclose-dylib-dynamic-ref: dlclose(handleBar) != 0, dlerrr()=%s", dlerror()); + exit(0); + } + + + // sym_bar should not be accessible via dladdr() because libbar was dlclose'ed + if ( dladdr(sym_bar, &info) != 0 ) { + FAIL("dlclose-dylib-dynamic-ref: dladdr(sym_bar) != 0, but should have failed"); + exit(0); + } + + // sym_foo should still be accessible via dladdr() because of external libbaz + // has a dynamic references to libfoo. + if ( dladdr(sym_foo, &info) == 0 ) { + FAIL("dlclose-dylib-dynamic-ref: dladdr(sym_base) == 0, but should have succeeded"); + exit(0); + } + + + if ( dlclose(handleBaz) != 0 ) { + FAIL("dlclose-dylib-dynamic-ref: dlclose(handleBar) != 0, dlerrr()=%s", dlerror()); + exit(0); + } + + // sym_baz should not be accessible via dladdr() because libbar was dlclose'ed + if ( dladdr(sym_baz, &info) != 0 ) { + FAIL("dlclose-dylib-dynamic-ref: dladdr(sym_baz) != 0, but should have failed"); + exit(0); + } + + + // sym_foo should finally be inaccessible via dladdr() because all dynamic references to libfoo are gone + if ( dladdr(sym_foo, &info) != 0 ) { + FAIL("dlclose-dylib-dynamic-ref: dladdr(sym_foo) == 0, but should have succeeded"); + exit(0); + } + + + PASS("dlclose-dylib-dynamic-ref"); + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlclose-dylib-ref-count/bar.c b/src/dyld-apple/unit-tests/test-cases/dlclose-dylib-ref-count/bar.c new file mode 100644 index 000000000..8312b899e --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlclose-dylib-ref-count/bar.c @@ -0,0 +1,6 @@ + + +int bar() +{ + return 10; +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlclose-dylib-ref-count/base.c b/src/dyld-apple/unit-tests/test-cases/dlclose-dylib-ref-count/base.c new file mode 100644 index 000000000..e7e0d08ab --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlclose-dylib-ref-count/base.c @@ -0,0 +1 @@ +void base() { } diff --git a/src/dyld-apple/unit-tests/test-cases/dlclose-dylib-ref-count/foo.c b/src/dyld-apple/unit-tests/test-cases/dlclose-dylib-ref-count/foo.c new file mode 100644 index 000000000..8184cd129 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlclose-dylib-ref-count/foo.c @@ -0,0 +1,5 @@ + +int foo() +{ + return 10; +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlclose-dylib-ref-count/main.c b/src/dyld-apple/unit-tests/test-cases/dlclose-dylib-ref-count/main.c new file mode 100644 index 000000000..5902d821b --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlclose-dylib-ref-count/main.c @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2012 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + + + +int main() +{ + Dl_info info; + + // load bar + void* handleBar = dlopen("libbar.dylib", RTLD_LAZY); + if ( handleBar == NULL ) { + FAIL("dlclose-dylib-ref-count: dlopen(\"libbar.dylib\", RTLD_LAZY) failed with dlerror()=%s", dlerror()); + exit(0); + } + + // load foo + void* handleFoo = dlopen("libfoo.dylib", RTLD_LAZY); + if ( handleFoo == NULL ) { + FAIL("dlclose-dylib-ref-count: dlopen(\"libfoo.dylib\", RTLD_LAZY) failed with dlerror()=%s", dlerror()); + exit(0); + } + + void* sym_base = dlsym(handleBar, "base"); + if ( sym_base == NULL ) { + FAIL("dlclose-dylib-ref-count: dlsym(handleBar, \"base\") failed"); + exit(0); + } + + void* sym_foo = dlsym(handleFoo, "foo"); + if ( sym_foo == NULL ) { + FAIL("dlclose-dylib-ref-count: dlsym(handleBar, \"base\") failed"); + exit(0); + } + + void* sym_bar = dlsym(handleBar, "bar"); + if ( sym_bar == NULL ) { + FAIL("dlclose-dylib-ref-count: dlsym(handleBar, \"base\") failed"); + exit(0); + } + + if ( dlclose(handleBar) != 0 ) { + FAIL("dlclose-dylib-ref-count: dlclose(handleBar) != 0, dlerrr()=%s", dlerror()); + exit(0); + } + + // sym_base should still be accessible via dladdr() because of external reference from libfoo.dylib + if ( dladdr(sym_base, &info) == 0 ) { + FAIL("dlclose-dylib-ref-count: dladdr(sym_base) == 0, but should have succeeded"); + exit(0); + } + + // sym_foo should still be accessible via dladdr() because libfoo was dlopen'ed + if ( dladdr(sym_foo, &info) == 0 ) { + FAIL("dlclose-dylib-ref-count: dladdr(sym_foo) == 0, but should have succeeded"); + exit(0); + } + + // sym_bar should not be accessible via dladdr() because libbar was dlclose'ed + if ( dladdr(sym_bar, &info) != 0 ) { + FAIL("dlclose-dylib-ref-count: dladdr(sym_bar) != 0, but should have failed"); + exit(0); + } + + if ( dlclose(handleFoo) != 0 ) { + FAIL("dlclose-dylib-ref-count: dlclose(handleBar) != 0, dlerrr()=%s", dlerror()); + exit(0); + } + + // sym_base should no longer be accessible via dladdr() because libfoo and libbar both closed + if ( dladdr(sym_base, &info) != 0 ) { + FAIL("dlclose-dylib-ref-count: dladdr(base) != 0, but should have failed"); + exit(0); + } + + // sym_foo should still be accessible via dladdr() because libfoo was dlclose'ed + if ( dladdr(sym_foo, &info) != 0 ) { + FAIL("dlclose-dylib-ref-count: dladdr(sym_foo) != 0, but should have failed"); + exit(0); + } + + PASS("dlclose-dylib-ref-count"); + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlclose-dylib-terminators/bar.cpp b/src/dyld-apple/unit-tests/test-cases/dlclose-dylib-terminators/bar.cpp new file mode 100644 index 000000000..281516768 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlclose-dylib-terminators/bar.cpp @@ -0,0 +1,17 @@ +extern "C" int bazData; + +class BazUser { +public: + BazUser() { } + ~BazUser() { bazData = 0; } +}; + + +BazUser b; + + +int bar() +{ + return bazData; +} + diff --git a/src/dyld-apple/unit-tests/test-cases/dlclose-dylib-terminators/baz.c b/src/dyld-apple/unit-tests/test-cases/dlclose-dylib-terminators/baz.c new file mode 100644 index 000000000..b5a740ecd --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlclose-dylib-terminators/baz.c @@ -0,0 +1,7 @@ +int bazData = 5; + + +int baz() +{ + return bazData; +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlclose-dylib-terminators/foo.cpp b/src/dyld-apple/unit-tests/test-cases/dlclose-dylib-terminators/foo.cpp new file mode 100644 index 000000000..ead0e832f --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlclose-dylib-terminators/foo.cpp @@ -0,0 +1,23 @@ + +extern void bar(); + + + +class A { +public: + A() { bar(); } + ~A() { bar(); } +}; + + +// Create global object which will have its destructor run when +// this dylib is unloaded. The destructor will call into libbar, +// so libbar.dylib can't be unloaded before this dylib. +A a; + + + + +void foo() +{ +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlclose-dylib-terminators/main.c b/src/dyld-apple/unit-tests/test-cases/dlclose-dylib-terminators/main.c new file mode 100644 index 000000000..34d2d0d7a --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlclose-dylib-terminators/main.c @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2013 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + + + +int main() +{ + // load foo + void* handleFoo = dlopen("libfoo.dylib", RTLD_LAZY); + if ( handleFoo == NULL ) { + FAIL("dlclose-dylib-order: dlopen(\"libfoo.dylib\", RTLD_LAZY) failed with dlerror()=%s", dlerror()); + exit(0); + } + + if ( dlclose(handleFoo) != 0 ) { + FAIL("dlclose-dylib-order: dlclose(handleFoo) != 0, dlerrr()=%s", dlerror()); + exit(0); + } + + + PASS("dlclose-dylib-order"); + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlclose-dylib-unload/bar.c b/src/dyld-apple/unit-tests/test-cases/dlclose-dylib-unload/bar.c new file mode 100644 index 000000000..817b8cd58 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlclose-dylib-unload/bar.c @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ + + +int bar() +{ + return 10; +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlclose-dylib-unload/foo.c b/src/dyld-apple/unit-tests/test-cases/dlclose-dylib-unload/foo.c new file mode 100644 index 000000000..81f7dcfc9 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlclose-dylib-unload/foo.c @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ + + +int foo() +{ + return 10; +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlclose-dylib-unload/main.c b/src/dyld-apple/unit-tests/test-cases/dlclose-dylib-unload/main.c new file mode 100644 index 000000000..5e48c00c3 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlclose-dylib-unload/main.c @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + + + + +void verifyfoo() +{ + // open same dylib three times + void* handle1 = dlopen("libfoo.dylib", RTLD_LAZY); + if ( handle1 == NULL ) { + FAIL("dlclose-dylib-unload: dlopen(\"libfoo.dylib\", RTLD_LAZY) failed with dlerror()=%s", dlerror()); + exit(0); + } + + void* handle2 = dlopen("libfoo.dylib", RTLD_LAZY); + if ( handle2 == NULL ) { + FAIL("dlclose-dylib-unload: dlopen(\"libfoo.dylib\", RTLD_LAZY) failed with dlerror()=%s", dlerror()); + exit(0); + } + + void* handle3 = dlopen("libfoo.dylib", RTLD_LAZY); + if ( handle3 == NULL ) { + FAIL("dlclose-dylib-unload: dlopen(\"libfoo.dylib\", RTLD_LAZY) failed with dlerror()=%s", dlerror()); + exit(0); + } + + // get symbol + void* sym = dlsym(handle1, "foo"); + if ( sym == NULL ) { + FAIL("dlclose-dylib-unload: dlsym(handle1, \"foo\") failed"); + exit(0); + } + + // close same bundle three times + if ( dlclose(handle3) != 0 ) { + FAIL("dlclose-dylib-unload: dlclose(handle3) != 0, dlerrr()=%s", dlerror()); + exit(0); + } + + if ( dlclose(handle2) != 0 ) { + FAIL("dlclose-dylib-unload: dlclose(handle2) != 0, dlerrr()=%s", dlerror()); + exit(0); + } + + if ( dlclose(handle1) != 0 ) { + FAIL("dlclose-dylib-unload: dlclose(handle1) != 0, dlerrr()=%s", dlerror()); + exit(0); + } + + // symbol foo should no longer be accessible via dladdr() + Dl_info info; + if ( dladdr(sym, &info) != 0 ) { + FAIL("dlclose-dylib-unload: dladdr(foo_sym) != 0, but should have failed"); + //exit(0); + } + + // extra close should fail + if ( dlclose(handle1) == 0 ) { + FAIL("dlclose-dylib-unload: dlclose(foo_handle4) == 0, but should have failed"); + //exit(0); + } + +} + + + +void verifybar() +{ + // open same dylib three times + void* handle1 = dlopen("libbar.dylib", RTLD_LAZY); + if ( handle1 == NULL ) { + FAIL("dlclose-dylib-unload: dlopen(\"libbar.dylib\", RTLD_LAZY) failed with dlerror()=%s", dlerror()); + exit(0); + } + + void* handle2 = dlopen("libbar.dylib", RTLD_LAZY); + if ( handle2 == NULL ) { + FAIL("dlclose-dylib-unload: dlopen(\"libbar.dylib\", RTLD_LAZY) failed with dlerror()=%s", dlerror()); + exit(0); + } + + void* handle3 = dlopen("libbar.dylib", RTLD_LAZY); + if ( handle3 == NULL ) { + FAIL("dlclose-dylib-unload: dlopen(\"libbar.dylib\", RTLD_LAZY) failed with dlerror()=%s", dlerror()); + exit(0); + } + + // get symbol + void* sym = dlsym(handle1, "bar"); + if ( sym == NULL ) { + FAIL("dlclose-dylib-unload: dlsym(handle1, \"bar\") failed"); + exit(0); + } + + // close same bundle three times + if ( dlclose(handle3) != 0 ) { + FAIL("dlclose-dylib-unload: dlclose(handle3) != 0, dlerrr()=%s", dlerror()); + exit(0); + } + + if ( dlclose(handle2) != 0 ) { + FAIL("dlclose-dylib-unload: dlclose(handle2) != 0, dlerrr()=%s", dlerror()); + exit(0); + } + + if ( dlclose(handle1) != 0 ) { + FAIL("dlclose-dylib-unload: dlclose(handle1) != 0, dlerrr()=%s", dlerror()); + exit(0); + } + + // symbol bar should still longer be accessible via dladdr() because of external reference to libbar.dylib + Dl_info info; + if ( dladdr(sym, &info) == 0 ) { + FAIL("dlclose-dylib-unload: dladdr(bar_sym) == 0, but should have succeeded"); + exit(0); + } + + // extra close should fail + if ( dlclose(handle1) == 0 ) { + FAIL("dlclose-dylib-unload: dlclose(bar_handle4) == 0, but should have failed"); + exit(0); + } +} + + +// verify libbar.dylib can be loaded and unloaded +// verify libbar.dylib can be loaded, but cannot be unloaded (because main executable links against it) +int main() +{ + verifyfoo(); + verifybar(); + + PASS("dlclose-dylib-unload"); + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlclose-order/bar.cxx b/src/dyld-apple/unit-tests/test-cases/dlclose-order/bar.cxx new file mode 100644 index 000000000..94fddfe49 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlclose-order/bar.cxx @@ -0,0 +1,12 @@ +#include "base.h" + + +class Bar { +public: + Bar() { if ( bazInitied) barInitied = true; } + ~Bar() { if ( barInitied && !bazTeminated ) barTeminated = true; } +}; + + +Bar bar; + diff --git a/src/dyld-apple/unit-tests/test-cases/dlclose-order/base.c b/src/dyld-apple/unit-tests/test-cases/dlclose-order/base.c new file mode 100644 index 000000000..d128ed885 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlclose-order/base.c @@ -0,0 +1,8 @@ +#include "base.h" + +bool barInitied = false; +bool barTeminated = false; + +bool bazInitied = false; +bool bazTeminated = false; + diff --git a/src/dyld-apple/unit-tests/test-cases/dlclose-order/base.h b/src/dyld-apple/unit-tests/test-cases/dlclose-order/base.h new file mode 100644 index 000000000..75b5960d0 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlclose-order/base.h @@ -0,0 +1,9 @@ +#include + +extern bool barInitied; +extern bool barTeminated; + +extern bool bazInitied; +extern bool bazTeminated; + + diff --git a/src/dyld-apple/unit-tests/test-cases/dlclose-order/baz.cxx b/src/dyld-apple/unit-tests/test-cases/dlclose-order/baz.cxx new file mode 100644 index 000000000..415a9c6aa --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlclose-order/baz.cxx @@ -0,0 +1,12 @@ +#include "base.h" + +class Baz { +public: + Baz() { bazInitied = true; } + ~Baz() { bazTeminated = true; } + +}; + + +Baz baz; + diff --git a/src/dyld-apple/unit-tests/test-cases/dlclose-order/foo.c b/src/dyld-apple/unit-tests/test-cases/dlclose-order/foo.c new file mode 100644 index 000000000..39ab8be54 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlclose-order/foo.c @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +int foo() +{ + return 1; +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlclose-order/main.c b/src/dyld-apple/unit-tests/test-cases/dlclose-order/main.c new file mode 100644 index 000000000..c776426cb --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlclose-order/main.c @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2013 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +#include "base.h" + + +int main(int argc, const char* argv[]) +{ + // regular open + void* handle = dlopen(argv[1], RTLD_LAZY); + if ( handle == NULL ) { + FAIL("dlclose-order: dlopen(\"libfoo.dylib\", RTLD_LAZY) failed"); + return EXIT_SUCCESS; + } + + // regular close + int result = dlclose(handle); + if ( result != 0 ) { + FAIL("dlclose-order: dlclose() failed"); + return EXIT_SUCCESS; + } + + if ( !bazTeminated ) { + FAIL("dlclose-order: baz not terminated"); + return EXIT_SUCCESS; + } + + if ( !barTeminated ) { + FAIL("dlclose-order: bar not terminated"); + return EXIT_SUCCESS; + } + + PASS("dlclose-order"); + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlclose-terminator-dlclose/bar.c b/src/dyld-apple/unit-tests/test-cases/dlclose-terminator-dlclose/bar.c new file mode 100644 index 000000000..6cd208d24 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlclose-terminator-dlclose/bar.c @@ -0,0 +1,28 @@ +/* + * 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@ + */ + + +int bar() +{ + return 10; +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlclose-terminator-dlclose/foo.c b/src/dyld-apple/unit-tests/test-cases/dlclose-terminator-dlclose/foo.c new file mode 100644 index 000000000..e12dc61e6 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlclose-terminator-dlclose/foo.c @@ -0,0 +1,48 @@ +/* + * 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@ + */ +#include +#include + + +// Leopard (9a499): dyld crash with recursive calls to dlclose() + +static void* handle = NULL; + +static void __attribute__((constructor)) myinit() +{ + handle = dlopen("libbar.dylib", RTLD_LAZY); +} + +static void __attribute__((destructor)) myterm() +{ + // myterm() is called within dlclose. + // now call dlclose() on another (chainded dylib) to test dlclose is re-entrant + if ( handle != NULL ) + dlclose(handle); +} + + +int foo() +{ + return 10; +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlclose-terminator-dlclose/main.c b/src/dyld-apple/unit-tests/test-cases/dlclose-terminator-dlclose/main.c new file mode 100644 index 000000000..90ea77908 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlclose-terminator-dlclose/main.c @@ -0,0 +1,47 @@ +/* + * 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + + +int main() +{ + void* handle = dlopen("libfoo.dylib", RTLD_LAZY); + if ( handle == NULL ) { + FAIL("dlclose-dylib-unload: dlopen(\"libfoo.dylib\", RTLD_LAZY) failed with dlerror()=%s", dlerror()); + exit(0); + } + + + if ( dlclose(handle) != 0 ) { + FAIL("dlclose-dylib-unload: dlclose(handle) != 0, dlerrr()=%s", dlerror()); + exit(0); + } + + PASS("dlclose-terminator-dlclose"); + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlclose-unload-c++/bar.c b/src/dyld-apple/unit-tests/test-cases/dlclose-unload-c++/bar.c new file mode 100644 index 000000000..c69ec99be --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlclose-unload-c++/bar.c @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ + + +static void local() +{ +} + +extern void* common() __attribute__((weak)); + +void* common() +{ + return &local; +} + +void* bar() +{ + return common(); +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlclose-unload-c++/foo.c b/src/dyld-apple/unit-tests/test-cases/dlclose-unload-c++/foo.c new file mode 100644 index 000000000..b6bd1e433 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlclose-unload-c++/foo.c @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ + + +static void local() +{ +} + +extern void* common() __attribute__((weak)); + +void* common() +{ + return &local; +} + +void* foo() +{ + return common(); +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlclose-unload-c++/main.c b/src/dyld-apple/unit-tests/test-cases/dlclose-unload-c++/main.c new file mode 100644 index 000000000..8300a4106 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlclose-unload-c++/main.c @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +/// +/// This tests that if a C++ symbol (any weak symbol) is bound to an image +/// that is dynamically unloaed, the image is not unloaded until all its clients are +/// + +typedef void* (*proc)(void); + +bool inImage(void* x) +{ + Dl_info info; + return ( dladdr(x, &info) != 0 ); +} + + +int main() +{ + void* handle1 = dlopen("libfoo.dylib", RTLD_LAZY); + if ( handle1 == NULL ) { + FAIL("dlclose-unload-c++: dlopen(\"libfoo.dylib\", RTLD_LAZY) failed with dlerror()=%s", dlerror()); + exit(0); + } + + proc fooProc = (proc)dlsym(handle1, "foo"); + if ( fooProc == NULL ) { + FAIL("dlclose-unload-c++: dlsym(handle1, \"foo\") failed"); + exit(0); + } + + void* handle2 = dlopen("libbar.dylib", RTLD_LAZY); + if ( handle2 == NULL ) { + FAIL("dlclose-unload-c++: dlopen(\"libfoo.dylib\", RTLD_LAZY) failed with dlerror()=%s", dlerror()); + exit(0); + } + + proc barProc = (proc)dlsym(handle2, "bar"); + if ( barProc == NULL ) { + FAIL("dlclose-unload-c++: dlsym(handle2, \"bar\") failed"); + exit(0); + } + + // verify that uniquing is happening + void* fooResult = (*fooProc)(); + void* barResult = (*barProc)(); + if ( fooResult != barResult ) { + FAIL("dlclose-unload-c++: foo() and bar() returned different values"); + exit(0); + } + + // close libfoo, even though libbar is using libfoo + dlclose(handle1); + + // error if libfoo was unloaded + if ( !inImage(fooProc) ) { + FAIL("dlclose-unload-c++: libfoo should not have been unloaded"); + exit(0); + } + + // close libbar which should release libfoo + dlclose(handle2); + + // error if libfoo was not unloaded + if ( inImage(fooProc) ) { + FAIL("dlclose-unload-c++: libfoo should have been unloaded"); + exit(0); + } + + PASS("dlclose-unload-c++"); + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlclose-unmap/foo.c b/src/dyld-apple/unit-tests/test-cases/dlclose-unmap/foo.c new file mode 100644 index 000000000..8826cbaa8 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlclose-unmap/foo.c @@ -0,0 +1,2 @@ + +void foo() {} \ No newline at end of file diff --git a/src/dyld-apple/unit-tests/test-cases/dlclose-unmap/main.c b/src/dyld-apple/unit-tests/test-cases/dlclose-unmap/main.c new file mode 100644 index 000000000..a2c3cbfb4 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlclose-unmap/main.c @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2005-2009 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +/// +/// This tests that dlclose() actually unmmaps the image +/// + +static void trySO(const char* path) +{ + void* handle = dlopen(path, RTLD_LAZY); + if ( handle == NULL ) { + const char* msg = dlerror(); + FAIL("dlopen(\"%s\" RTLD_LAZY) failed but it should have worked: %s", path, msg); + exit(0); + } + void* sym = dlsym(handle, "foo"); + if ( sym == NULL ) { + const char* msg = dlerror(); + FAIL("dlsym(handle, \"foo\") failed but it should have worked: %s", msg); + exit(0); + } + + int result = dlclose(handle); + if ( result != 0 ) { + FAIL("dlclose(handle) returned %d", result); + exit(0); + } + + // now try to create a page where foo() was + vm_address_t addr = ((uintptr_t)sym) & (-4096); + kern_return_t r = vm_allocate(mach_task_self(), &addr, 4096, VM_FLAGS_FIXED); + if ( r != KERN_SUCCESS ) { + FAIL("dlclose-unmap: could not allocate page where SO was previously mapped", result); + exit(0); + } +} + + +int main() +{ + trySO("test.bundle"); + trySO("test.dylib"); + + PASS("dlclose-unmap"); + return 0; +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlerror-clear/main.c b/src/dyld-apple/unit-tests/test-cases/dlerror-clear/main.c new file mode 100644 index 000000000..f70cc8671 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlerror-clear/main.c @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +/// +/// This tests that the dlerror message is cleared when dlerror is called +/// + +int main() +{ + // try to non-existent library + void* handle1 = dlopen("frobulite", RTLD_LAZY); + if ( handle1 != NULL ) { + FAIL("dlerror-clear: dlopen(\"frobulite\", RTLD_LAZY) succeeded but should have failed"); + exit(0); + } + + // verify there is an error message + const char* msg = dlerror(); + if ( msg == NULL ) { + FAIL("dlerror-clear: dlerror() returned NULL but should have returned an error message"); + exit(0); + } + + // verify error message was cleared + const char* msg2 = dlerror(); + if ( msg2 != NULL ) { + FAIL("dlerror-clear: dlerror() returned message but should have returned NULL"); + exit(0); + } + + PASS("dlerror-clear"); + return 0; +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlerror/main.c b/src/dyld-apple/unit-tests/test-cases/dlerror/main.c new file mode 100644 index 000000000..71ed8ab7b --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlerror/main.c @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +/// +/// This tests that the dlerror message is kept per thread +/// + +static void* work(void* arg) +{ + const char* str = (char*)arg; + for(int i=0; i < 1000; ++i) { + //fprintf(stderr, "dlopen(%s)\n", str); + void* handle = dlopen(str, RTLD_LAZY); + if ( handle != NULL ) { + FAIL("dlopen(%s) unexpectedly succeeded", str); + exit(0); + } + char* msg = dlerror(); + //fprintf(stderr, "dlopen(%s) => %s\n", str, msg); + if ( (msg == NULL) || (strstr(msg, str) == NULL) ) { + FAIL("dlerror() did not contain library name that could not be loaded", str); + exit(0); + } + + + } + return 0; +} + + + +int main() +{ + dlsym(RTLD_DEFAULT, "foobar"); + //fprintf(stderr, "%s\n", dlerror()); + + pthread_t worker1; + if ( pthread_create(&worker1, NULL, work, "/frazzle/bar") != 0 ) { + FAIL("pthread_create failed"); + exit(0); + } + + pthread_t worker2; + if ( pthread_create(&worker2, NULL, work, "/frazzle/foo") != 0 ) { + FAIL("pthread_create failed"); + exit(0); + } + + pthread_t worker3; + if ( pthread_create(&worker3, NULL, work, "/frazzle/dazzle") != 0 ) { + FAIL("pthread_create failed"); + exit(0); + } + + void* result; + //fprintf(stderr, "waiting for worker 1\n"); + pthread_join(worker1, &result); + //fprintf(stderr, "waiting for worker 2\n"); + pthread_join(worker2, &result); + //fprintf(stderr, "waiting for worker 3\n"); + pthread_join(worker3, &result); + + PASS("dlerror-thread-test"); + return 0; +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlopen-DYLD_FALLBACK_LIBRARY_PATH/foo.c b/src/dyld-apple/unit-tests/test-cases/dlopen-DYLD_FALLBACK_LIBRARY_PATH/foo.c new file mode 100644 index 000000000..33c0685b8 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlopen-DYLD_FALLBACK_LIBRARY_PATH/foo.c @@ -0,0 +1,6 @@ + + +int foo() +{ + return 0; +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlopen-DYLD_FALLBACK_LIBRARY_PATH/main.c b/src/dyld-apple/unit-tests/test-cases/dlopen-DYLD_FALLBACK_LIBRARY_PATH/main.c new file mode 100644 index 000000000..d2ca85e29 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlopen-DYLD_FALLBACK_LIBRARY_PATH/main.c @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2008 Apple Computer, 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include // for getenv() + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + + +int main(int argc, const char* argv[]) +{ + void* handle = dlopen("/junk/path/libfoo.dylib", RTLD_LAZY); + if ( handle != NULL ) + FAIL("dlopen-DYLD_FALLBACK_LIBRARY_PATH unexpected found dylib"); + else + PASS("dlopen-DYLD_FALLBACK_LIBRARY_PATH"); + + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlopen-DYLD_LIBRARY_PATH/foo.c b/src/dyld-apple/unit-tests/test-cases/dlopen-DYLD_LIBRARY_PATH/foo.c new file mode 100644 index 000000000..0502e90e1 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlopen-DYLD_LIBRARY_PATH/foo.c @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ + + +int foo() +{ +#ifdef ALT + return 1; +#else + return 0; +#endif +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlopen-DYLD_LIBRARY_PATH/main.c b/src/dyld-apple/unit-tests/test-cases/dlopen-DYLD_LIBRARY_PATH/main.c new file mode 100644 index 000000000..8067d7cba --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlopen-DYLD_LIBRARY_PATH/main.c @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include // for getenv() + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + + +typedef int (*fooproc)(); + +int main(int argc, const char* argv[]) +{ + void* handle = dlopen(argv[1], RTLD_LAZY); + if ( handle == NULL ) { + FAIL("dlopen(\"%s\") failed", argv[1]); + exit(0); + } + + fooproc sym = (fooproc)dlsym(handle, "foo"); + if ( sym == NULL ) { + FAIL("dlsym(handle, \"foo\") failed"); + exit(0); + } + + int expectedResult = 0; + if ( getenv("DYLD_LIBRARY_PATH") != NULL ) + expectedResult = 1; + + int actualResult = (*sym)(); + + if ( actualResult != expectedResult ) + FAIL("dlopen-DYLD_LIBRARY_PATH using wrong dylib"); + else + PASS("dlopen-DYLD_LIBRARY_PATH"); + + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlopen-LD_LIBRARY_PATH/foo.c b/src/dyld-apple/unit-tests/test-cases/dlopen-LD_LIBRARY_PATH/foo.c new file mode 100644 index 000000000..de65df55e --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlopen-LD_LIBRARY_PATH/foo.c @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ + + +int foo() +{ + return VALUE; +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlopen-LD_LIBRARY_PATH/main.c b/src/dyld-apple/unit-tests/test-cases/dlopen-LD_LIBRARY_PATH/main.c new file mode 100644 index 000000000..70f18bb29 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlopen-LD_LIBRARY_PATH/main.c @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include // for getenv() + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + + +typedef int (*fooproc)(); + +// +// argv[1] is path to dlopen() +// argv[2] is exepect result from foo() +// argv[3] is message +// +int main(int argc, const char* argv[]) +{ + void* handle = dlopen(argv[1], RTLD_LAZY); + if ( handle == NULL ) { + FAIL("dlopen-LD_LIBRARY_PATH %s, dlopen(\"%s\") failed", argv[3], argv[1]); + exit(0); + } + + fooproc sym = (fooproc)dlsym(handle, "foo"); + if ( sym == NULL ) { + FAIL("dlopen-LD_LIBRARY_PATH %s, dlsym(handle, \"foo\") failed", argv[3]); + exit(0); + } + + int expectedResult = atoi(argv[2]); + + int actualResult = (*sym)(); + + if ( actualResult != expectedResult ) + FAIL("dlopen-LD_LIBRARY_PATH %s", argv[3]); + else + PASS("dlopen-LD_LIBRARY_PATH %s", argv[3]); + + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlopen-NULL-RTLD_FIRST/foo.c b/src/dyld-apple/unit-tests/test-cases/dlopen-NULL-RTLD_FIRST/foo.c new file mode 100644 index 000000000..fea884b9e --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlopen-NULL-RTLD_FIRST/foo.c @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2006 Apple Computer, 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@ + */ + + +int foo() +{ + return 10; +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlopen-NULL-RTLD_FIRST/main.c b/src/dyld-apple/unit-tests/test-cases/dlopen-NULL-RTLD_FIRST/main.c new file mode 100644 index 000000000..b86c5e587 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlopen-NULL-RTLD_FIRST/main.c @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +/// +/// When dlopen(NULL, RTLD_FIRST) is called, +/// any dlsym() looks against that handle should only look in +/// the main executable, and not subsequent images. +/// + + +struct info +{ + const char* path; + void* handle; +}; +typedef struct info info; + +static info dlopen_or_fail(const char* path, int options) +{ + info result; + result.path = path; + result.handle = dlopen(path, options); + if ( result.handle == NULL ) { + FAIL("dlsym-NULL-RTLD_FIRST: dlopen(\"%s\") failed: %s", path, dlerror()); + exit(0); + } + //fprintf(stderr, "dlopen(%s, 0x%0X) => %p\n", path, options, result.handle); + return result; +} + +static void dlsym_should_fail(info hp, const char* symbol) +{ + void* sym = dlsym(hp.handle, symbol); + if ( sym != NULL ) { + FAIL("dlsym-NULL-RTLD_FIRST: dlsym(handle-%s, \"%s\") should have failed", hp.path, symbol); + exit(0); + } +} + +static void dlsym_should_succeed(info hp, const char* symbol) +{ + void* sym = dlsym(hp.handle, symbol); + if ( sym == NULL ) { + FAIL("dlsym-NULL-RTLD_FIRST: dlsym(handle-%s, \"%s\") failed", hp.path, symbol); + exit(0); + } +} + + +int main() +{ + int result; + info mainFirst = dlopen_or_fail(NULL, RTLD_FIRST); + info mainDefault = dlopen_or_fail(NULL, 0); + + dlsym_should_succeed(mainFirst, "main_foo"); + dlsym_should_fail(mainFirst, "foo"); + + dlsym_should_succeed(mainDefault, "main_foo"); + dlsym_should_succeed(mainDefault, "foo"); + + result = dlclose(mainFirst.handle); + if ( result != 0 ) { + FAIL("dlsym-NULL-RTLD_FIRST: dlclose(mainFirst.handle) failed: %s", dlerror()); + exit(0); + } + result = dlclose(mainDefault.handle); + if ( result != 0 ) { + FAIL("dlsym-NULL-RTLD_FIRST: dlclose(mainDefault.handle) failed: %s", dlerror()); + exit(0); + } + + PASS("dlsym-NULL-RTLD_FIRST"); + return EXIT_SUCCESS; +} + + +void main_foo() {} + + diff --git a/src/dyld-apple/unit-tests/test-cases/dlopen-RTLD_FIRST/bar.c b/src/dyld-apple/unit-tests/test-cases/dlopen-RTLD_FIRST/bar.c new file mode 100644 index 000000000..7ef59fba3 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlopen-RTLD_FIRST/bar.c @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2006 Apple Computer, 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@ + */ + + +int bar() +{ + return 10; +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlopen-RTLD_FIRST/base.c b/src/dyld-apple/unit-tests/test-cases/dlopen-RTLD_FIRST/base.c new file mode 100644 index 000000000..87bb46c9a --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlopen-RTLD_FIRST/base.c @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2006 Apple Computer, 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@ + */ + + +int base() +{ + return 10; +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlopen-RTLD_FIRST/foo.c b/src/dyld-apple/unit-tests/test-cases/dlopen-RTLD_FIRST/foo.c new file mode 100644 index 000000000..fea884b9e --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlopen-RTLD_FIRST/foo.c @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2006 Apple Computer, 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@ + */ + + +int foo() +{ + return 10; +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlopen-RTLD_FIRST/main.c b/src/dyld-apple/unit-tests/test-cases/dlopen-RTLD_FIRST/main.c new file mode 100644 index 000000000..95d3ce44e --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlopen-RTLD_FIRST/main.c @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +/// +/// When dlopen() is called with the RTLD_FIRST option, +/// any dlsym() looks against that handle should only look in +/// that handle, and not subsequent images. +/// + + +struct info +{ + const char* path; + void* handle; +}; +typedef struct info info; + +static info dlopen_or_fail(const char* path, int options) +{ + info result; + result.path = path; + result.handle = dlopen(path, options); + if ( result.handle == NULL ) { + FAIL("dlopen-RTLD_FIRST: dlopen(\"%s\") failed: %s", path, dlerror()); + exit(0); + } + //fprintf(stderr, "dlopen(%s, 0x%0X) => %p\n", path, options, result.handle); + return result; +} + +static void dlsym_should_fail(info hp, const char* symbol) +{ + void* sym = dlsym(hp.handle, symbol); + if ( sym != NULL ) { + FAIL("dlopen-RTLD_FIRST: dlsym(handle-%s, \"%s\") should have failed", hp.path, symbol); + exit(0); + } +} + +static void dlsym_should_succeed(info hp, const char* symbol) +{ + void* sym = dlsym(hp.handle, symbol); + if ( sym == NULL ) { + FAIL("dlopen-RTLD_FIRST: dlsym(handle-%s, \"%s\") failed", hp.path, symbol); + exit(0); + } +} + + +int main() +{ + info libFooFirst = dlopen_or_fail("libfoo.dylib", RTLD_FIRST); + info libFoo = dlopen_or_fail("libfoo.dylib", 0); + + info libBarFirst = dlopen_or_fail("libbar.dylib", RTLD_FIRST); + info libBar = dlopen_or_fail("libbar.dylib", 0); + + dlsym_should_succeed(libFooFirst, "foo"); + dlsym_should_fail(libFooFirst, "base"); + dlsym_should_fail(libFooFirst, "bar"); + + dlsym_should_succeed(libFoo, "foo"); + dlsym_should_succeed(libFoo, "base"); + + dlsym_should_succeed(libBarFirst, "bar"); + dlsym_should_succeed(libBarFirst, "base"); // libbar re-exports libbase + dlsym_should_fail(libBarFirst, "foo"); + + dlsym_should_succeed(libBar, "bar"); + dlsym_should_succeed(libBar, "base"); + + + + info bundleFooFirst = dlopen_or_fail("foo.bundle", RTLD_FIRST); + info bundleFoo = dlopen_or_fail("foo.bundle", 0); + + info bundleBarFirst = dlopen_or_fail("bar.bundle", RTLD_FIRST); + info bundleBar = dlopen_or_fail("bar.bundle", 0); + + dlsym_should_succeed(bundleFooFirst, "foo"); + dlsym_should_fail(bundleFooFirst, "base"); + dlsym_should_fail(bundleFooFirst, "bar"); + + dlsym_should_succeed(bundleFoo, "foo"); + dlsym_should_succeed(bundleFoo, "base"); + + dlsym_should_succeed(bundleBarFirst, "bar"); + dlsym_should_fail(bundleBarFirst, "base"); + dlsym_should_fail(bundleBarFirst, "foo"); + + dlsym_should_succeed(bundleBar, "bar"); + dlsym_should_succeed(bundleBar, "base"); + + + PASS("dlsym-RTLD_FIRST bundle and dylib"); + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlopen-RTLD_GLOBAL/bar.c b/src/dyld-apple/unit-tests/test-cases/dlopen-RTLD_GLOBAL/bar.c new file mode 100644 index 000000000..e8e0806aa --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlopen-RTLD_GLOBAL/bar.c @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ + +extern int foo; + +int bar() +{ + return foo; +} + + diff --git a/src/dyld-apple/unit-tests/test-cases/dlopen-RTLD_GLOBAL/foo.c b/src/dyld-apple/unit-tests/test-cases/dlopen-RTLD_GLOBAL/foo.c new file mode 100644 index 000000000..86895974c --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlopen-RTLD_GLOBAL/foo.c @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ + + +int foo = 0; diff --git a/src/dyld-apple/unit-tests/test-cases/dlopen-RTLD_GLOBAL/main.c b/src/dyld-apple/unit-tests/test-cases/dlopen-RTLD_GLOBAL/main.c new file mode 100644 index 000000000..a25854b0d --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlopen-RTLD_GLOBAL/main.c @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + + + + +int main(int argc, const char* argv[]) +{ + // open first object which defines foo + void* handle = dlopen(argv[1], RTLD_GLOBAL); + if ( handle == NULL ) { + FAIL("dlopen(\"%s\") failed", argv[1]); + exit(1); + } + + // open second object which uses foo + void* handle2 = dlopen(argv[2], RTLD_NOW); + if ( handle2 == NULL ) { + FAIL("dlopen(\"%s\") failed", argv[2]); + exit(1); + } + + PASS("dlopen-RTLD_GLOBAL"); + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlopen-RTLD_LOCAL-ignore/bar.c b/src/dyld-apple/unit-tests/test-cases/dlopen-RTLD_LOCAL-ignore/bar.c new file mode 100644 index 000000000..73b29f264 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlopen-RTLD_LOCAL-ignore/bar.c @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ + + +int bar() +{ + return 2; +} + + diff --git a/src/dyld-apple/unit-tests/test-cases/dlopen-RTLD_LOCAL-ignore/foo.c b/src/dyld-apple/unit-tests/test-cases/dlopen-RTLD_LOCAL-ignore/foo.c new file mode 100644 index 000000000..d040e48c4 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlopen-RTLD_LOCAL-ignore/foo.c @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ + + +int foo() +{ + return 1; +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlopen-RTLD_LOCAL-ignore/main.c b/src/dyld-apple/unit-tests/test-cases/dlopen-RTLD_LOCAL-ignore/main.c new file mode 100644 index 000000000..b2862c57f --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlopen-RTLD_LOCAL-ignore/main.c @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + + +int main(int argc, const char* argv[]) +{ + // main alreadly links with foo.dylib + // now dynamically link with bar.dylib + void* handle = dlopen("./bar.dylib", RTLD_GLOBAL); + if ( handle == NULL ) { + const char* msg = dlerror(); + FAIL("dlopen(\"%s\", RTLD_GLOBAL) failed: %s", argv[1], msg); + return EXIT_SUCCESS; + } + + // verify we can find _foo + void* fooSym = dlsym(RTLD_DEFAULT, "foo"); + if ( fooSym == NULL ) { + const char* msg = dlerror(); + FAIL("dlsym(RTLD_DEFAULT, \"foo\") failed: %s", msg); + return EXIT_SUCCESS; + } + + // verify we can find _bar + void* barSym = dlsym(RTLD_DEFAULT, "bar"); + if ( barSym == NULL ) { + const char* msg = dlerror(); + FAIL("dlsym(RTLD_DEFAULT, \"bar\") failed: %s", msg); + return EXIT_SUCCESS; + } + + // open foo privately (since it was already opened global, RTLD_LOCAL should have no effect + void* handleFoo = dlopen("./foo.dylib", RTLD_LOCAL); + if ( handleFoo == NULL ) { + const char* msg = dlerror(); + FAIL("dlopen(\"%s\", RTLD_LOCAL) failed: %s", "./foo.dylib", msg); + return EXIT_SUCCESS; + } + + // open foo privately (since it was already opened global, RTLD_LOCAL should have no effect + void* handleBar = dlopen("./bar.dylib", RTLD_LOCAL); + if ( handleBar == NULL ) { + const char* msg = dlerror(); + FAIL("dlopen(\"%s\", RTLD_LOCAL) failed: %s", "./bar.dylib", msg); + return EXIT_SUCCESS; + } + + // verify we can still find _foo + fooSym = dlsym(RTLD_DEFAULT, "foo"); + if ( fooSym == NULL ) { + const char* msg = dlerror(); + FAIL("dlsym(RTLD_DEFAULT, \"foo\") failed: %s", msg); + return EXIT_SUCCESS; + } + + // verify we can still find _bar + barSym = dlsym(RTLD_DEFAULT, "bar"); + if ( barSym == NULL ) { + const char* msg = dlerror(); + FAIL("dlsym(RTLD_DEFAULT, \"bar\") failed: %s", msg); + return EXIT_SUCCESS; + } + + PASS("dlopen-RTLD_LOCAL-ignore"); + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlopen-RTLD_LOCAL-weak/bar.c b/src/dyld-apple/unit-tests/test-cases/dlopen-RTLD_LOCAL-weak/bar.c new file mode 100644 index 000000000..b4ae377f0 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlopen-RTLD_LOCAL-weak/bar.c @@ -0,0 +1,5 @@ +int __attribute__((weak)) A[] = { 1, 2, 3, 4 }; + + +int* getA() { return A; } + diff --git a/src/dyld-apple/unit-tests/test-cases/dlopen-RTLD_LOCAL-weak/foo.c b/src/dyld-apple/unit-tests/test-cases/dlopen-RTLD_LOCAL-weak/foo.c new file mode 100644 index 000000000..c8ea49b71 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlopen-RTLD_LOCAL-weak/foo.c @@ -0,0 +1,5 @@ + + +int __attribute__((weak)) A[] = { 5, 6, 7, 8 }; + +int* getA() { return A; } diff --git a/src/dyld-apple/unit-tests/test-cases/dlopen-RTLD_LOCAL-weak/main.c b/src/dyld-apple/unit-tests/test-cases/dlopen-RTLD_LOCAL-weak/main.c new file mode 100644 index 000000000..034013833 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlopen-RTLD_LOCAL-weak/main.c @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2008 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +// this strong A should not override weak +// As in loaded code because of RTLD_LOCAL +int A[] = { 10, 11, 12, 13 }; + +typedef int* (*getproc)(void); + +int main(int argc, const char* argv[]) +{ + // open first object + void* fooHandle = dlopen("libfoo.dylib", RTLD_LOCAL); + if ( fooHandle == NULL ) { + FAIL("dlopen-RTLD_LOCAL-weak: dlopen(\"libfoo.dylib\", RTLD_LOCAL) failed: %s", dlerror()); + return EXIT_SUCCESS; + } + + // open second object + void* barHandle = dlopen("libbar.dylib", RTLD_LOCAL); + if ( barHandle == NULL ) { + FAIL("dlopen-RTLD_LOCAL-weak: dlopen(\"libbar.dylib\", RTLD_LOCAL) failed: %s", dlerror()); + return EXIT_SUCCESS; + } + + // get functions + getproc fooproc = (getproc)dlsym(fooHandle, "getA"); + if ( fooproc == NULL ) { + FAIL("dlopen-RTLD_LOCAL-weak: dlsym(getA) failed: %s", dlerror()); + return EXIT_SUCCESS; + } + getproc barproc = (getproc)dlsym(barHandle, "getA"); + if ( barproc == NULL ) { + FAIL("dlopen-RTLD_LOCAL-weak: dlsym(getA) failed: %s", dlerror()); + return EXIT_SUCCESS; + } + + // get values + int* fooA = (*fooproc)(); + int* barA = (*barproc)(); + + if ( fooA == A ) + FAIL("dlopen-RTLD_LOCAL-weak: fooA == A"); + else if ( barA == A ) + FAIL("dlopen-RTLD_LOCAL-weak: barA == A"); + else if ( fooA == barA ) + FAIL("dlopen-RTLD_LOCAL-weak: fooA == barA"); + else + PASS("dlopen-RTLD_LOCAL-weak"); + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlopen-RTLD_LOCAL/bar.c b/src/dyld-apple/unit-tests/test-cases/dlopen-RTLD_LOCAL/bar.c new file mode 100644 index 000000000..e8e0806aa --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlopen-RTLD_LOCAL/bar.c @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ + +extern int foo; + +int bar() +{ + return foo; +} + + diff --git a/src/dyld-apple/unit-tests/test-cases/dlopen-RTLD_LOCAL/foo.c b/src/dyld-apple/unit-tests/test-cases/dlopen-RTLD_LOCAL/foo.c new file mode 100644 index 000000000..86895974c --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlopen-RTLD_LOCAL/foo.c @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ + + +int foo = 0; diff --git a/src/dyld-apple/unit-tests/test-cases/dlopen-RTLD_LOCAL/main.c b/src/dyld-apple/unit-tests/test-cases/dlopen-RTLD_LOCAL/main.c new file mode 100644 index 000000000..b8a30bcde --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlopen-RTLD_LOCAL/main.c @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + + +int main(int argc, const char* argv[]) +{ + // open first object which defines foo + void* handle = dlopen(argv[1], RTLD_LOCAL); + if ( handle == NULL ) { + const char* msg = dlerror(); + // Panther dlopen() fails on RTLD_LOCAL of a dylib + if ( strstr(msg, "RTLD_LOCAL") != NULL ) + XFAIL("dlopen(\"%s\", RTLD_LOCAL) failed: %s", argv[1], msg); + else + FAIL("dlopen(\"%s\", RTLD_LOCAL) failed: %s", argv[1], msg); + return EXIT_SUCCESS; + } + + // open second object which uses foo + void* handle2 = dlopen(argv[2], RTLD_NOW); + if ( handle2 != NULL ) { + FAIL("dlopen(\"%s\") succeeded but foo should have been not found", argv[2]); + return EXIT_SUCCESS; + } + const char* msg = dlerror(); + if ( strstr(msg, "foo") == NULL ) { + FAIL("dlopen(\"%s\") correctly failed, but foo was not in error mesage: %s", argv[2], msg); + return EXIT_SUCCESS; + } + + + PASS("dlopen-RTLD_LOCAL"); + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlopen-RTLD_NODELETE/foo.c b/src/dyld-apple/unit-tests/test-cases/dlopen-RTLD_NODELETE/foo.c new file mode 100644 index 000000000..5cc9791ba --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlopen-RTLD_NODELETE/foo.c @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ + + +int foo = 10; \ No newline at end of file diff --git a/src/dyld-apple/unit-tests/test-cases/dlopen-RTLD_NODELETE/main.c b/src/dyld-apple/unit-tests/test-cases/dlopen-RTLD_NODELETE/main.c new file mode 100644 index 000000000..296479efa --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlopen-RTLD_NODELETE/main.c @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +/// +/// This tests that RTLD_NODELETE prevents an image from being unloaded +/// + +static int trySO(const char* path) +{ // main links against libfoo.dylib so it should already be loaded + void* handle = dlopen(path, RTLD_NODELETE); + if ( handle == NULL ) { + const char* msg = dlerror(); + FAIL("dlopen(\"%s\" RTLD_NODELETE) failed but it should have worked: %s", path, msg); + exit(0); + } + void* sym = dlsym(handle, "foo"); + if ( sym == NULL ) { + const char* msg = dlerror(); + FAIL("dlsym(handle, \"foo\") failed but it should have worked: %s", msg); + exit(0); + } + + int result = dlclose(handle); + if ( result != 0 ) { + if ( result == 1 ) { + // panther dyld returns 1 if you try to dlclose() a dylib + XFAIL("dlclose(handle[%s]) returned %d", path, result); + } + else { + FAIL("dlclose(handle) returned %d", result); + exit(0); + } + } + + // now try to access foo. If .so was unmapped, this will bus error + return *((int*)sym); +} + + +int main() +{ + trySO("test.bundle"); + trySO("test.dylib"); + + PASS("dlopen-RTLD_NODELETE"); + return 0; +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlopen-RTLD_NOLOAD-fallback/foo.c b/src/dyld-apple/unit-tests/test-cases/dlopen-RTLD_NOLOAD-fallback/foo.c new file mode 100644 index 000000000..81f7dcfc9 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlopen-RTLD_NOLOAD-fallback/foo.c @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ + + +int foo() +{ + return 10; +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlopen-RTLD_NOLOAD-fallback/main.c b/src/dyld-apple/unit-tests/test-cases/dlopen-RTLD_NOLOAD-fallback/main.c new file mode 100644 index 000000000..f629da7a4 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlopen-RTLD_NOLOAD-fallback/main.c @@ -0,0 +1,46 @@ +/* + * 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +/// +/// This tests that RTLD_NOLOAD does not find an image via a fallback path +/// + + +int main(int argc, const char* argv[]) +{ + const char* path = argv[1]; + void* handle = dlopen(path, RTLD_NOLOAD); + if ( handle != NULL ) + FAIL("dlopen-RTLD_NOLOAD-fallback, dlopen(% RTLD_NOLOAD) should have failed"); + else + PASS("dlopen-RTLD_NOLOAD-fallback"); + + return 0; +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlopen-RTLD_NOLOAD-in-initializer/bar.c b/src/dyld-apple/unit-tests/test-cases/dlopen-RTLD_NOLOAD-in-initializer/bar.c new file mode 100644 index 000000000..62fdfec53 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlopen-RTLD_NOLOAD-in-initializer/bar.c @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2006 Apple Computer, 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@ + */ +#include +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +extern bool bazInitialized(); + +static void myInit() __attribute__((constructor)); + +static void myInit() +{ + if ( bazInitialized() ) { + FAIL("dlopen-RTLD_NOLOAD-in-initializer baz initialized too early"); + exit(0); + } + + // calling dlopen with RTLD_NOLOAD. + // this should not cause initialzers to run. + // in particular, baz should be be initialized + dlopen("libbaz.dylib", RTLD_NOLOAD); + + if ( bazInitialized() ) { + FAIL("dlopen-RTLD_NOLOAD-in-initializer baz initialized by dlopen"); + exit(0); + } +} + diff --git a/src/dyld-apple/unit-tests/test-cases/dlopen-RTLD_NOLOAD-in-initializer/base.c b/src/dyld-apple/unit-tests/test-cases/dlopen-RTLD_NOLOAD-in-initializer/base.c new file mode 100644 index 000000000..3bcd16438 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlopen-RTLD_NOLOAD-in-initializer/base.c @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2006 Apple Computer, 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@ + */ +#include +#include // exit(), EXIT_SUCCESS +#include + +static bool inited = false; + +void setBazInitialized() { inited = true; } + +bool bazInitialized() { return inited; } + diff --git a/src/dyld-apple/unit-tests/test-cases/dlopen-RTLD_NOLOAD-in-initializer/baz.c b/src/dyld-apple/unit-tests/test-cases/dlopen-RTLD_NOLOAD-in-initializer/baz.c new file mode 100644 index 000000000..6a80314bc --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlopen-RTLD_NOLOAD-in-initializer/baz.c @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2006 Apple Computer, 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@ + */ +#include +#include + +extern void setBazInitialized(); + +static void myInit() __attribute__((constructor)); + +static void myInit() +{ + setBazInitialized(); +} + diff --git a/src/dyld-apple/unit-tests/test-cases/dlopen-RTLD_NOLOAD-in-initializer/foo.c b/src/dyld-apple/unit-tests/test-cases/dlopen-RTLD_NOLOAD-in-initializer/foo.c new file mode 100644 index 000000000..1e61ab3ea --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlopen-RTLD_NOLOAD-in-initializer/foo.c @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2006 Apple Computer, 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@ + */ +#include +#include + + +bool foo() +{ + return true; +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlopen-RTLD_NOLOAD-in-initializer/main.c b/src/dyld-apple/unit-tests/test-cases/dlopen-RTLD_NOLOAD-in-initializer/main.c new file mode 100644 index 000000000..9b67e3759 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlopen-RTLD_NOLOAD-in-initializer/main.c @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +typedef bool (*fooProc)(void); + +int main() +{ + void* handle = dlopen("libfoo.dylib", RTLD_LAZY); + if ( handle == NULL ) { + FAIL("dlopen(\"libfoo.dylib\") failed with: %s", dlerror()); + exit(0); + } + + dlclose(handle); + + PASS("dlopen-RTLD_NOLOAD-in-initializer"); + + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlopen-RTLD_NOLOAD-symlink/bar.c b/src/dyld-apple/unit-tests/test-cases/dlopen-RTLD_NOLOAD-symlink/bar.c new file mode 100644 index 000000000..7fe64038c --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlopen-RTLD_NOLOAD-symlink/bar.c @@ -0,0 +1 @@ +int bar() { return 0; } diff --git a/src/dyld-apple/unit-tests/test-cases/dlopen-RTLD_NOLOAD-symlink/foo.c b/src/dyld-apple/unit-tests/test-cases/dlopen-RTLD_NOLOAD-symlink/foo.c new file mode 100644 index 000000000..c58fc07eb --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlopen-RTLD_NOLOAD-symlink/foo.c @@ -0,0 +1,2 @@ +int foo() { return 0; } + diff --git a/src/dyld-apple/unit-tests/test-cases/dlopen-RTLD_NOLOAD-symlink/main.c b/src/dyld-apple/unit-tests/test-cases/dlopen-RTLD_NOLOAD-symlink/main.c new file mode 100644 index 000000000..4ce752c0a --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlopen-RTLD_NOLOAD-symlink/main.c @@ -0,0 +1,46 @@ +/* + * 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +/// +/// This tests that RTLD_NOLOAD finds existing images +/// + + +int main(int argc, const char* argv[]) +{ + // main links against libfoo.dylib, so it is already loaded + // libbar.dylib is a symlink to libfoo.dylib, so it should succeed + void* handle = dlopen(argv[1], RTLD_NOLOAD); + if ( handle != NULL ) + PASS("dlopen-RTLD_NOLOAD-symlink"); + else + FAIL("dlopen-RTLD_NOLOAD-symlink"); + return 0; +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlopen-RTLD_NOLOAD/foo.c b/src/dyld-apple/unit-tests/test-cases/dlopen-RTLD_NOLOAD/foo.c new file mode 100644 index 000000000..81f7dcfc9 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlopen-RTLD_NOLOAD/foo.c @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ + + +int foo() +{ + return 10; +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlopen-RTLD_NOLOAD/main.c b/src/dyld-apple/unit-tests/test-cases/dlopen-RTLD_NOLOAD/main.c new file mode 100644 index 000000000..7383a8c8a --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlopen-RTLD_NOLOAD/main.c @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +/// +/// This tests that RTLD_NOLOAD binds finds existing images +/// + + +int main() +{ + // main links against libfoo.dylib so it should already be loaded + void* handle = dlopen("libfoo.dylib", RTLD_NOLOAD); + if ( handle == NULL ) { + const char* msg = dlerror(); + // Panther dlcompat does not check existing loaded images (only those opened with dlopen) + if ( strstr(msg, "RTLD_NOLOAD") != NULL ) + XFAIL("dlopen(libfoo.dylib, RTLD_NOLOAD) failed but it should have worked: %s", msg); + else + FAIL("dlopen(libfoo.dylib, RTLD_NOLOAD) failed but it should have worked: %s", msg); + return 0; + } + void* sym = dlsym(handle, "foo"); + if ( sym == NULL ) { + const char* msg = dlerror(); + FAIL("dlsym(handle, \"foo\") failed but it should have worked: %s", msg); + return 0; + } + + // libfobbulate.dylib does not exist, so load should return NULL + void* handle2 = dlopen("libfobbulate.dylib", RTLD_NOLOAD); + if ( handle2 != NULL ) { + FAIL("dlopen(libfobbulate.dylib, RTLD_NOLOAD) succeeded but it should have failed"); + return 0; + } + + + + PASS("dlopen-RTLD_NOLOAD"); + return 0; +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlopen-RTLD_NOW/bundle.c b/src/dyld-apple/unit-tests/test-cases/dlopen-RTLD_NOW/bundle.c new file mode 100644 index 000000000..5772ff483 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlopen-RTLD_NOW/bundle.c @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ + + +extern int foo(); +extern int foo2(); + +void doit() +{ + foo(); + foo2(); +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlopen-RTLD_NOW/foo.c b/src/dyld-apple/unit-tests/test-cases/dlopen-RTLD_NOW/foo.c new file mode 100644 index 000000000..e93777094 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlopen-RTLD_NOW/foo.c @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ + + +int foo() +{ + return 10; +} + +#if FOO2 +int foo2() +{ + return 10; +} +#endif diff --git a/src/dyld-apple/unit-tests/test-cases/dlopen-RTLD_NOW/main.c b/src/dyld-apple/unit-tests/test-cases/dlopen-RTLD_NOW/main.c new file mode 100644 index 000000000..2ebb4ad8e --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlopen-RTLD_NOW/main.c @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +/// +/// This tests that TLD_NOW binds all lazy symbols. +/// We do this by making sure a lazy symbol does not exist +/// and dlopen() errors out with a message string +/// that contains the missing symbol name. +/// + + +int main() +{ + void* handle = dlopen("test.bundle", RTLD_NOW); + if ( handle == NULL ) { + const char* msg = dlerror(); + if ( strstr(msg, "foo2") != NULL ) { + PASS("dlopen-RTLD_NOW"); + exit(0); + } + FAIL("dlopen(test.bundle, RTLD_NOW) failed but error message did not contain foo2: %s", msg); + exit(0); + } + FAIL("dlopen(test.bundle, RTLD_NOW) succeed but should have failed because foo2 does not exist"); + exit(0); +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlopen-basic/foo.c b/src/dyld-apple/unit-tests/test-cases/dlopen-basic/foo.c new file mode 100644 index 000000000..81f7dcfc9 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlopen-basic/foo.c @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ + + +int foo() +{ + return 10; +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlopen-basic/main.c b/src/dyld-apple/unit-tests/test-cases/dlopen-basic/main.c new file mode 100644 index 000000000..f877537cb --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlopen-basic/main.c @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +static void trySO(const char* path) +{ + void* handle = dlopen(path, RTLD_LAZY); + if ( handle == NULL ) { + FAIL("dlopen(\"%s\") failed with: %s", path, dlerror()); + exit(0); + } + + void* sym = dlsym(handle, "foo"); + if ( sym == NULL ) { + FAIL("dlsym(handle, \"foo\") failed"); + exit(0); + } + + int result = dlclose(handle); + if ( result != 0 ) { + if ( result == 1 ) { + // panther dyld returns 1 if you try to dlclose() a dylib + XFAIL("dlclose(handle) returned %d", result); + } + else { + FAIL("dlclose(handle) returned %d", result); + exit(0); + } + } + +} + + + +int main() +{ + trySO("test.bundle"); + trySO("test.dylib"); + + PASS("dlopen-basic bundle and dylib"); + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlopen-codesign-dynamic/foo.c b/src/dyld-apple/unit-tests/test-cases/dlopen-codesign-dynamic/foo.c new file mode 100644 index 000000000..3695dc91e --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlopen-codesign-dynamic/foo.c @@ -0,0 +1,3 @@ +void foo() +{ +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlopen-codesign-dynamic/main.c b/src/dyld-apple/unit-tests/test-cases/dlopen-codesign-dynamic/main.c new file mode 100644 index 000000000..37dba846a --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlopen-codesign-dynamic/main.c @@ -0,0 +1,45 @@ +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "test.h" + + +int main() +{ +#if ENFORCE + uint32_t flags = CS_ENFORCEMENT | CS_KILL; + if ( csops(0, CS_OPS_SET_STATUS, &flags, sizeof(flags)) != 0 ) { + FAIL("dlopen-codesign-dynamic: csops() failed"); + return EXIT_SUCCESS; + } + + void* handle = dlopen("libfoo.dylib", RTLD_LAZY); + if ( handle != NULL ) { + FAIL("dlopen-codesign-dynamic: load of libfoo.dylib should have failed"); + return EXIT_SUCCESS; + } + const char* msg = dlerror(); + if ( strstr(msg, "signature") == NULL ) { + FAIL("dlopen-codesign-dynamic: load of libfoo.dylib failed, but message was wrong: %s", msg); + return EXIT_SUCCESS; + } + +#else + void* handle = dlopen("libfoo.dylib", RTLD_LAZY); + if ( handle == NULL ) { + FAIL("dlopen-codesign-dynamic: load of libfoo.dylib failed"); + return EXIT_SUCCESS; + } + +#endif + + PASS("dlopen-codesign-dynamic"); + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlopen-codesign/foo.c b/src/dyld-apple/unit-tests/test-cases/dlopen-codesign/foo.c new file mode 100644 index 000000000..3695dc91e --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlopen-codesign/foo.c @@ -0,0 +1,3 @@ +void foo() +{ +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlopen-codesign/main.c b/src/dyld-apple/unit-tests/test-cases/dlopen-codesign/main.c new file mode 100644 index 000000000..53fce5f53 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlopen-codesign/main.c @@ -0,0 +1,27 @@ +#include +#include +#include +#include +#include +#include + + +#include "test.h" + + +int main() +{ + void* handle = dlopen("libfoo.dylib", RTLD_LAZY); + if ( handle != NULL ) { + FAIL("dlopen-codesign: load of libfoo.dylib should have failed"); + return EXIT_SUCCESS; + } + const char* msg = dlerror(); + if ( strstr(msg, "signature") == NULL ) { + FAIL("dlopen-codesign: load of libfoo.dylib failed, but message was wrong: %s", msg); + return EXIT_SUCCESS; + } + + PASS("dlopen-codesign"); + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlopen-dyld-locking/bar.c b/src/dyld-apple/unit-tests/test-cases/dlopen-dyld-locking/bar.c new file mode 100644 index 000000000..0531aa280 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlopen-dyld-locking/bar.c @@ -0,0 +1,31 @@ +/* + * 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@ + */ + + +void __attribute__((constructor)) myInit() +{ + // printf("in bar\n"); +} + + + diff --git a/src/dyld-apple/unit-tests/test-cases/dlopen-dyld-locking/base.c b/src/dyld-apple/unit-tests/test-cases/dlopen-dyld-locking/base.c new file mode 100644 index 000000000..6947d3159 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlopen-dyld-locking/base.c @@ -0,0 +1,70 @@ +/* + * 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@ + */ +#include +#include +#include +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +static pthread_mutex_t sBarrierMutex; +static pthread_cond_t sBarrierFree; + +static volatile int sValue = 0; + +static void __attribute__((constructor)) myinit() +{ + pthread_mutex_init(&sBarrierMutex, NULL); + pthread_cond_init(&sBarrierFree, NULL); +} + +void waitForState(int value) +{ + //fprintf(stderr, "waitForState(%d), currently %d\n", value, sValue); + pthread_mutex_lock(&sBarrierMutex); + while ( sValue < value ) { + struct timeval tvNow; + struct timespec tsTimeout; + gettimeofday(&tvNow, NULL); + TIMEVAL_TO_TIMESPEC(&tvNow, &tsTimeout); + tsTimeout.tv_sec += 2; // fail if block for 2 seconds + if ( pthread_cond_timedwait(&sBarrierFree, &sBarrierMutex, &tsTimeout) == ETIMEDOUT ) { + FAIL("dlopen-dyld-locking: lock timed out"); + exit(0); + } + } + pthread_mutex_unlock(&sBarrierMutex); +} + + +void setState(int value) +{ + pthread_mutex_lock(&sBarrierMutex); + //fprintf(stderr, "setState(%d)\n", value); + sValue = value; + pthread_cond_broadcast(&sBarrierFree); + pthread_mutex_unlock(&sBarrierMutex); +} + + diff --git a/src/dyld-apple/unit-tests/test-cases/dlopen-dyld-locking/base.h b/src/dyld-apple/unit-tests/test-cases/dlopen-dyld-locking/base.h new file mode 100644 index 000000000..f1aef7998 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlopen-dyld-locking/base.h @@ -0,0 +1,3 @@ + +extern void waitForState(int); +extern void setState(int); diff --git a/src/dyld-apple/unit-tests/test-cases/dlopen-dyld-locking/foo.c b/src/dyld-apple/unit-tests/test-cases/dlopen-dyld-locking/foo.c new file mode 100644 index 000000000..5753fa542 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlopen-dyld-locking/foo.c @@ -0,0 +1,33 @@ +/* + * 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@ + */ + +#include "base.h" + +void __attribute__((constructor)) myInit() +{ + setState(1); + waitForState(2); +} + + + diff --git a/src/dyld-apple/unit-tests/test-cases/dlopen-dyld-locking/main.c b/src/dyld-apple/unit-tests/test-cases/dlopen-dyld-locking/main.c new file mode 100644 index 000000000..3c68a9d50 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlopen-dyld-locking/main.c @@ -0,0 +1,68 @@ +/* + * 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +#include "base.h" + + +/// +/// This test verifies that dlopen() releases the dyld lock +/// while initializers are run. +/// +/// + + +static void* work(void* arg) +{ + waitForState(1); + dlopen("libbar.dylib", RTLD_LAZY); // dladdr() will hang until dyld lock is released + setState(2); // the initializer in libfoo.dylib is block waiting for state 2 + return NULL; +} + + +int main() +{ + pthread_t otherThread; + if ( pthread_create(&otherThread, NULL, work, NULL) != 0 ) { + FAIL("dlopen-dyld-locking: pthread_create failed"); + exit(0); + } + + + void* handle = dlopen("libfoo.dylib", RTLD_LAZY); + if ( handle == NULL ) { + FAIL("dlopen-dyld-locking: dlopen(\"libfoo.dylib\") failed: %s", dlerror()); + exit(0); + } + + PASS("dlopen-dyld-locking"); + return EXIT_SUCCESS; +} + + diff --git a/src/dyld-apple/unit-tests/test-cases/dlopen-error/foo.c b/src/dyld-apple/unit-tests/test-cases/dlopen-error/foo.c new file mode 100644 index 000000000..0fd6c1e7c --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlopen-error/foo.c @@ -0,0 +1,2 @@ + +void foo() {} diff --git a/src/dyld-apple/unit-tests/test-cases/dlopen-error/main.c b/src/dyld-apple/unit-tests/test-cases/dlopen-error/main.c new file mode 100644 index 000000000..481db0c28 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlopen-error/main.c @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2008 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include // strstr() +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + + +int main() +{ + // test error message of a dylib that does not exist + void* handle = dlopen("libdoesnotexist.dylib", RTLD_LAZY); + if ( handle != NULL ) { + FAIL("dlopen-error: dlopen(libdoesnotexist.dylib, RTLD_LAZY) should have failed"); + return 0; + } + if ( strstr(dlerror(), "image not found") == NULL ) { + FAIL("dlopen-error: expected 'image not found' in dlerror() string"); + return 0; + } + + // test error message of a dylib that is not readabble + handle = dlopen("libnoread.dylib", RTLD_LAZY); + if ( handle != NULL ) { + FAIL("dlopen-error: dlopen(libnoread.dylib, RTLD_LAZY) should have failed"); + return 0; + } + if ( strstr(dlerror(), "failed with errno=13") == NULL ) { + FAIL("dlopen-error: expected 'failed with errno=13' in dlerror() string"); + return 0; + } + + + PASS("dlopen-error"); + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlopen-executable/foo.c b/src/dyld-apple/unit-tests/test-cases/dlopen-executable/foo.c new file mode 100644 index 000000000..d63901138 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlopen-executable/foo.c @@ -0,0 +1,10 @@ +int foo() +{ + return 10; +} + +int main() +{ + return 0; +} + diff --git a/src/dyld-apple/unit-tests/test-cases/dlopen-executable/main.c b/src/dyld-apple/unit-tests/test-cases/dlopen-executable/main.c new file mode 100644 index 000000000..a9a6f21fb --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlopen-executable/main.c @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2008 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +typedef int (*fooproc)(void); + +int main() +{ + // dlopen of regular executable should fail + void* handle = dlopen("./foo.exe", RTLD_LAZY); + if ( handle != NULL ) { + FAIL("dlopen-executable: dlopen(\"./foo.exe\") did not fail"); + return EXIT_SUCCESS; + } + + // dlopen of pie should work + handle = dlopen("./foo.pie", RTLD_LAZY); + if ( handle == NULL ) { + FAIL("dlopen-executable: dlopen(\"./foo.pie\") failed with: %s", dlerror()); + return EXIT_SUCCESS; + } + + fooproc pfoo = (fooproc)dlsym(handle, "foo"); + if ( pfoo == NULL ) { + FAIL("dlopen-executable: dlsym(handle, \"foo\") failed"); + return EXIT_SUCCESS; + } + + if ( (*pfoo)() != 10 ) { + FAIL("dlopen-executable: foo() != 10"); + return EXIT_SUCCESS; + } + + int result = dlclose(handle); + if ( result != 0 ) { + FAIL("dlopen-executable: dlclose(handle) returned %d", result); + return EXIT_SUCCESS; + } + + PASS("dlopen-executable"); + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlopen-from-anonymous-code/foo.c b/src/dyld-apple/unit-tests/test-cases/dlopen-from-anonymous-code/foo.c new file mode 100644 index 000000000..fea884b9e --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlopen-from-anonymous-code/foo.c @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2006 Apple Computer, 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@ + */ + + +int foo() +{ + return 10; +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlopen-from-anonymous-code/main.c b/src/dyld-apple/unit-tests/test-cases/dlopen-from-anonymous-code/main.c new file mode 100644 index 000000000..e208e1464 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlopen-from-anonymous-code/main.c @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2006-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@ + */ +#include // fprintf(), NULL +#include // memcpy +#include // exit(), EXIT_SUCCESS +#include +#include // sys_icache_invalidate +#include // for mprotext +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + + +void* calldlopen(const char* path, int mode, void* (*dlopen_proc)(const char* path, int mode)) +{ + return (*dlopen_proc)(path, mode); +} + +#if __thumb__ + #define START_OF_FUNC(x) ((void*)((long)x & (-2))) + #define ADDR_FROM_BLOCK(x) ((void*)((long)x | 1)) +#else + #define START_OF_FUNC(x) (x) + #define ADDR_FROM_BLOCK(x) (x) +#endif + +// +// try calling dlopen() from code not owned by dyld +// +int main() +{ + // now try to create a page where foo() was + vm_address_t addr = 0; + kern_return_t r = vm_allocate(mach_task_self(), &addr, 4096, VM_FLAGS_ANYWHERE); + if ( r != KERN_SUCCESS ) { + FAIL("vm_allocate returned %d", r); + return 0; + } + void* codeBlock = (void*)(addr); + memcpy(codeBlock, START_OF_FUNC(calldlopen), 4096); + sys_icache_invalidate(codeBlock, 4096); + mprotect(codeBlock, 4096, PROT_READ | PROT_EXEC); + //fprintf(stderr, "codeBlock=%p\n", codeBlock); + + void* (*caller)(const char* path, int mode, void* (*dlopen_proc)(const char* path, int mode)) = ADDR_FROM_BLOCK(codeBlock); + + void* handle = (*caller)("foo.bundle", RTLD_LAZY, &dlopen); + if ( handle == NULL ) { + FAIL("dlopen(\"%s\") failed with: %s", "foo.bundle", dlerror()); + exit(0); + } + + void* sym = dlsym(handle, "foo"); + if ( sym == NULL ) { + FAIL("dlsym(handle, \"foo\") failed"); + exit(0); + } + + int result = dlclose(handle); + if ( result != 0 ) { + FAIL("dlclose(handle) returned %d", result); + exit(0); + } + + PASS("dlopen-from-anonymous-code"); + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlopen-in-initializer/foo.c b/src/dyld-apple/unit-tests/test-cases/dlopen-in-initializer/foo.c new file mode 100644 index 000000000..3bef65e74 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlopen-in-initializer/foo.c @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2006 Apple Computer, 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@ + */ + +#include + +static void myInit() __attribute__((constructor)); + +static void myInit() +{ + // call dlopen on a non-existent file to verify error handled cleanly + dlopen("/no_such/path", RTLD_LAZY); +} + + +int foo() +{ + return 10; +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlopen-in-initializer/main.c b/src/dyld-apple/unit-tests/test-cases/dlopen-in-initializer/main.c new file mode 100644 index 000000000..bdc6dcf28 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlopen-in-initializer/main.c @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +static void trySO(const char* path) +{ + void* handle = dlopen(path, RTLD_LAZY); + if ( handle == NULL ) { + FAIL("dlopen(\"%s\") failed with: %s", path, dlerror()); + exit(0); + } + + void* sym = dlsym(handle, "foo"); + if ( sym == NULL ) { + FAIL("dlsym(handle, \"foo\") failed"); + exit(0); + } + + int result = dlclose(handle); + if ( result != 0 ) { + if ( result == 1 ) { + // panther dyld returns 1 if you try to dlclose() a dylib + XFAIL("dlclose(handle) returned %d", result); + } + else { + FAIL("dlclose(handle) returned %d", result); + exit(0); + } + } + +} + + + +int main() +{ + trySO("test.bundle"); + trySO("test.dylib"); + + PASS("dlopen-in-initializer"); + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlopen-init-dlopen-notify/bar.c b/src/dyld-apple/unit-tests/test-cases/dlopen-init-dlopen-notify/bar.c new file mode 100644 index 000000000..e42599986 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlopen-init-dlopen-notify/bar.c @@ -0,0 +1 @@ +void bar() {} diff --git a/src/dyld-apple/unit-tests/test-cases/dlopen-init-dlopen-notify/foo.c b/src/dyld-apple/unit-tests/test-cases/dlopen-init-dlopen-notify/foo.c new file mode 100644 index 000000000..edbdbc46f --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlopen-init-dlopen-notify/foo.c @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2006 Apple Computer, 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@ + */ + +#include + + + +int foo() +{ + return 10; +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlopen-init-dlopen-notify/foo1.c b/src/dyld-apple/unit-tests/test-cases/dlopen-init-dlopen-notify/foo1.c new file mode 100644 index 000000000..c24996bd1 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlopen-init-dlopen-notify/foo1.c @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2006 Apple Computer, 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@ + */ + +#include + +static void myInit() __attribute__((constructor)); + +static void myInit() +{ + // call dlopen to verify that initializer lock can be held recursively + dlopen("libbar.dylib", RTLD_LAZY); +} + + +int foo1() +{ + return 10; +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlopen-init-dlopen-notify/foo2.c b/src/dyld-apple/unit-tests/test-cases/dlopen-init-dlopen-notify/foo2.c new file mode 100644 index 000000000..5dd0a2a74 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlopen-init-dlopen-notify/foo2.c @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2006 Apple Computer, 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@ + */ + +#include + +static void myInit() __attribute__((constructor)); + +static void myInit() +{ + +} + + +int foo2() +{ + return 10; +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlopen-init-dlopen-notify/main.cxx b/src/dyld-apple/unit-tests/test-cases/dlopen-init-dlopen-notify/main.cxx new file mode 100644 index 000000000..cb916f23b --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlopen-init-dlopen-notify/main.cxx @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +static void trySO(const char* path) +{ + void* handle = dlopen(path, RTLD_LAZY); + if ( handle == NULL ) { + FAIL("dlopen(\"%s\") failed with: %s", path, dlerror()); + exit(0); + } + + void* sym = dlsym(handle, "foo"); + if ( sym == NULL ) { + FAIL("dlsym(handle, \"foo\") failed"); + exit(0); + } + + int result = dlclose(handle); + if ( result != 0 ) { + if ( result == 1 ) { + // panther dyld returns 1 if you try to dlclose() a dylib + XFAIL("dlclose(handle) returned %d", result); + } + else { + FAIL("dlclose(handle) returned %d", result); + exit(0); + } + } + +} + +static std::set sCurrentImages; + +static void notify(const struct mach_header *mh, intptr_t vmaddr_slide) +{ + //fprintf(stderr, "mh=%p\n", mh); + if ( sCurrentImages.count(mh) != 0 ) { + FAIL("notified twice about %p", mh); + exit(0); + } + sCurrentImages.insert(mh); +} + + + +int main() +{ + _dyld_register_func_for_add_image(¬ify); + + trySO("libfoo.dylib"); + + PASS("dlopen-init-dlopen-notify"); + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlopen-init-dlopen-up/bar.c b/src/dyld-apple/unit-tests/test-cases/dlopen-init-dlopen-up/bar.c new file mode 100644 index 000000000..15b13278d --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlopen-init-dlopen-up/bar.c @@ -0,0 +1,5 @@ + + +extern int foo(); + +int bar() { return foo(); } diff --git a/src/dyld-apple/unit-tests/test-cases/dlopen-init-dlopen-up/foo.c b/src/dyld-apple/unit-tests/test-cases/dlopen-init-dlopen-up/foo.c new file mode 100644 index 000000000..a816ea86b --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlopen-init-dlopen-up/foo.c @@ -0,0 +1,38 @@ +/* + * 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@ + */ + +#include + +static void myInit() __attribute__((constructor)); + +static void myInit() +{ + // this is an upward dependency since libbar depends on libfoo + dlopen("libbar.dylib", RTLD_LAZY); +} + + +int foo() +{ + return 10; +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlopen-init-dlopen-up/main.c b/src/dyld-apple/unit-tests/test-cases/dlopen-init-dlopen-up/main.c new file mode 100644 index 000000000..d9a2c703d --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlopen-init-dlopen-up/main.c @@ -0,0 +1,60 @@ +/* + * 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +static void trySO(const char* path) +{ + void* handle = dlopen(path, RTLD_LAZY); + if ( handle == NULL ) { + FAIL("dlopen(\"%s\") failed with: %s", path, dlerror()); + exit(0); + } + + void* sym = dlsym(handle, "foo"); + if ( sym == NULL ) { + FAIL("dlsym(handle, \"foo\") failed"); + exit(0); + } + + int result = dlclose(handle); + if ( result != 0 ) { + FAIL("dlclose(handle) returned %d", result); + exit(0); + } + +} + + + +int main() +{ + trySO("libfoo.dylib"); + + PASS("dlopen-init-dlopen-up"); + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlopen-init-dlopen/bar.c b/src/dyld-apple/unit-tests/test-cases/dlopen-init-dlopen/bar.c new file mode 100644 index 000000000..e42599986 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlopen-init-dlopen/bar.c @@ -0,0 +1 @@ +void bar() {} diff --git a/src/dyld-apple/unit-tests/test-cases/dlopen-init-dlopen/foo.c b/src/dyld-apple/unit-tests/test-cases/dlopen-init-dlopen/foo.c new file mode 100644 index 000000000..78ab60d69 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlopen-init-dlopen/foo.c @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2006 Apple Computer, 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@ + */ + +#include + +static void myInit() __attribute__((constructor)); + +static void myInit() +{ + // call dlopen to verify that initializer lock can be held recursively + dlopen("libbar.dylib", RTLD_LAZY); +} + + +int foo() +{ + return 10; +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlopen-init-dlopen/main.c b/src/dyld-apple/unit-tests/test-cases/dlopen-init-dlopen/main.c new file mode 100644 index 000000000..9f1c5fbc0 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlopen-init-dlopen/main.c @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +static void trySO(const char* path) +{ + void* handle = dlopen(path, RTLD_LAZY); + if ( handle == NULL ) { + FAIL("dlopen(\"%s\") failed with: %s", path, dlerror()); + exit(0); + } + + void* sym = dlsym(handle, "foo"); + if ( sym == NULL ) { + FAIL("dlsym(handle, \"foo\") failed"); + exit(0); + } + + int result = dlclose(handle); + if ( result != 0 ) { + if ( result == 1 ) { + // panther dyld returns 1 if you try to dlclose() a dylib + XFAIL("dlclose(handle) returned %d", result); + } + else { + FAIL("dlclose(handle) returned %d", result); + exit(0); + } + } + +} + + + +int main() +{ + trySO("libfoo.dylib"); + + PASS("dlopen-init-dlopen"); + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlopen-init-up/bar.c b/src/dyld-apple/unit-tests/test-cases/dlopen-init-up/bar.c new file mode 100644 index 000000000..15b13278d --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlopen-init-up/bar.c @@ -0,0 +1,5 @@ + + +extern int foo(); + +int bar() { return foo(); } diff --git a/src/dyld-apple/unit-tests/test-cases/dlopen-init-up/foo.c b/src/dyld-apple/unit-tests/test-cases/dlopen-init-up/foo.c new file mode 100644 index 000000000..77dd28fb6 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlopen-init-up/foo.c @@ -0,0 +1,39 @@ +/* + * 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@ + */ + +#include + +static void myInit() __attribute__((constructor)); + +static void myInit() +{ + // this is an upward dependency since libbar depends on libfoo + // rdar://problem/5213017 + dlopen("libbar.dylib", RTLD_LAZY); +} + + +int foo() +{ + return 10; +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlopen-init-up/main.c b/src/dyld-apple/unit-tests/test-cases/dlopen-init-up/main.c new file mode 100644 index 000000000..abdd5f165 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlopen-init-up/main.c @@ -0,0 +1,33 @@ +/* + * 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +int main() +{ + PASS("dlopen-init-up"); + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlopen-initializer/bar.c b/src/dyld-apple/unit-tests/test-cases/dlopen-initializer/bar.c new file mode 100644 index 000000000..e055874b2 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlopen-initializer/bar.c @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ + + +static int initCount = 0; + + +void _init() +{ + initCount++; +} + +int getInitCount() +{ + return initCount; +} + diff --git a/src/dyld-apple/unit-tests/test-cases/dlopen-initializer/foo.c b/src/dyld-apple/unit-tests/test-cases/dlopen-initializer/foo.c new file mode 100644 index 000000000..9ffde512e --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlopen-initializer/foo.c @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ + + +static int initCount = 0; + +void INIT_NAME() __attribute__((constructor)); + +void INIT_NAME() +{ + initCount++; +} + +int getInitCount() +{ + return initCount; +} + diff --git a/src/dyld-apple/unit-tests/test-cases/dlopen-initializer/main.c b/src/dyld-apple/unit-tests/test-cases/dlopen-initializer/main.c new file mode 100644 index 000000000..5dc4f2e27 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlopen-initializer/main.c @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +typedef int (*getInitCountProc)(void); + + +static void trySO(const char* path) +{ + void* handle = dlopen(path, RTLD_LAZY); + if ( handle == NULL ) { + FAIL("dlopen(\"%s\") failed", path); + exit(0); + } + + getInitCountProc sym = (getInitCountProc)dlsym(handle, "getInitCount"); + if ( sym == NULL ) { + FAIL("dlsym(handle, \"getInitCount\") failed"); + exit(0); + } + + int count = (*sym)(); + if ( count != 1 ) { + FAIL("initializer in %s called %d times", path, count); + exit(0); + } + +} + + + +int main() +{ + trySO("test1.dylib"); + trySO("test2.dylib"); + //trySO("test3.dylib"); // Mac OS X 10.4 does not automatically run _init functions + PASS("dlopen-initializer"); + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlopen-leak-threaded/main.c b/src/dyld-apple/unit-tests/test-cases/dlopen-leak-threaded/main.c new file mode 100644 index 000000000..c1b3a3712 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlopen-leak-threaded/main.c @@ -0,0 +1,88 @@ +/* + * 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@ + */ +#include +#include +#include +#include +#include +#include + + +#include "test.h" + + +/// +/// Test that the dyld copy of the unwinder keeps each thread's +/// exception object seperate. +/// + +static void* work(void* arg) +{ + // will fail and cause exception to be thrown inside dyld + dlopen((char*)arg, RTLD_LAZY); + return NULL; +} + + +int main() +{ + pthread_t worker1; + if ( pthread_create(&worker1, NULL, work, "/frazzle/bar") != 0 ) { + FAIL("pthread_create failed"); + exit(0); + } + + pthread_t worker2; + if ( pthread_create(&worker2, NULL, work, "/frazzle/foo") != 0 ) { + FAIL("pthread_create failed"); + exit(0); + } + + pthread_t worker3; + if ( pthread_create(&worker3, NULL, work, "/frazzle/dazzle") != 0 ) { + FAIL("pthread_create failed"); + exit(0); + } + + + void* result; + //fprintf(stderr, "waiting for worker 1\n"); + pthread_join(worker1, &result); + //fprintf(stderr, "waiting for worker 2\n"); + pthread_join(worker2, &result); + //fprintf(stderr, "waiting for worker 3\n"); + pthread_join(worker3, &result); + + // execute leaks command on myself + char cmd[512]; + sprintf(cmd, "sudo leaks %u > /dev/null\n", getpid()); + int status = system(cmd); + if ( status == EXIT_SUCCESS ) + PASS("dlopen-leak-threaded"); + else + FAIL("dlopen-leak-threaded"); + + return EXIT_SUCCESS; +} + + diff --git a/src/dyld-apple/unit-tests/test-cases/dlopen-leak/bar.c b/src/dyld-apple/unit-tests/test-cases/dlopen-leak/bar.c new file mode 100644 index 000000000..a48d74f22 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlopen-leak/bar.c @@ -0,0 +1,6 @@ +#include + +void bar() +{ + strcpy(NULL, NULL); +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlopen-leak/foo.c b/src/dyld-apple/unit-tests/test-cases/dlopen-leak/foo.c new file mode 100644 index 000000000..3695dc91e --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlopen-leak/foo.c @@ -0,0 +1,3 @@ +void foo() +{ +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlopen-leak/main.c b/src/dyld-apple/unit-tests/test-cases/dlopen-leak/main.c new file mode 100644 index 000000000..3ff66e51e --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlopen-leak/main.c @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2007-2013 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@ + */ +#include +#include +#include +#include +#include + + +#include "test.h" + + +int main() +{ + for (int i=0; i < 100; ++i) { + void* handle = dlopen("libfoo.dylib", RTLD_LAZY); + if ( handle != NULL ) + dlclose(handle); + dlopen("libnotthere.dylib", RTLD_LAZY); + } + + + // execute leaks command on myself + char cmd[512]; + sprintf(cmd, "sudo leaks %u > /dev/null\n", getpid()); + int result = system(cmd); + if ( result == EXIT_SUCCESS ) + PASS("dlopen-leak"); + else + FAIL("dlopen-leak"); + + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlopen-local-and-global/bar.c b/src/dyld-apple/unit-tests/test-cases/dlopen-local-and-global/bar.c new file mode 100644 index 000000000..e8e0806aa --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlopen-local-and-global/bar.c @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ + +extern int foo; + +int bar() +{ + return foo; +} + + diff --git a/src/dyld-apple/unit-tests/test-cases/dlopen-local-and-global/foo.c b/src/dyld-apple/unit-tests/test-cases/dlopen-local-and-global/foo.c new file mode 100644 index 000000000..86895974c --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlopen-local-and-global/foo.c @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ + + +int foo = 0; diff --git a/src/dyld-apple/unit-tests/test-cases/dlopen-local-and-global/main.c b/src/dyld-apple/unit-tests/test-cases/dlopen-local-and-global/main.c new file mode 100644 index 000000000..5c3b97ee3 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlopen-local-and-global/main.c @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + + + +static void* openWithModeGetSymbol(const char* path, int mode, const char* symbol) +{ + void* handle = dlopen(path, mode); + if ( handle == NULL ) { + const char* msg = dlerror(); + if ( ((mode & RTLD_LOCAL) != 0) && (strstr(msg, "RTLD_LOCAL") != NULL) ) + XFAIL("dlopen(\"%s\") failed: %s", path, msg); + else + FAIL("dlopen(\"%s\") failed: %s", path, msg); + exit(0); + } + + void* sym = dlsym(handle, symbol); + if ( sym == NULL ) { + FAIL("dlsym(handle, \"%s\") failed", symbol); + exit(0); + } + return sym; +} + +static void trySO(const char* path) +{ +#if LOCAL_FIRST + void* symLocal = openWithModeGetSymbol(path, RTLD_LOCAL, "foo"); + void* symGlobal = openWithModeGetSymbol(path, RTLD_GLOBAL, "foo"); +#else + void* symGlobal = openWithModeGetSymbol(path, RTLD_GLOBAL, "foo"); + void* symLocal = openWithModeGetSymbol(path, RTLD_LOCAL, "foo"); +#endif + if ( symLocal != symGlobal ) { + FAIL("global load after local load failed"); + exit(0); + } +} + + +int main(int argc, const char* argv[]) +{ + trySO("foo.bundle"); + trySO("foo.dylib"); + + PASS("dlopen-local-and-global"); + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlopen-multi/foo.c b/src/dyld-apple/unit-tests/test-cases/dlopen-multi/foo.c new file mode 100644 index 000000000..81f7dcfc9 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlopen-multi/foo.c @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ + + +int foo() +{ + return 10; +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlopen-multi/main.c b/src/dyld-apple/unit-tests/test-cases/dlopen-multi/main.c new file mode 100644 index 000000000..180e91e02 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlopen-multi/main.c @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +int main() +{ + void* handle1 = dlopen("test.bundle", RTLD_LAZY); + void* handle2 = dlopen("test.bundle", RTLD_LAZY); + void* handle3 = dlopen("test.dylib", RTLD_LAZY); + void* handle4 = dlopen("test.dylib", RTLD_LAZY); + if ((NULL == handle1)||(NULL == handle2)) { + FAIL("dlopen(\"test.bundle\") failed"); + } + if ((NULL == handle3)||(NULL == handle4)) { + FAIL("dlopen(\"test.dylib\") failed"); + } + if (handle1 != handle2) { + FAIL("dlopen handle1 and handle2 are not equal %p != %p",handle1,handle2); + } + if (handle3 != handle4) { + FAIL("dlopen handle3 and handle4 are not equal %p != %p",handle3,handle4); + } + if (dlclose(handle4)) { + XFAIL("Could not close handle4"); + } + if (dlclose(handle2)) { + FAIL("Could not close handle2"); + } + void* sym = dlsym(handle1, "foo"); + if ( sym == NULL ) { + FAIL("dlsym(handle1, \"foo\") failed"); + exit(0); + } + sym = dlsym(handle3, "foo"); + if ( sym == NULL ) { + FAIL("dlsym(handle3, \"foo\") failed"); + exit(0); + } + if (dlclose(handle1)) { + XFAIL("Could not close handle1"); + } + if (dlclose(handle3)) { + XFAIL("Could not close handle3"); + } + + PASS("dlopen-multi"); + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlopen-non-canonical-path/main.c b/src/dyld-apple/unit-tests/test-cases/dlopen-non-canonical-path/main.c new file mode 100644 index 000000000..023c370fd --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlopen-non-canonical-path/main.c @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2009 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@ + */ +#include // EXIT_SUCCESS +#include +#include +#include + +#include "test.h" + +static void tryPath(const char* path) +{ + if ( dlopen_preflight(path) ) { + void* handle = dlopen(path, RTLD_LAZY); + if ( handle == NULL ) { + FAIL("dlopen-non-canonical-path: dlopen(%s)", path); + exit(0); + } + } + else { + FAIL("dlopen-non-canonical-path: dlopen_preflight(%s)", path); + exit(0); + } +} + +// +// dlopen() not opening frameworks with non-canonical paths +// + +int main() +{ + tryPath("//usr/lib/libSystem.B.dylib"); + tryPath("/usr/bin/../lib/libSystem.B.dylib"); + tryPath("/usr/lib/./libSystem.B.dylib"); + tryPath("/usr/lib//libSystem.B.dylib"); + + PASS("dlopen-non-canonical-path"); + return EXIT_SUCCESS; +} + + diff --git a/src/dyld-apple/unit-tests/test-cases/dlopen-notify-bind/foo.c b/src/dyld-apple/unit-tests/test-cases/dlopen-notify-bind/foo.c new file mode 100644 index 000000000..8dab6be7c --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlopen-notify-bind/foo.c @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2006 Apple Computer, 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@ + */ + +#include + + +void* externalRlocToMalloc = &malloc; + +void* foo() +{ + return externalRlocToMalloc; +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlopen-notify-bind/main.c b/src/dyld-apple/unit-tests/test-cases/dlopen-notify-bind/main.c new file mode 100644 index 000000000..630ba970f --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlopen-notify-bind/main.c @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2005-2009 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +typedef void* (*fooProc)(); + + +static void notify(const struct mach_header *mh, intptr_t vmaddr_slide) +{ +// NSLookupSymbolInImage is only available on Mac OS X - not iPhone OS +#if __MAC_OS_X_VERSION_MIN_REQUIRED + //fprintf(stderr, "mh=%p\n", mh); + NSLookupSymbolInImage(mh, "_bar", NSLOOKUPSYMBOLINIMAGE_OPTION_BIND_FULLY | NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR); +#endif +} + + +int main() +{ + _dyld_register_func_for_add_image(¬ify); + + void* handle = dlopen("libfoo.dylib", RTLD_LAZY); + if ( handle == NULL ) { + FAIL("dlopen(\"%s\") failed with: %s", "libfoo.dylib", dlerror()); + exit(0); + } + + fooProc fooPtr = (fooProc)dlsym(handle, "foo"); + if ( fooPtr == NULL ) { + FAIL("dlsym(handle, \"foo\") failed"); + exit(0); + } + + void* foosMalloc = (*fooPtr)(); + //fprintf(stderr, "foo says &malloc=%p\n", foosMalloc); + //fprintf(stderr, "&malloc=%p\n", &malloc); + + dlclose(handle); + + if ( foosMalloc == &malloc ) + PASS("dlopen-notify-bind"); + else + FAIL("dlopen-notify-bind libfoo.dylib double bound"); + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlopen-sandbox/base.sb b/src/dyld-apple/unit-tests/test-cases/dlopen-sandbox/base.sb new file mode 100644 index 000000000..c97f7e8ed --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlopen-sandbox/base.sb @@ -0,0 +1,9 @@ + +(version 1) +(allow default) +(debug deny) + +(deny FILTER + (literal "DIR/libfoo.dylib")) + + diff --git a/src/dyld-apple/unit-tests/test-cases/dlopen-sandbox/foo.c b/src/dyld-apple/unit-tests/test-cases/dlopen-sandbox/foo.c new file mode 100644 index 000000000..8f1b3d8b6 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlopen-sandbox/foo.c @@ -0,0 +1,7 @@ + + +int foo() +{ + return 42; +} + diff --git a/src/dyld-apple/unit-tests/test-cases/dlopen-sandbox/main.c b/src/dyld-apple/unit-tests/test-cases/dlopen-sandbox/main.c new file mode 100644 index 000000000..f3ade8f0a --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlopen-sandbox/main.c @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2015 Apple Computer, 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +int main(int argc, const char* argv[]) +{ + void* handle = dlopen("./libfoo.dylib", RTLD_LAZY); + if ( handle != NULL ) { + FAIL("dlopen-sandbox dylib should not have loaded"); + return EXIT_SUCCESS; + } + const char* errorMsg = dlerror(); + const char* shouldContain = argv[1]; + if ( strstr(errorMsg, shouldContain) == NULL ) + FAIL("dlopen-sandbox dylib correctly failed to loaded, but with wrong error message: %s", errorMsg); + else + PASS("dlopen-sandbox: %s", shouldContain); + + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlopen-search-leak/foo.c b/src/dyld-apple/unit-tests/test-cases/dlopen-search-leak/foo.c new file mode 100644 index 000000000..3695dc91e --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlopen-search-leak/foo.c @@ -0,0 +1,3 @@ +void foo() +{ +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlopen-search-leak/main.c b/src/dyld-apple/unit-tests/test-cases/dlopen-search-leak/main.c new file mode 100644 index 000000000..33c608be0 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlopen-search-leak/main.c @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2007-2009 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@ + */ +#include +#include +#include +#include +#include + + +#include "test.h" + + +int main() +{ + for (int i=0; i < 100; ++i) { + void* handle = dlopen("libfoo.dylib", RTLD_LAZY); + if ( handle != NULL ) + dlclose(handle); + } + + // execute leaks command on myself + char cmd[512]; + sprintf(cmd, "sudo leaks %u > /dev/null\n", getpid()); + int result = system(cmd); + if ( result == EXIT_SUCCESS ) + PASS("dlopen-search-leak"); + else + FAIL("dlopen-search-leak"); + + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlopen-zero/main.c b/src/dyld-apple/unit-tests/test-cases/dlopen-zero/main.c new file mode 100644 index 000000000..81977a420 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlopen-zero/main.c @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +int foo() +{ + return 42; +} + + +int main() +{ + // passing NULL as path to dlopen() has the special meaning of + // "get handle to main executable" + void* handle = dlopen(NULL, RTLD_LAZY); + if ( handle == NULL ) { + FAIL("dlopen(NULL, RTLD_LAZY) failed"); + exit(1); + } + + // make sure we find "foo" in this main executable + void* sym = dlsym(handle, "foo"); + if ( sym == NULL ) { + FAIL("dlsym(handle, \"foo\") failed"); + exit(1); + } + + if ( sym != &foo ) { + FAIL("dlsym(handle, \"foo\") returned wrong address"); + exit(1); + } + + PASS("dlopen-zero"); + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlopen_preflight-basic/bar.c b/src/dyld-apple/unit-tests/test-cases/dlopen_preflight-basic/bar.c new file mode 100644 index 000000000..63c34e06c --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlopen_preflight-basic/bar.c @@ -0,0 +1,2 @@ + +int bar = 10; diff --git a/src/dyld-apple/unit-tests/test-cases/dlopen_preflight-basic/foo.c b/src/dyld-apple/unit-tests/test-cases/dlopen_preflight-basic/foo.c new file mode 100644 index 000000000..6924ac69d --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlopen_preflight-basic/foo.c @@ -0,0 +1,3 @@ + +void foo() {} + diff --git a/src/dyld-apple/unit-tests/test-cases/dlopen_preflight-basic/main.c b/src/dyld-apple/unit-tests/test-cases/dlopen_preflight-basic/main.c new file mode 100644 index 000000000..4fdec0ef3 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlopen_preflight-basic/main.c @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include // fprintf(), NULL +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +// +// By returning a string, we prevent that image from loading. +// We just prevent any image with "bar" in its name from loading. +// + +static const char* batchMappedHandler(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info info[]) +{ + //for (uint32_t i=0; i < infoCount; ++i) { + // fprintf(stderr, "batch mapped: %d/%d %s\n", i, infoCount, info[i].imageFilePath); + //} + for (uint32_t i=0; i < infoCount; ++i) { + if ( strstr(info[i].imageFilePath, "bar") != NULL ) + return "cannot load bar"; + } + return NULL; +} + +static const char* singleMappedHandler(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info info[]) +{ + //fprintf(stderr, "single mapped: %s\n", info[0].imageFilePath); + if ( strstr(info[0].imageFilePath, "bar") != NULL ) + return "can't load bar"; + return NULL; +} + + +int main(int argc, const char* argv[]) +{ + // tell dyld we want to know when images are mapped + if ( argc > 1 ) + dyld_register_image_state_change_handler(dyld_image_state_dependents_mapped, true, batchMappedHandler); + else + dyld_register_image_state_change_handler(dyld_image_state_mapped, false, singleMappedHandler); + + if ( dlopen_preflight("foo.bundle") ) { + FAIL("dlopen_preflight-basic foo.bundle should not be loadable"); + exit(0); + } + //fprintf(stderr, "foo.bundle, dlerror: %s\n", dlerror()); + + if ( dlopen_preflight("bar.bundle") ) { + FAIL("dlopen_preflight-basic bar.bundle should not be loadable"); + exit(0); + } + //fprintf(stderr, "bar.bundle, dlerror: %s\n", dlerror()); + + if ( dlopen_preflight("bogus.bundle") ) { + FAIL("dlopen_preflight-basic bogus.bundle should not be loadable"); + exit(0); + } + //fprintf(stderr, "bogus.bundle, dlerror: %s\n", dlerror()); + + if ( ! dlopen_preflight("/usr/lib/libSystem.B.dylib") ) { + FAIL("dlopen_preflight-basic libSystem should be loadable, but got: %s", dlerror()); + exit(0); + } + + + PASS("dlopen_preflight-basic"); + + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlopen_preflight-cycle/bar.c b/src/dyld-apple/unit-tests/test-cases/dlopen_preflight-cycle/bar.c new file mode 100644 index 000000000..63c34e06c --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlopen_preflight-cycle/bar.c @@ -0,0 +1,2 @@ + +int bar = 10; diff --git a/src/dyld-apple/unit-tests/test-cases/dlopen_preflight-cycle/baz.c b/src/dyld-apple/unit-tests/test-cases/dlopen_preflight-cycle/baz.c new file mode 100644 index 000000000..256a0e368 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlopen_preflight-cycle/baz.c @@ -0,0 +1 @@ +void baz() {} diff --git a/src/dyld-apple/unit-tests/test-cases/dlopen_preflight-cycle/foo.c b/src/dyld-apple/unit-tests/test-cases/dlopen_preflight-cycle/foo.c new file mode 100644 index 000000000..6924ac69d --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlopen_preflight-cycle/foo.c @@ -0,0 +1,3 @@ + +void foo() {} + diff --git a/src/dyld-apple/unit-tests/test-cases/dlopen_preflight-cycle/main.c b/src/dyld-apple/unit-tests/test-cases/dlopen_preflight-cycle/main.c new file mode 100644 index 000000000..af5caec36 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlopen_preflight-cycle/main.c @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2011 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@ + */ +#include // fprintf(), NULL +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +int main(int argc, const char* argv[]) +{ + if ( ! dlopen_preflight("libfoo.dylib") ) { + FAIL("dlopen_preflight-cycle libfoo.dylib should not be loadable"); + return EXIT_SUCCESS; + } + + void* handle = dlopen("libbar.dylib", RTLD_NOLOAD); + if ( handle != NULL ) { + FAIL("dlopen_preflight-cycle libbar.dylib was left loaded by dlopen_preflight()"); + return EXIT_SUCCESS; + } + + handle = dlopen("libbaz.dylib", RTLD_NOLOAD); + if ( handle != NULL ) { + FAIL("dlopen_preflight-cycle libbaz.dylib was left loaded by dlopen_preflight()"); + return EXIT_SUCCESS; + } + + handle = dlopen("libfoo.dylib", RTLD_NOLOAD); + if ( handle != NULL ) { + FAIL("dlopen_preflight-cycle libfoo.dylib was left loaded by dlopen_preflight()"); + return EXIT_SUCCESS; + } + + PASS("dlopen_preflight-cycle"); + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlopen_preflight-leak-image-deny-single/foo.c b/src/dyld-apple/unit-tests/test-cases/dlopen_preflight-leak-image-deny-single/foo.c new file mode 100644 index 000000000..3695dc91e --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlopen_preflight-leak-image-deny-single/foo.c @@ -0,0 +1,3 @@ +void foo() +{ +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlopen_preflight-leak-image-deny-single/main.c b/src/dyld-apple/unit-tests/test-cases/dlopen_preflight-leak-image-deny-single/main.c new file mode 100644 index 000000000..48b5b72e7 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlopen_preflight-leak-image-deny-single/main.c @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2007-2008 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@ + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "test.h" + +static bool doCheck = false; + +static const char* batchMappedHandler(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info info[]) +{ + if ( doCheck ) + return "can't load"; + return NULL; +} + + +int main() +{ + // tell dyld we want to know when images are mapped + dyld_register_image_state_change_handler(dyld_image_state_mapped, false, batchMappedHandler); + doCheck = true; + + for (int i=0; i < 10; ++i) { + dlopen_preflight("libfoo.dylib"); + } + + // execute leaks command on myself + char cmd[512]; + sprintf(cmd, "sudo leaks %u > /dev/null\n", getpid()); + int result = system(cmd); + if ( result == EXIT_SUCCESS ) + PASS("dlopen_preflight-leak-image-deny-single"); + else + FAIL("dlopen_preflight-leak-image-deny-single"); + + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlopen_preflight-leak/bar.c b/src/dyld-apple/unit-tests/test-cases/dlopen_preflight-leak/bar.c new file mode 100644 index 000000000..b72a1a516 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlopen_preflight-leak/bar.c @@ -0,0 +1,3 @@ +void bar() +{ +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlopen_preflight-leak/foo.c b/src/dyld-apple/unit-tests/test-cases/dlopen_preflight-leak/foo.c new file mode 100644 index 000000000..3695dc91e --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlopen_preflight-leak/foo.c @@ -0,0 +1,3 @@ +void foo() +{ +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlopen_preflight-leak/main.c b/src/dyld-apple/unit-tests/test-cases/dlopen_preflight-leak/main.c new file mode 100644 index 000000000..094159702 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlopen_preflight-leak/main.c @@ -0,0 +1,49 @@ +/* + * 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@ + */ +#include +#include +#include +#include +#include + + +#include "test.h" + + +int main() +{ + for (int i=0; i < 100; ++i) { + dlopen_preflight("libfoo.dylib"); + } + + // execute leaks command on myself + char cmd[512]; + sprintf(cmd, "sudo leaks %u > /dev/null\n", getpid()); + int result = system(cmd); + if ( result == EXIT_SUCCESS ) + PASS("dlopen_preflight-leak"); + else + FAIL("dlopen_preflight-leak"); + + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlsym-RTLD_DEFAULT/foo.c b/src/dyld-apple/unit-tests/test-cases/dlsym-RTLD_DEFAULT/foo.c new file mode 100644 index 000000000..675f4df62 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlsym-RTLD_DEFAULT/foo.c @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include +#include +#include // exit(), EXIT_SUCCESS + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +char* strdup(const char* str) +{ + return "from foo"; +} + +typedef char* (*strdupProc)(const char* str); + + +void myinit() __attribute__((constructor)); +void myinit() +{ + strdupProc sym = (strdupProc)dlsym(RTLD_DEFAULT, "strdup"); + if ( sym == NULL ) { + FAIL("dlsym(RTLD_DEFAULT, \"strdup\") failed"); + exit(0); + } + + const char* result = (*sym)("hello"); + if ( strcmp(result, "from main") != 0 ) { + FAIL("dlsym(RTLD_DEFAULT, \"strdup\") returned wrong strdup: %s", result); + exit(0); + } + +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlsym-RTLD_DEFAULT/main.c b/src/dyld-apple/unit-tests/test-cases/dlsym-RTLD_DEFAULT/main.c new file mode 100644 index 000000000..ed3d88430 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlsym-RTLD_DEFAULT/main.c @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +typedef char* (*strdupProc)(const char* str); + + +char* strdup(const char* str) +{ + return "from main"; +} + + +static void trySO(const char* path) +{ + void* handle = dlopen(path, RTLD_LAZY); + if ( handle == NULL ) { + FAIL("dlopen(\"%s\") failed", path); + exit(0); + } + + strdupProc sym = (strdupProc)dlsym(RTLD_DEFAULT, "strdup"); + if ( sym == NULL ) { + FAIL("dlsym(RTLD_DEFAULT, \"strdup\") failed"); + exit(0); + } + + const char* result = (*sym)("hello"); + if ( strcmp(result, "from main") != 0 ) { + FAIL("dlsym(RTLD_DEFAULT, \"strdup\") returned wrong strdup: %s", result); + exit(0); + } + +} + + + +int main() +{ + trySO("test.bundle"); + trySO("test.dylib"); + + PASS("dlsym-RTLD_DEFAULT bundle and dylib"); + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlsym-RTLD_MAIN_ONLY/foo.c b/src/dyld-apple/unit-tests/test-cases/dlsym-RTLD_MAIN_ONLY/foo.c new file mode 100644 index 000000000..a82c67df9 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlsym-RTLD_MAIN_ONLY/foo.c @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ + + +int foo() +{ + return 10; +} + +int bar() +{ + return 10; +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlsym-RTLD_MAIN_ONLY/main.c b/src/dyld-apple/unit-tests/test-cases/dlsym-RTLD_MAIN_ONLY/main.c new file mode 100644 index 000000000..65aacee0a --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlsym-RTLD_MAIN_ONLY/main.c @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2008 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +int foo() +{ + return 0; +} + + +int main() +{ + // make sure we get the foo() in main and not the one in libfoo.dylib + if ( dlsym(RTLD_MAIN_ONLY, "foo") != &foo ) { + FAIL("dlsym(RTLD_MAIN_ONLY, \"foo\") returned wrong value"); + } + + // make sure don't find bar() in libfoo.dylib + if ( dlsym(RTLD_MAIN_ONLY, "bar") != NULL ) { + FAIL("dlsym(RTLD_MAIN_ONLY, \"bar\") returned non-NULL"); + } + + + PASS("dlsym-RTLD_MAIN_ONLY"); + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlsym-RTLD_NEXT-missing/foo1.c b/src/dyld-apple/unit-tests/test-cases/dlsym-RTLD_NEXT-missing/foo1.c new file mode 100644 index 000000000..eda5424f7 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlsym-RTLD_NEXT-missing/foo1.c @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ + + +int foo1() +{ + return 10; +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlsym-RTLD_NEXT-missing/foo2.c b/src/dyld-apple/unit-tests/test-cases/dlsym-RTLD_NEXT-missing/foo2.c new file mode 100644 index 000000000..a17cf3d87 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlsym-RTLD_NEXT-missing/foo2.c @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ + + +int foo2() +{ + return 10; +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlsym-RTLD_NEXT-missing/foo3.c b/src/dyld-apple/unit-tests/test-cases/dlsym-RTLD_NEXT-missing/foo3.c new file mode 100644 index 000000000..923e3638f --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlsym-RTLD_NEXT-missing/foo3.c @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ + + +int foo3() +{ + return 10; +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlsym-RTLD_NEXT-missing/main.c b/src/dyld-apple/unit-tests/test-cases/dlsym-RTLD_NEXT-missing/main.c new file mode 100644 index 000000000..914cca628 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlsym-RTLD_NEXT-missing/main.c @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +/// +/// This process has no "bar" function, but it does have libraries with a +/// circular dependency. In 10.4.0 this cause dlsym(RTLD_NEXT) to +/// go into an infinite loop. +/// + + + +int main() +{ + dlsym(RTLD_NEXT, "bar"); + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlsym-RTLD_NEXT/foo.c b/src/dyld-apple/unit-tests/test-cases/dlsym-RTLD_NEXT/foo.c new file mode 100644 index 000000000..81f7dcfc9 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlsym-RTLD_NEXT/foo.c @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ + + +int foo() +{ + return 10; +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlsym-RTLD_NEXT/main.c b/src/dyld-apple/unit-tests/test-cases/dlsym-RTLD_NEXT/main.c new file mode 100644 index 000000000..5c1064b09 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlsym-RTLD_NEXT/main.c @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +/// +/// This process has five foo functions. They are in: +/// main, test.bundle, test.dylib, foo1.dylib, foo2.dylib +/// +/// Both test.bundle and test.dylib call dlsym(RTLD_NEXT, "foo"); +/// They should find the ones in foo1.dylib and foo2.dylib respectively. +/// We test this be looking up those symbols explictly. +/// + + +int foo() +{ + return 0; +} + +typedef void* (*TestProc)(void); + +static void trySO(const char* pathToLoad, const char* indirectLibrary) +{ + void* indirectHandle = dlopen(indirectLibrary, RTLD_LAZY); + if ( indirectHandle == NULL ) { + FAIL("dlopen(\"%s\") failed", indirectLibrary); + exit(0); + } + + void* indirectFoo = (TestProc)dlsym(indirectHandle, "foo"); + if ( indirectFoo == NULL ) { + FAIL("dlsym(handle, \"test\") failed"); + exit(0); + } + + void* handle = dlopen(pathToLoad, RTLD_LAZY); + if ( handle == NULL ) { + FAIL("dlopen(\"%s\") failed", pathToLoad); + exit(0); + } + + TestProc sym = (TestProc)dlsym(handle, "test"); + if ( sym == NULL ) { + FAIL("dlsym(handle, \"test\") failed"); + exit(0); + } + + void* targetFoo = (*sym)(); + + //printf("targetFoo = %p, indirectFoo = %p\n", targetFoo, indirectFoo); + + if ( targetFoo != indirectFoo ) { + FAIL("dlsym-RTLD_NEXT wrong foo found"); + exit(0); + } + +} + + + +int main() +{ + trySO("test.bundle", "foo1.dylib"); + trySO("test.dylib", "foo2.dylib"); + + PASS("dlsym-RTLD_NEXT bundle and dylib"); + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlsym-RTLD_NEXT/test.c b/src/dyld-apple/unit-tests/test-cases/dlsym-RTLD_NEXT/test.c new file mode 100644 index 000000000..0d4b46382 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlsym-RTLD_NEXT/test.c @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ + +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +void* test() +{ + return dlsym(RTLD_NEXT, "foo"); +} + +int foo() +{ + return 2; +} \ No newline at end of file diff --git a/src/dyld-apple/unit-tests/test-cases/dlsym-RTLD_SELF/foo.c b/src/dyld-apple/unit-tests/test-cases/dlsym-RTLD_SELF/foo.c new file mode 100644 index 000000000..81f7dcfc9 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlsym-RTLD_SELF/foo.c @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ + + +int foo() +{ + return 10; +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlsym-RTLD_SELF/main.c b/src/dyld-apple/unit-tests/test-cases/dlsym-RTLD_SELF/main.c new file mode 100644 index 000000000..8c137c6e3 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlsym-RTLD_SELF/main.c @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +/// +/// This process has five foo functions. They are in: +/// main, test.bundle, test.dylib, foo1.dylib, foo2.dylib +/// +/// Both test.bundle and test.dylib call dlsym(RTLD_SELF, "foo"); +/// They should find the ones in their own linkage unit and +/// call FAIL() otherwise. +/// We also check that this works in the main executable. +/// + + +int foo() +{ + return 0; +} + +typedef void (*TestProc)(void); + +static void trySO(const char* pathToLoad) +{ + void* handle = dlopen(pathToLoad, RTLD_LAZY); + if ( handle == NULL ) { + FAIL("dlopen(\"%s\") failed", pathToLoad); + exit(0); + } + + TestProc sym = (TestProc)dlsym(handle, "test"); + if ( sym == NULL ) { + FAIL("dlsym(handle, \"test\") failed"); + exit(0); + } + + (*sym)(); +} + + + +int main() +{ + trySO("test.bundle"); + trySO("test.dylib"); + + if ( dlsym(RTLD_SELF, "foo") != &foo ) { + FAIL("dlsym(RTLD_SELF, \"foo\") returned wrong value"); + } + + PASS("dlsym-RTLD_SELF bundle and dylib"); + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlsym-RTLD_SELF/test.c b/src/dyld-apple/unit-tests/test-cases/dlsym-RTLD_SELF/test.c new file mode 100644 index 000000000..b4ae94127 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlsym-RTLD_SELF/test.c @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ + +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +int foo() +{ + return 2; +} + +void test() +{ +#ifdef RTLD_SELF + if ( dlsym(RTLD_SELF, "foo") != &foo ) + FAIL("dlsym(RTLD_SELF, \"foo\") returned wrong value"); +#endif +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlsym-error/main.c b/src/dyld-apple/unit-tests/test-cases/dlsym-error/main.c new file mode 100644 index 000000000..cb4a9d662 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlsym-error/main.c @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +int foo() +{ + return 42; +} + + +int main() +{ + void* handle = (void*)0x12345; // bogus value + + // expect dlsym() to return NULL + void* sym = dlsym(handle, "foo"); + if ( sym != NULL ) { + FAIL("dlsym(handle, \"foo\") should not have succeeded"); + exit(1); + } + // expect dlerro() to mention "handle" + if ( strstr(dlerror(), "handle") == NULL ) { + FAIL("dlerror() after dlsym(handle, \"foo\") does not mention \"handle\""); + exit(1); + } + + PASS("dlsym-error"); + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlsym-indirect/foo.c b/src/dyld-apple/unit-tests/test-cases/dlsym-indirect/foo.c new file mode 100644 index 000000000..81f7dcfc9 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlsym-indirect/foo.c @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ + + +int foo() +{ + return 10; +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlsym-indirect/foo1.c b/src/dyld-apple/unit-tests/test-cases/dlsym-indirect/foo1.c new file mode 100644 index 000000000..eda5424f7 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlsym-indirect/foo1.c @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ + + +int foo1() +{ + return 10; +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlsym-indirect/foo2.c b/src/dyld-apple/unit-tests/test-cases/dlsym-indirect/foo2.c new file mode 100644 index 000000000..a17cf3d87 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlsym-indirect/foo2.c @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ + + +int foo2() +{ + return 10; +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlsym-indirect/foo3.c b/src/dyld-apple/unit-tests/test-cases/dlsym-indirect/foo3.c new file mode 100644 index 000000000..923e3638f --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlsym-indirect/foo3.c @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ + + +int foo3() +{ + return 10; +} diff --git a/src/dyld-apple/unit-tests/test-cases/dlsym-indirect/main.c b/src/dyld-apple/unit-tests/test-cases/dlsym-indirect/main.c new file mode 100644 index 000000000..e619d824b --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dlsym-indirect/main.c @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +/// +/// This tests that dlsym() will search indirect libraries +/// rdar://problem/4047391 +/// + + + +int main(int argc, const char* argv[]) +{ + const char* path = argv[1]; + const char* otherPath = "foo3.dylib"; + + void* handle = dlopen(path, RTLD_LAZY); + if ( handle == NULL ) { + FAIL("dlsym-indirect dlopen(\"%s\") failed", path); + exit(0); + } + + void* handle3 = dlopen(otherPath, RTLD_LAZY); + if ( handle3 == NULL ) { + FAIL("dlsym-indirect dlopen(\"%s\") failed", otherPath); + exit(0); + } + + void* foo = dlsym(handle, "foo"); + if ( foo == NULL ) { + FAIL("dlsym-indirect dlsym(handle, \"foo\") failed"); + exit(0); + } + + void* foo1 = dlsym(handle, "foo1"); + if ( foo1 == NULL ) { + FAIL("dlsym-indirect dlsym(handle, \"foo1\") failed"); + //exit(0); + } + + void* foo2 = dlsym(handle, "foo2"); + if ( foo2 == NULL ) { + FAIL("dlsym-indirect dlsym(handle, \"foo2\") failed"); + //exit(0); + } + + void* foo3 = dlsym(handle, "foo3"); + if ( foo3 != NULL ) { + FAIL("dlsym-indirect dlsym(handle, \"foo3\") should have failed"); + //exit(0); + } + + PASS("dlsym-indirect %s", path); + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/dtrace-static-probes/foo.d b/src/dyld-apple/unit-tests/test-cases/dtrace-static-probes/foo.d new file mode 100644 index 000000000..930965bcb --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dtrace-static-probes/foo.d @@ -0,0 +1,7 @@ + +provider Foo { + probe count(int); +}; + + +#pragma D attributes Evolving/Evolving/Common provider Foo args diff --git a/src/dyld-apple/unit-tests/test-cases/dtrace-static-probes/main.c b/src/dyld-apple/unit-tests/test-cases/dtrace-static-probes/main.c new file mode 100644 index 000000000..851bb3381 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dtrace-static-probes/main.c @@ -0,0 +1,13 @@ + +#include + +#include "foo.h" + + +int main() { + for (int i=0; i < 5; ++i) { + FOO_COUNT(i); + } + + return 0; +} diff --git a/src/dyld-apple/unit-tests/test-cases/dyld-func-lookup/foo.c b/src/dyld-apple/unit-tests/test-cases/dyld-func-lookup/foo.c new file mode 100644 index 000000000..bc232a2ec --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dyld-func-lookup/foo.c @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2005-2009 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@ + */ + +#include +#include + + +// _dyld_func_lookup is only available in 10.5 and earlier +#if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && (__MAC_OS_X_VERSION_MIN_REQUIRED <= __MAC_10_5) + +extern bool _dyld_func_lookup(const char* dyld_func_name, void** address); + + + +bool check_dyld_func_lookup() +{ + void* address; + + // dlopen does not exist, something is wrong + if ( ! _dyld_func_lookup("__dyld_dlopen", &address) ) + return false; + + // if a garbage string returns true, something is wrong + if ( _dyld_func_lookup("blablah", &address) ) + return false; + + // looks good + return true; +} + +#endif diff --git a/src/dyld-apple/unit-tests/test-cases/dyld-func-lookup/main.c b/src/dyld-apple/unit-tests/test-cases/dyld-func-lookup/main.c new file mode 100644 index 000000000..35bbd9ff5 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dyld-func-lookup/main.c @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2006 Apple Computer, 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +// _dyld_func_lookup is only available in 10.5 and earlier +#if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && (__MAC_OS_X_VERSION_MIN_REQUIRED <= __MAC_10_5) +extern bool check_dyld_func_lookup(); + +typedef bool (*proc)(void); + +static void trySO(const char* path) +{ + void* handle = dlopen(path, RTLD_LAZY); + if ( handle == NULL ) { + FAIL("dlopen(\"%s\") failed with: %s", path, dlerror()); + exit(0); + } + + proc sym = (proc)dlsym(handle, "check_dyld_func_lookup"); + if ( sym == NULL ) { + FAIL("dlsym(handle, \"foo\") failed"); + exit(0); + } + + if ( sym == &check_dyld_func_lookup ) + FAIL("found same check_dyld_func_lookup as in main executable"); + + if ( ! (*sym)() ) + FAIL("check_dyld_func_lookup failed for %s", path); + + dlclose(handle); +} +#endif + + +int main() +{ +// _dyld_func_lookup is only available in 10.5 and earlier +#if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && (__MAC_OS_X_VERSION_MIN_REQUIRED <= __MAC_10_5) + if ( ! check_dyld_func_lookup() ) + FAIL("check_dyld_func_lookup failed for main executable"); + + trySO("test.bundle"); + trySO("test.dylib"); +#endif + + PASS("dyld-func-lookup"); + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/dyld-launched-prebound/main.c b/src/dyld-apple/unit-tests/test-cases/dyld-launched-prebound/main.c new file mode 100644 index 000000000..aa6ab80e8 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dyld-launched-prebound/main.c @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2005-2009 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +int +main(int argc, const char* argv[]) +{ +#if __MAC_OS_X_VERSION_MIN_REQUIRED + _dyld_launched_prebound(); +#endif + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/dyld_is_memory_immutable/bar.c b/src/dyld-apple/unit-tests/test-cases/dyld_is_memory_immutable/bar.c new file mode 100644 index 000000000..7bf381e2e --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dyld_is_memory_immutable/bar.c @@ -0,0 +1,7 @@ + + + +const char* bar() +{ + return "bar"; +} diff --git a/src/dyld-apple/unit-tests/test-cases/dyld_is_memory_immutable/foo.c b/src/dyld-apple/unit-tests/test-cases/dyld_is_memory_immutable/foo.c new file mode 100644 index 000000000..ab30bb41c --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dyld_is_memory_immutable/foo.c @@ -0,0 +1,6 @@ + + +const char* foo() +{ + return "foo"; +} \ No newline at end of file diff --git a/src/dyld-apple/unit-tests/test-cases/dyld_is_memory_immutable/main.c b/src/dyld-apple/unit-tests/test-cases/dyld_is_memory_immutable/main.c new file mode 100644 index 000000000..aa3a9d4ef --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dyld_is_memory_immutable/main.c @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2015 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +extern uint32_t _cpu_capabilities; + +extern const char* foo(); + +typedef const char* (*BarProc)(void); + +const char* myStr = "myStr"; + +int myInt; + +int main() +{ + if ( !_dyld_is_memory_immutable(myStr, 6) ) { + FAIL("_dyld_is_memory_immutable() returned false for string in main executable"); + return EXIT_SUCCESS; + } + + if ( _dyld_is_memory_immutable(strdup("hello"), 6) ) { + FAIL("_dyld_is_memory_immutable() returned true for result from strdup()"); + return EXIT_SUCCESS; + } + + if ( _dyld_is_memory_immutable(&myInt, 4) ) { + FAIL("_dyld_is_memory_immutable() returned true for global variabe in main executable"); + return EXIT_SUCCESS; + } + + if ( !_dyld_is_memory_immutable(foo(), 4) ) { + FAIL("_dyld_is_memory_immutable() returned false for string in statically linked dylib"); + return EXIT_SUCCESS; + } + + if ( !_dyld_is_memory_immutable(&strcpy, 4) ) { + FAIL("_dyld_is_memory_immutable() returned false for function in dyld shared cache"); + return EXIT_SUCCESS; + } + + if ( _dyld_is_memory_immutable(&_cpu_capabilities, 4) ) { + FAIL("_dyld_is_memory_immutable() returned true for global variabe in shared cache"); + return EXIT_SUCCESS; + } + + void* handle = dlopen("libbar.dylib", 0); + if ( handle == NULL ) { + FAIL("dlopen(libbar.dylib) failed"); + return EXIT_SUCCESS; + } + + BarProc proc = dlsym(handle, "bar"); + if ( proc == NULL ) { + FAIL("dlsym(bar) failed"); + return EXIT_SUCCESS; + } + const char* barStr = (*proc)(); + if ( _dyld_is_memory_immutable(barStr, 4) ) { + FAIL("_dyld_is_memory_immutable() returned true for string in unloadable dylib"); + return EXIT_SUCCESS; + } + + + PASS("_dyld_is_memory_immutable"); + return 0; +} diff --git a/src/dyld-apple/unit-tests/test-cases/dyld_shared_cache_iterate_text/main.c b/src/dyld-apple/unit-tests/test-cases/dyld_shared_cache_iterate_text/main.c new file mode 100644 index 000000000..228935beb --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dyld_shared_cache_iterate_text/main.c @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2015 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@ + */ +#include // EXIT_SUCCESS +#include +#include +#include +#include +#include +#include +#include + +#include "test.h" +#include "dyld_cache_format.h" + + + +int main() +{ + const struct dyld_all_image_infos* allInfo = _dyld_get_all_image_infos(); + if ( allInfo == NULL ) { + FAIL("dyld_shared_cache_iterate_text: _dyld_get_all_image_infos() failed"); + exit(0); + } + uuid_t curUuid; + memcpy(curUuid, allInfo->sharedCacheUUID, 16); + + int __block imageCount = 0; + int result = dyld_shared_cache_iterate_text(curUuid, ^(const dyld_shared_cache_dylib_text_info* info) { + ++imageCount; + //printf(" cur: 0x%09llX -> 0x%09llX %s\n", info->loadAddressUnslid, info->loadAddressUnslid + info->textSegmentSize, info->path); + }); + if ( result != 0 ) { + FAIL("dyld_shared_cache_iterate_text: dyld_shared_cache_iterate_text() failed"); + exit(0); + } + + if ( imageCount < 500 ) { + FAIL("dyld_shared_cache_iterate_text: dyld_shared_cache_iterate_text() iterated less than 500 dylibs (%d)", imageCount); + exit(0); + } +#if 0 + //uuid_t fixedUUID = {0x3E, 0xDE, 0x37, 0x05, 0x81, 0x68, 0x33, 0xEF, 0xBF, 0x97, 0xC0, 0xF6, 0xD2, 0x4D, 0x93, 0xEC}; + uuid_t fixedUUID = {0xD4, 0x3B, 0x31, 0x2B, 0xA5, 0xA7, 0x3C, 0x55, 0x90, 0xA0, 0x9A, 0x37, 0x60, 0x7D, 0x70, 0xAF}; + result = dyld_shared_cache_iterate_text(fixedUUID, ^(const dyld_shared_cache_dylib_text_info* info) { + printf(" my: 0x%09llX -> 0x%09llX %s\n", info->loadAddressUnslid, info->loadAddressUnslid + info->textSegmentSize, info->path); + }); +#endif + + PASS("dyld_shared_cache_iterate_text"); + return EXIT_SUCCESS; +} + + diff --git a/src/dyld-apple/unit-tests/test-cases/dynamic_cast-basic/foo.cxx b/src/dyld-apple/unit-tests/test-cases/dynamic_cast-basic/foo.cxx new file mode 100644 index 000000000..5c824bbe2 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dynamic_cast-basic/foo.cxx @@ -0,0 +1,15 @@ + +#include + +#include "foo.h" + +test* maketestsub() +{ + return new testsub(); +} + +bool istestsub(test* t) +{ + return (dynamic_cast(t) != NULL); +} + diff --git a/src/dyld-apple/unit-tests/test-cases/dynamic_cast-basic/foo.h b/src/dyld-apple/unit-tests/test-cases/dynamic_cast-basic/foo.h new file mode 100644 index 000000000..940e356b0 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dynamic_cast-basic/foo.h @@ -0,0 +1,32 @@ + + +class test +{ +public: + virtual void aa() {} + + int f; +}; + +class testsub : public test +{ +public: + testsub() : g(0) {} + virtual void aa() {} + + int g; +}; + + +class testsubother : public test +{ +public: + testsubother() : h(0) {} + virtual void aa() {} + + int h; +}; + +extern test* maketestsub(); + +extern bool istestsub(test* t); \ No newline at end of file diff --git a/src/dyld-apple/unit-tests/test-cases/dynamic_cast-basic/main.cxx b/src/dyld-apple/unit-tests/test-cases/dynamic_cast-basic/main.cxx new file mode 100644 index 000000000..a25c44805 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dynamic_cast-basic/main.cxx @@ -0,0 +1,10 @@ + +extern void realmain(); + +int main() +{ + realmain(); + return 0; +} + + diff --git a/src/dyld-apple/unit-tests/test-cases/dynamic_cast-basic/realmain.cxx b/src/dyld-apple/unit-tests/test-cases/dynamic_cast-basic/realmain.cxx new file mode 100644 index 000000000..e951768e7 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/dynamic_cast-basic/realmain.cxx @@ -0,0 +1,24 @@ + + +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +#include "foo.h" + + +void realmain() +{ + test* t1 = maketestsub(); + test* t2 = new testsub(); + test* t3 = new testsubother(); + testsub* t1a = dynamic_cast(t1); + testsubother* t3a = dynamic_cast(t3); + if ( (t1a == NULL) || (t3a == NULL) || !istestsub(t2) ) + FAIL("dynamic_cast-basic"); + else + PASS("dynamic_cast-basic"); +} + + diff --git a/src/dyld-apple/unit-tests/test-cases/env-DYLD_FALLBACK_LIBRARY_PATH/compress.c b/src/dyld-apple/unit-tests/test-cases/env-DYLD_FALLBACK_LIBRARY_PATH/compress.c new file mode 100644 index 000000000..b45d61317 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/env-DYLD_FALLBACK_LIBRARY_PATH/compress.c @@ -0,0 +1,4 @@ +int compress() +{ + return 0; +} diff --git a/src/dyld-apple/unit-tests/test-cases/env-DYLD_FALLBACK_LIBRARY_PATH/main.c b/src/dyld-apple/unit-tests/test-cases/env-DYLD_FALLBACK_LIBRARY_PATH/main.c new file mode 100644 index 000000000..353403273 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/env-DYLD_FALLBACK_LIBRARY_PATH/main.c @@ -0,0 +1,24 @@ +#include +#include +#include +#include + +#include "test.h" + +extern void compress(); + +int main(int argc, const char* argv[]) +{ +#if !SHOULD_FAIL + Dl_info info; + if ( dladdr(&compress, &info) ) { + //fprintf(stderr, "_compress found in %s\n", info.dli_fname); + if ( strstr(info.dli_fname, argv[1]) != 0 ) { + PASS("env-DYLD_FALLBACK_LIBRARY_PATH"); + exit(0); + } + } + FAIL("env-DYLD_FALLBACK_LIBRARY_PATH"); +#endif + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/executable-image-index/foo.c b/src/dyld-apple/unit-tests/test-cases/executable-image-index/foo.c new file mode 100644 index 000000000..83c0b65e0 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/executable-image-index/foo.c @@ -0,0 +1,2 @@ +void foo() { } + diff --git a/src/dyld-apple/unit-tests/test-cases/executable-image-index/main.c b/src/dyld-apple/unit-tests/test-cases/executable-image-index/main.c new file mode 100644 index 000000000..315ea8e56 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/executable-image-index/main.c @@ -0,0 +1,45 @@ +/* + * 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@ + */ +#include +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +// many apps rely on _dyld_get_image_header(0) being the main executable +// DYLD_INSERT_LIBRARIES can change the order + +extern const struct mach_header __dso_handle; + +int main() +{ + if ( _dyld_get_image_header(0) == &__dso_handle ) + PASS("executable-image-index"); + else + FAIL("executable-image-index: index(0) is %s", _dyld_get_image_name(0)); + + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/fallback-non-unique-leaf-names/bar.c b/src/dyld-apple/unit-tests/test-cases/fallback-non-unique-leaf-names/bar.c new file mode 100644 index 000000000..b4415f859 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/fallback-non-unique-leaf-names/bar.c @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include + +extern int foo(); + +int bar() +{ + return foo(); +} diff --git a/src/dyld-apple/unit-tests/test-cases/fallback-non-unique-leaf-names/foo.c b/src/dyld-apple/unit-tests/test-cases/fallback-non-unique-leaf-names/foo.c new file mode 100644 index 000000000..9644c2359 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/fallback-non-unique-leaf-names/foo.c @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include + +int foo() +{ + return FOO; +} diff --git a/src/dyld-apple/unit-tests/test-cases/fallback-non-unique-leaf-names/main.c b/src/dyld-apple/unit-tests/test-cases/fallback-non-unique-leaf-names/main.c new file mode 100644 index 000000000..351769ade --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/fallback-non-unique-leaf-names/main.c @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include +#include + +#include "test.h" + +/// +/// This process has two libfoo.dylibs each of which has a foo() that returns a different value. +/// If dyld only loads one libfoo.dylib, then this test fails +/// main links directly against one libfoo.dylib and indirectly through libbar.dylib with the other. +/// + +extern int foo(); +extern int bar(); + +int main(int argc, const char* argv[]) +{ + if ( foo() == bar() ) { + FAIL("fallback-non-unique-leaf-names"); + return EXIT_SUCCESS; + } + + PASS("fallback-non-unique-leaf-names"); + return EXIT_SUCCESS; +} + diff --git a/src/dyld-apple/unit-tests/test-cases/fallback-with-suid/foo.c b/src/dyld-apple/unit-tests/test-cases/fallback-with-suid/foo.c new file mode 100644 index 000000000..4ecb9310f --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/fallback-with-suid/foo.c @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2006 Apple Computer, 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@ + */ + +void compress() {} + diff --git a/src/dyld-apple/unit-tests/test-cases/fallback-with-suid/main.c b/src/dyld-apple/unit-tests/test-cases/fallback-with-suid/main.c new file mode 100644 index 000000000..bb793934c --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/fallback-with-suid/main.c @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2006 Apple Computer, 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include // strcmp(), strncmp() +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +// +// binaries set to run as some other user id never use $HOME part of fallback-path +// + +extern void compress(); + +int main(int argc, const char *argv[]) +{ + if ( &compress != NULL ) + FAIL("fallback-with-suid root"); + + return EXIT_SUCCESS; +} + diff --git a/src/dyld-apple/unit-tests/test-cases/flat-data/bar.c b/src/dyld-apple/unit-tests/test-cases/flat-data/bar.c new file mode 100644 index 000000000..a53bfdd84 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/flat-data/bar.c @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ + +int bar = 2; diff --git a/src/dyld-apple/unit-tests/test-cases/flat-data/getbar.c b/src/dyld-apple/unit-tests/test-cases/flat-data/getbar.c new file mode 100644 index 000000000..020d7ad28 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/flat-data/getbar.c @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ + +extern int bar; + +int* getbar() +{ + return &bar; +} + diff --git a/src/dyld-apple/unit-tests/test-cases/flat-data/main.c b/src/dyld-apple/unit-tests/test-cases/flat-data/main.c new file mode 100644 index 000000000..fc85a6e58 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/flat-data/main.c @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +// getbar() is implemented in libbar.dylib which has its own bar +// libbar.dylib is built flat-namespace so it should use the bar from main +// libbar.dylib is built multi_module so that the static linker won't complain +// about two bar's. + +extern int* getbar(); +int bar = 1; + +int main() +{ + if ( getbar() != &bar ) + FAIL("flat-data found wrong bar"); + else + PASS("flat-data"); + + return EXIT_SUCCESS; +} + diff --git a/src/dyld-apple/unit-tests/test-cases/flat-insert/foo.c b/src/dyld-apple/unit-tests/test-cases/flat-insert/foo.c new file mode 100644 index 000000000..2d68edf3b --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/flat-insert/foo.c @@ -0,0 +1,30 @@ +/* + * 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@ + */ +#include + + +int foo() +{ + return 42; +} + diff --git a/src/dyld-apple/unit-tests/test-cases/flat-insert/main.c b/src/dyld-apple/unit-tests/test-cases/flat-insert/main.c new file mode 100644 index 000000000..14cb600fc --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/flat-insert/main.c @@ -0,0 +1,44 @@ +/* + * 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +extern int foo(); + +int main(int argc, const char* argv[]) +{ + if ( foo() == 42 ) + PASS("flat-insert"); + else + FAIL("flat-insert"); + return EXIT_SUCCESS; +} + +// inserted library has another copy of foo() that should +// override this one and return 42 +int foo() +{ + return 0; +} diff --git a/src/dyld-apple/unit-tests/test-cases/flat-prebound/bar.c b/src/dyld-apple/unit-tests/test-cases/flat-prebound/bar.c new file mode 100644 index 000000000..e12bfce1e --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/flat-prebound/bar.c @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ + +#include + +int bar() +{ + return 1; +} + +int barVar = 1; +int barVar2 = 1; diff --git a/src/dyld-apple/unit-tests/test-cases/flat-prebound/foo.c b/src/dyld-apple/unit-tests/test-cases/flat-prebound/foo.c new file mode 100644 index 000000000..f47a91afe --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/flat-prebound/foo.c @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include + +extern int bar(); +extern int barVar; +extern int barVar2; + +int* myBarVar = &barVar; + + +bool foo() +{ + // test non-lazy pointer + if ( barVar2 != 0 ) + return false; + + // test external relocation + if ( *myBarVar != 0 ) + return false; + + // test lazy pointer + if ( bar() != 0 ) + return false; + + return true; +} diff --git a/src/dyld-apple/unit-tests/test-cases/flat-prebound/main.c b/src/dyld-apple/unit-tests/test-cases/flat-prebound/main.c new file mode 100644 index 000000000..54862f4ac --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/flat-prebound/main.c @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +// foo() internally calls bar() +// libfoo.dylib is build flat and prebound to libbar.dylib +// but the bar in this main executable should override the prebound bar +extern bool foo(); + +int main() +{ + if ( foo() ) + PASS("flat-prebound"); + else + FAIL("flat-prebound found wrong bar"); + + return EXIT_SUCCESS; +} + + +int bar() +{ + return 0; +} +int barVar = 0; +int barVar2 = 0; + diff --git a/src/dyld-apple/unit-tests/test-cases/flat-private-extern/bar.c b/src/dyld-apple/unit-tests/test-cases/flat-private-extern/bar.c new file mode 100644 index 000000000..0b13569c0 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/flat-private-extern/bar.c @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2006 Apple Computer, 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@ + */ + +int bar() __attribute__((visibility("hidden"))); + +int bar() +{ + return 1; +} diff --git a/src/dyld-apple/unit-tests/test-cases/flat-private-extern/foo.c b/src/dyld-apple/unit-tests/test-cases/flat-private-extern/foo.c new file mode 100644 index 000000000..ed6fc9b09 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/flat-private-extern/foo.c @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2006 Apple Computer, 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@ + */ + +extern int bar(); + +int foo() +{ + return bar(); +} diff --git a/src/dyld-apple/unit-tests/test-cases/flat-private-extern/main.c b/src/dyld-apple/unit-tests/test-cases/flat-private-extern/main.c new file mode 100644 index 000000000..6505066ac --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/flat-private-extern/main.c @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +// foo() internally calls bar() +// libfoobar.dylib is build flat with bar() that is private_extern +// The bar() in the main executable should not override the one in libfoobar + +extern int foo(); + +int main() +{ + if ( foo() == 0 ) + FAIL("flat-private-extern found wrong bar"); + else + PASS("flat-private-extern"); + + return EXIT_SUCCESS; +} + +int bar() +{ + return 0; +} diff --git a/src/dyld-apple/unit-tests/test-cases/framework-DYLD_LIBRARY_PATH/foo.c b/src/dyld-apple/unit-tests/test-cases/framework-DYLD_LIBRARY_PATH/foo.c new file mode 100644 index 000000000..01c576d96 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/framework-DYLD_LIBRARY_PATH/foo.c @@ -0,0 +1,5 @@ + +int foo() +{ + return RESULT; +} diff --git a/src/dyld-apple/unit-tests/test-cases/framework-DYLD_LIBRARY_PATH/main.c b/src/dyld-apple/unit-tests/test-cases/framework-DYLD_LIBRARY_PATH/main.c new file mode 100644 index 000000000..7e6502e58 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/framework-DYLD_LIBRARY_PATH/main.c @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2013 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include // for atoi() + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + + +extern int foo(); + +int main(int argc, const char* argv[]) +{ + int expectedResult = atoi(argv[1]); + int actualResult = foo(); + //fprintf(stderr, "foo() returned %d, expected %d\n", actualResult, expectedResult); + if ( actualResult != expectedResult ) + FAIL("framework-DYLD_LIBRARY_PATH using wrong dylib. foo() returned %d, expected %d", actualResult, expectedResult); + else + PASS("framework-DYLD_LIBRARY_PATH"); + + return EXIT_SUCCESS; +} + diff --git a/src/dyld-apple/unit-tests/test-cases/framework-fallback/main.c b/src/dyld-apple/unit-tests/test-cases/framework-fallback/main.c new file mode 100644 index 000000000..fdaa89366 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/framework-fallback/main.c @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include +#include + +#include "test.h" + +/// +/// rdar://problem/3736945 +/// +/// Test that a std framework can be dynamically loaded via the fallback paths +/// +/// + + + +int +main(int argc, const char* argv[]) +{ +#if __MAC_OS_X_VERSION_MIN_REQUIRED + const struct mach_header *image; + + image = NSAddImage("AppKit.framework/AppKit", + NSADDIMAGE_OPTION_RETURN_ON_ERROR | NSADDIMAGE_OPTION_WITH_SEARCHING); + if ( image == NULL ) + FAIL("Could not load AppKit"); + else +#endif + PASS("AppKit loaded"); + + return 0; +} + diff --git a/src/dyld-apple/unit-tests/test-cases/ignore-bad-files/foo.c b/src/dyld-apple/unit-tests/test-cases/ignore-bad-files/foo.c new file mode 100644 index 000000000..4315d22ba --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/ignore-bad-files/foo.c @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +void foo() {} diff --git a/src/dyld-apple/unit-tests/test-cases/ignore-bad-files/main.c b/src/dyld-apple/unit-tests/test-cases/ignore-bad-files/main.c new file mode 100644 index 000000000..72d7abef6 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/ignore-bad-files/main.c @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +extern void foo(); + +int +main(int argc, const char* argv[]) +{ + foo(); + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/image-count/foo.c b/src/dyld-apple/unit-tests/test-cases/image-count/foo.c new file mode 100644 index 000000000..c1f5255ba --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/image-count/foo.c @@ -0,0 +1,2 @@ +void foo() {} + diff --git a/src/dyld-apple/unit-tests/test-cases/image-count/main.c b/src/dyld-apple/unit-tests/test-cases/image-count/main.c new file mode 100644 index 000000000..06740b9d8 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/image-count/main.c @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2010 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@ + */ +#include // EXIT_SUCCESS +#include +#include +#include +#include + +#include "test.h" + +extern struct mach_header __dso_handle; + + +struct dyld_all_image_infos* getImageInfosFromKernel() +{ + task_dyld_info_data_t task_dyld_info; + mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT; + + if ( task_info(mach_task_self(), TASK_DYLD_INFO, (task_info_t)&task_dyld_info, &count) ) { + FAIL("all_image_infos: task_info() failed"); + exit(0); + } + return (struct dyld_all_image_infos*)(uintptr_t)task_dyld_info.all_image_info_addr; +} + + +int +main() +{ + struct dyld_all_image_infos* info = getImageInfosFromKernel(); + if ( info->version < 10 ) { + FAIL("image-count: dyld_all_image_infos is < 10"); + exit(0); + } + + if ( info->infoArrayCount != info->initialImageCount ) { + FAIL("image-count: dyld_all_image_infos.infoArrayCount != dyld_all_image_infos.initialImageCount"); + exit(0); + } + + void* h = dlopen("libfoo.dylib", RTLD_LAZY); + if ( h == NULL ) { + FAIL("image-count: dyld_all_image_infos is < 10"); + exit(0); + } + + if ( info->infoArrayCount != (info->initialImageCount+1) ) { + FAIL("image-count: infoArrayCount did not increment when libfoo.dylib was loaded"); + exit(0); + } + + dlclose(h); + + if ( info->infoArrayCount != info->initialImageCount ) { + FAIL("image-count: infoArrayCount did not decrement when libfoo.dylib was unloaded"); + exit(0); + } + + PASS("image-count"); + return EXIT_SUCCESS; +} + + diff --git a/src/dyld-apple/unit-tests/test-cases/image-remove-crash/foo.c b/src/dyld-apple/unit-tests/test-cases/image-remove-crash/foo.c new file mode 100644 index 000000000..6924ac69d --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/image-remove-crash/foo.c @@ -0,0 +1,3 @@ + +void foo() {} + diff --git a/src/dyld-apple/unit-tests/test-cases/image-remove-crash/main.c b/src/dyld-apple/unit-tests/test-cases/image-remove-crash/main.c new file mode 100644 index 000000000..612671f36 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/image-remove-crash/main.c @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2013 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +static bool sNotified = false; + +static void removeNotification2(const struct mach_header *mh, intptr_t vmaddr_slide) +{ +} + +static void removeNotification(const struct mach_header *mh, intptr_t vmaddr_slide) +{ + // crash in dyld possible if remove callbacks added within a remove callback + _dyld_register_func_for_remove_image(removeNotification2); + _dyld_register_func_for_remove_image(removeNotification2); + _dyld_register_func_for_remove_image(removeNotification2); + _dyld_register_func_for_remove_image(removeNotification2); + _dyld_register_func_for_remove_image(removeNotification2); + + sNotified = true; +} + + +int main(int argc, const char* argv[]) +{ + // tell dyld we want to know when images go away + _dyld_register_func_for_remove_image(removeNotification); + + void* handle = dlopen("foo.bundle", RTLD_LAZY); + if ( handle == NULL ) { + FAIL("image-remove-crash: dlopen(foo.bundle) failed: %s", dlerror()); + exit(0); + } + + int result = dlclose(handle); + if ( result != 0 ) { + FAIL("image-remove-crash: dlclose(handle) returned %d, %s", result, dlerror()); + exit(0); + } + + if ( sNotified ) + PASS("image-remove-crash"); + else + FAIL("image-remove-crash"); + + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/image-remove-notification/foo.c b/src/dyld-apple/unit-tests/test-cases/image-remove-notification/foo.c new file mode 100644 index 000000000..6924ac69d --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/image-remove-notification/foo.c @@ -0,0 +1,3 @@ + +void foo() {} + diff --git a/src/dyld-apple/unit-tests/test-cases/image-remove-notification/main.c b/src/dyld-apple/unit-tests/test-cases/image-remove-notification/main.c new file mode 100644 index 000000000..d7e0b4a01 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/image-remove-notification/main.c @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +static void* sExpectedBundleBaseAddress = 0; +static bool sNotified = false; + +static void removeNotification(const struct mach_header *mh, intptr_t vmaddr_slide) +{ + if ( mh == sExpectedBundleBaseAddress ) + sNotified = true; +} + + + +int main(int argc, const char* argv[]) +{ + // tell dyld we want to know when images go away + _dyld_register_func_for_remove_image(removeNotification); + + void* handle = dlopen("foo.bundle", RTLD_LAZY); + if ( handle == NULL ) { + FAIL("image-remove-notification: dlopen(foo.bundle) failed: %s", dlerror()); + exit(0); + } + + void* sym = dlsym(handle, "foo"); + if ( sym == NULL ) { + FAIL("image-remove-notification: dlsym(handle, \"foo\") failed: %s", dlerror()); + exit(0); + } + + Dl_info info; + if ( dladdr(sym, &info) == 0 ) { + FAIL("image-remove-notification: dladdr() failed"); + exit(0); + } + + // record the base address we expect to get notification about + sExpectedBundleBaseAddress = info.dli_fbase; + + int result = dlclose(handle); + if ( result != 0 ) { + FAIL("image-remove-notification: dlclose(handle) returned %d, %s", result, dlerror()); + exit(0); + } + + if ( sNotified ) + PASS("image-remove-notification"); + else + FAIL("image-remove-notification"); + + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/image-slide/foo.c b/src/dyld-apple/unit-tests/test-cases/image-slide/foo.c new file mode 100644 index 000000000..c1f5255ba --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/image-slide/foo.c @@ -0,0 +1,2 @@ +void foo() {} + diff --git a/src/dyld-apple/unit-tests/test-cases/image-slide/main.c b/src/dyld-apple/unit-tests/test-cases/image-slide/main.c new file mode 100644 index 000000000..f4fb3189a --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/image-slide/main.c @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2008 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@ + */ +#include // EXIT_SUCCESS +#include +#include +#include + +#include "test.h" + +extern void foo(); + +intptr_t findSlide(const struct mach_header* mh) +{ + int count = _dyld_image_count(); + for(int i=0; i < count; ++i) { + if ( mh == _dyld_get_image_header(i) ) { + return _dyld_get_image_vmaddr_slide(i); + } + } + return -1; +} + + +int +main() +{ + // find mach_header for libfoo.dylib + const struct mach_header* mh; + Dl_info info; + if ( dladdr(&foo, &info) ) { + mh = info.dli_fbase; + } + else { + FAIL("dladdr() could not find foo()"); + exit(0); + } + + + intptr_t slide1 = _dyld_get_image_slide(mh); + intptr_t slide2 = findSlide(mh); + if ( slide1 == slide2 ) + PASS("image-slide"); + else + FAIL("image-slide: 0x%lX != 0x%lX", slide1, slide2); + + return EXIT_SUCCESS; +} + + diff --git a/src/dyld-apple/unit-tests/test-cases/image-state-change/bar.c b/src/dyld-apple/unit-tests/test-cases/image-state-change/bar.c new file mode 100644 index 000000000..63c34e06c --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/image-state-change/bar.c @@ -0,0 +1,2 @@ + +int bar = 10; diff --git a/src/dyld-apple/unit-tests/test-cases/image-state-change/foo.c b/src/dyld-apple/unit-tests/test-cases/image-state-change/foo.c new file mode 100644 index 000000000..6924ac69d --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/image-state-change/foo.c @@ -0,0 +1,3 @@ + +void foo() {} + diff --git a/src/dyld-apple/unit-tests/test-cases/image-state-change/main.c b/src/dyld-apple/unit-tests/test-cases/image-state-change/main.c new file mode 100644 index 000000000..3b636aab5 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/image-state-change/main.c @@ -0,0 +1,170 @@ +/* + * Copyright (c) 2006 Apple Computer, 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@ + */ +#include // fprintf(), NULL +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + + +static int singleMappedCount = 0; +static int batchMappedCount = 0; +static int singleUnMappedCount = 0; +static int batchBoundCount = 0; +static int singleDepsInitedCount = 0; +static int singleBoundCount = 0; + +static const char* batchMappedHandler(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info info[]) +{ + for (uint32_t i=0; i < infoCount; ++i) + printf("batchMappedHandler(): %u/%u -> %s\n", i, infoCount, info[i].imageFilePath); + if ( state != dyld_image_state_dependents_mapped ) { + FAIL("image-state-change: batchMappedHandler passed state %d", state); + exit(0); + } + batchMappedCount += infoCount; + return NULL; +} + +static const char* batchBoundHandler(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info info[]) +{ + for (uint32_t i=0; i < infoCount; ++i) + printf("batchBoundHandler(): %u/%u -> %s\n", i, infoCount, info[i].imageFilePath); + if ( state != dyld_image_state_bound ) { + FAIL("image-state-change: batchBoundHandler passed state %d", state); + exit(0); + } + batchBoundCount += infoCount; + return NULL; +} + +static const char* singleMappedHandler(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info info[]) +{ + printf("singleMappedHandler(%s)\n", info[0].imageFilePath); + if ( state != dyld_image_state_mapped ) { + FAIL("image-state-change: singleMappedHandler passed state %d", state); + exit(0); + } + if ( infoCount != 1 ) { + FAIL("image-state-change: singleMappedHandler given %d images", infoCount); + exit(0); + } + ++singleMappedCount; + return NULL; +} + + +static const char* singleBoundHandler(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info info[]) +{ + printf("singleBoundHandler(%s)\n", info[0].imageFilePath); + if ( state != dyld_image_state_bound ) { + FAIL("image-state-change: singleBoundHandler passed state %d", state); + exit(0); + } + if ( infoCount != 1 ) { + FAIL("image-state-change: singleBoundHandler given %d images", infoCount); + exit(0); + } + ++singleBoundCount; + return NULL; +} + + + +static const char* singleDepsInitedHandler(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info info[]) +{ + printf("singleDepsInitedHandler(%s)\n", info[0].imageFilePath); + if ( state != dyld_image_state_dependents_initialized ) { + FAIL("image-state-change: singleDepsInitedHandler passed state %d", state); + exit(0); + } + if ( infoCount != 1 ) { + FAIL("image-state-change: singleDepsInitedHandler given %d images", infoCount); + exit(0); + } + ++singleDepsInitedCount; + return NULL; +} + + + +static const char* singleUnmappedHandler(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info info[]) +{ + printf("singleUnmappedHandler(%s)\n", info[0].imageFilePath); + if ( state != dyld_image_state_terminated ) { + FAIL("image-state-change: singleUnmappedHandler passed state %d", state); + exit(0); + } + if ( infoCount != 1 ) { + FAIL("image-state-change: singleUnmappedHandler given %d images", infoCount); + exit(0); + } + ++singleUnMappedCount; + return NULL; +} + +static void loadAndUnLoad() +{ + void* handle = dlopen("foo.bundle", RTLD_LAZY); + if ( handle == NULL ) { + FAIL("image-state-change: dlopen(foo.bundle) failed: %s", dlerror()); + exit(0); + } + + int result = dlclose(handle); + if ( result != 0 ) { + FAIL("image-state-change: dlclose(handle) returned %d, %s", result, dlerror()); + exit(0); + } +} + + +int main(int argc, const char* argv[]) +{ + // tell dyld we want to know when images are mapped + dyld_register_image_state_change_handler(dyld_image_state_dependents_mapped, true, batchMappedHandler); + dyld_register_image_state_change_handler(dyld_image_state_mapped, false, singleMappedHandler); + dyld_register_image_state_change_handler(dyld_image_state_bound, true, batchBoundHandler); + dyld_register_image_state_change_handler(dyld_image_state_bound, false, singleBoundHandler); + dyld_register_image_state_change_handler(dyld_image_state_dependents_initialized, false, singleDepsInitedHandler); + dyld_register_image_state_change_handler(dyld_image_state_terminated, false, singleUnmappedHandler); + + // with batch mode we get notified of existing images, but not with single mode, so re-sync counts + batchMappedCount=0; + + loadAndUnLoad(); + + loadAndUnLoad(); + + if ( (singleMappedCount == batchMappedCount) && (singleMappedCount == singleUnMappedCount) ) + PASS("image-state-change"); + else + FAIL("image-state-change: batch count=%d, single count=%d", batchMappedCount, singleMappedCount); + + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/image-state-deny-OFI/bar.c b/src/dyld-apple/unit-tests/test-cases/image-state-deny-OFI/bar.c new file mode 100644 index 000000000..63c34e06c --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/image-state-deny-OFI/bar.c @@ -0,0 +1,2 @@ + +int bar = 10; diff --git a/src/dyld-apple/unit-tests/test-cases/image-state-deny-OFI/foo.c b/src/dyld-apple/unit-tests/test-cases/image-state-deny-OFI/foo.c new file mode 100644 index 000000000..6924ac69d --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/image-state-deny-OFI/foo.c @@ -0,0 +1,3 @@ + +void foo() {} + diff --git a/src/dyld-apple/unit-tests/test-cases/image-state-deny-OFI/main.c b/src/dyld-apple/unit-tests/test-cases/image-state-deny-OFI/main.c new file mode 100644 index 000000000..37eb8d7a8 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/image-state-deny-OFI/main.c @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include // fprintf(), NULL +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +#if __MAC_OS_X_VERSION_MIN_REQUIRED + +static bool doneRegistering = false; + +// +// By returning a string, we prevent that image from loading. +// We just prevent any image with "bar" in its name from loading. +// + +static const char* batchMappedHandler(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info info[]) +{ + for (uint32_t i=0; i < infoCount; ++i) { + //fprintf(stderr, "batch mapped: %d %s\n", i, info[i].imageFilePath); + if ( strstr(info[i].imageFilePath, "bar") != NULL ) + return "can't load bar"; + } + return NULL; +} + +static const char* singleMappedHandler(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info info[]) +{ + //fprintf(stderr, "single mapped: %s\n", info[0].imageFilePath); + if ( strstr(info[0].imageFilePath, "bar") != NULL ) + return "can't load bar"; + return NULL; +} + + + +static const char* singleInitializedHandler(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info info[]) +{ + //fprintf(stderr, "singleInitializedHandler(%s)\n", info[0].imageFilePath); + if ( doneRegistering ) { + FAIL("image-state-deny something loaded"); + exit(0); + } + return NULL; +} + +//static const char* batchBoundHandler(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info info[]) +//{ +// //for (uint32_t i=0; i < infoCount; ++i) +// // fprintf(stderr, "bound: %u %s\n", i, info[i].imageFilePath); +// return NULL; +//} + +static void load(const char* name) +{ + NSObjectFileImage ofi; + if ( NSCreateObjectFileImageFromFile(name, &ofi) == NSObjectFileImageSuccess ) { + NSModule mod = NSLinkModule(ofi, name, NSLINKMODULE_OPTION_PRIVATE | NSLINKMODULE_OPTION_RETURN_ON_ERROR); + if ( mod != NULL ) { + FAIL("neither NSCreateObjectFileImageFromFile nor NSLinkModule failed but should have"); + if ( !NSDestroyObjectFileImage(ofi) ) { + FAIL("NSDestroyObjectFileImage failed"); + } + exit(0); + } + else { + NSDestroyObjectFileImage(ofi); + } + } +} +#endif + + +int main(int argc, const char* argv[]) +{ +// NSCreateObjectFileImageFromMemory is only available on Mac OS X - not iPhone OS +#if __MAC_OS_X_VERSION_MIN_REQUIRED + // tell dyld we want to know when images successfully loaded + dyld_register_image_state_change_handler(dyld_image_state_initialized, false, singleInitializedHandler); + doneRegistering = true; + + // tell dyld we want to know when images successfully loaded + //dyld_register_image_state_change_handler(dyld_image_state_bound, true, batchBoundHandler); + + // tell dyld we want to know when images are mapped + if ( argc > 1 ) + dyld_register_image_state_change_handler(dyld_image_state_dependents_mapped, true, batchMappedHandler); + else + dyld_register_image_state_change_handler(dyld_image_state_mapped, false, singleMappedHandler); + + NSObjectFileImage ofi; + if ( NSCreateObjectFileImageFromFile("foo2.bundle", &ofi) != NSObjectFileImageSuccess ) { + FAIL("NSCreateObjectFileImageFromFile failed for foo2.bundle"); + exit(0); + } + + load("foo.bundle"); + + load("bar.bundle"); + + if ( !NSDestroyObjectFileImage(ofi) ) { + FAIL("NSDestroyObjectFileImage failed for foo2.bundle"); + exit(0); + } + +// dlopen("/usr/lib/libz.1.2.3.dylib", RTLD_LAZY); +#endif + + PASS("image-state-deny"); + + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/image-state-deny-all_image_infos/bar.c b/src/dyld-apple/unit-tests/test-cases/image-state-deny-all_image_infos/bar.c new file mode 100644 index 000000000..e42599986 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/image-state-deny-all_image_infos/bar.c @@ -0,0 +1 @@ +void bar() {} diff --git a/src/dyld-apple/unit-tests/test-cases/image-state-deny-all_image_infos/main.c b/src/dyld-apple/unit-tests/test-cases/image-state-deny-all_image_infos/main.c new file mode 100644 index 000000000..ed4b56c3c --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/image-state-deny-all_image_infos/main.c @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include // fprintf(), NULL +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include +#include +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +// +// By returning a string, we prevent that image from loading. +// We just prevent any image with "bar" in its name from loading. +// + + +static const char* singleMappedHandler(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info info[]) +{ + //fprintf(stderr, "single mapped: %s\n", info[0].imageFilePath); + if ( strstr(info[0].imageFilePath, "bar") != NULL ) + return "can't load bar"; + return NULL; +} + + +static void load(const char* name) +{ + void* handle = dlopen(name, RTLD_LAZY); + if ( handle != NULL ) { + FAIL("image-state-deny-all_image_infos: dlopen(%s) should have failed", name); + exit(0); + } +} + + +int main(int argc, const char* argv[]) +{ + // tell dyld we want to know when images are mapped + dyld_register_image_state_change_handler(dyld_image_state_mapped, false, singleMappedHandler); + + const struct dyld_all_image_infos* infos = _dyld_get_all_image_infos(); + const uint32_t initialCount = infos->infoArrayCount; + + load("bar.bundle"); + + if ( infos->infoArrayCount != initialCount ){ + FAIL("image-state-deny-all_image_infos: infoArrayCount should not have changed"); + exit(0); + } + + //for (int i=0; i < infos->infoArrayCount; ++i) + // printf("mh=%p, path=%s\n", infos->infoArray[i].imageLoadAddress, infos->infoArray[i].imageFilePath); + + PASS("image-state-deny-all_image_infos"); + + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/image-state-deny-cache-leak/bar.c b/src/dyld-apple/unit-tests/test-cases/image-state-deny-cache-leak/bar.c new file mode 100644 index 000000000..30ade8f7b --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/image-state-deny-cache-leak/bar.c @@ -0,0 +1,2 @@ + +void bar() { } diff --git a/src/dyld-apple/unit-tests/test-cases/image-state-deny-cache-leak/foo.c b/src/dyld-apple/unit-tests/test-cases/image-state-deny-cache-leak/foo.c new file mode 100644 index 000000000..6924ac69d --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/image-state-deny-cache-leak/foo.c @@ -0,0 +1,3 @@ + +void foo() {} + diff --git a/src/dyld-apple/unit-tests/test-cases/image-state-deny-cache-leak/main.c b/src/dyld-apple/unit-tests/test-cases/image-state-deny-cache-leak/main.c new file mode 100644 index 000000000..edcb894b6 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/image-state-deny-cache-leak/main.c @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2014 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@ + */ +#include // fprintf(), NULL +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +// +// By returning a string, we prevent that image from loading. +// We just prevent any image with "libz" in its name from loading. +// +static const char* singleMappedHandler(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info info[]) +{ + //fprintf(stderr, "single mapped: %s\n", info[0].imageFilePath); + if ( strstr(info[0].imageFilePath, "libz") != NULL ) + return "can't load libz"; + return NULL; +} + + +int main(int argc, const char* argv[]) +{ + // tell dyld we want to know when images are mapped + dyld_register_image_state_change_handler(dyld_image_state_mapped, false, singleMappedHandler); + + void* handle = dlopen("libfoo1.dylib", RTLD_LAZY); + if ( handle != NULL ) { + FAIL("image-state-deny-cache-leak: dlopen(libfoo.dylib) should have failed"); + return EXIT_SUCCESS; + } + + handle = dlopen("libfoo2.dylib", RTLD_LAZY); + if ( handle == NULL ) { + FAIL("image-state-deny-cache-leak: dlopen(libfoo.dylib) should have succeeded"); + return EXIT_SUCCESS; + } + dlclose(handle); + + // execute leaks command on myself + char cmd[512]; + sprintf(cmd, "sudo leaks %u > /dev/null\n", getpid()); + int result = system(cmd); + if ( result == EXIT_SUCCESS ) + PASS("image-state-deny-cache-leak"); + else + FAIL("image-state-deny-cache-leak"); + + + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/image-state-deny-dlclose/base.c b/src/dyld-apple/unit-tests/test-cases/image-state-deny-dlclose/base.c new file mode 100644 index 000000000..842e2e24a --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/image-state-deny-dlclose/base.c @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2006 Apple Computer, 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@ + */ +#include // fprintf(), NULL +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include +#include +#include + + +static const char* batchMappedHandler(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info info[]) +{ + //for (uint32_t i=0; i < infoCount; ++i) { + // fprintf(stderr, "batch mapped: %d %s\n", i, info[i].imageFilePath); + //} + return NULL; +} + +static void myregister() __attribute__((constructor)); + +static void myregister() +{ + // tell dyld we want to know when images successfully loaded + dyld_register_image_state_change_handler(dyld_image_state_dependents_mapped, true, batchMappedHandler); +} diff --git a/src/dyld-apple/unit-tests/test-cases/image-state-deny-dlclose/foo.c b/src/dyld-apple/unit-tests/test-cases/image-state-deny-dlclose/foo.c new file mode 100644 index 000000000..6924ac69d --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/image-state-deny-dlclose/foo.c @@ -0,0 +1,3 @@ + +void foo() {} + diff --git a/src/dyld-apple/unit-tests/test-cases/image-state-deny-dlclose/main.c b/src/dyld-apple/unit-tests/test-cases/image-state-deny-dlclose/main.c new file mode 100644 index 000000000..e4756bbe7 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/image-state-deny-dlclose/main.c @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include // fprintf(), NULL +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +int main(int argc, const char* argv[]) +{ + // open and close base + void* h1 = dlopen("libbase.dylib", RTLD_LAZY); + if ( h1 == NULL ) { + FAIL("image-state-deny-dlclose: can't dlopen libbase.dylib: %s", dlerror()); + return EXIT_SUCCESS; + } + dlclose(h1); + + // if base was unloaded, then this dlopen will jump to neverland + void* h2 = dlopen("libfoo.dylib", RTLD_LAZY); + if ( h2 == NULL ) { + FAIL("image-state-deny-dlclose: can't dlopen libfoo.dylib: %s", dlerror()); + return EXIT_SUCCESS; + } + + void* h3 = dlopen("libbase.dylib", RTLD_LAZY); + if ( h3 == NULL ) { + FAIL("image-state-deny-dlclose: can't dlopen libbase.dylib: %s", dlerror()); + return EXIT_SUCCESS; + } + dlclose(h3); + + dlclose(h2); + + + PASS("image-state-deny-dlclose"); + + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/image-state-deny-leak/bar.c b/src/dyld-apple/unit-tests/test-cases/image-state-deny-leak/bar.c new file mode 100644 index 000000000..30ade8f7b --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/image-state-deny-leak/bar.c @@ -0,0 +1,2 @@ + +void bar() { } diff --git a/src/dyld-apple/unit-tests/test-cases/image-state-deny-leak/foo.c b/src/dyld-apple/unit-tests/test-cases/image-state-deny-leak/foo.c new file mode 100644 index 000000000..6924ac69d --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/image-state-deny-leak/foo.c @@ -0,0 +1,3 @@ + +void foo() {} + diff --git a/src/dyld-apple/unit-tests/test-cases/image-state-deny-leak/main.c b/src/dyld-apple/unit-tests/test-cases/image-state-deny-leak/main.c new file mode 100644 index 000000000..42e369ec5 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/image-state-deny-leak/main.c @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include // fprintf(), NULL +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +// +// By returning a string, we prevent that image from loading. +// We just prevent any image with "bar" in its name from loading. +// + +const char* sBlockName = NULL; + +static const char* singleMappedHandler(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info info[]) +{ + //fprintf(stderr, "single mapped: %s\n", info[0].imageFilePath); + if ( strstr(info[0].imageFilePath, sBlockName) != NULL ) + return "can't load blocked name"; + return NULL; +} + + +int main(int argc, const char* argv[]) +{ + // tell dyld we want to know when images are mapped + dyld_register_image_state_change_handler(dyld_image_state_mapped, false, singleMappedHandler); + + sBlockName = "libbar1"; + void* handle = dlopen("libfoo.dylib", RTLD_LAZY); + if ( handle != NULL ) { + FAIL("image-state-deny-leak: dlopen(libfoo.dylib) should have failed"); + return EXIT_SUCCESS; + } + + sBlockName = "libbar2"; + handle = dlopen("libfoo.dylib", RTLD_LAZY); + if ( handle == NULL ) { + FAIL("image-state-deny-leak: dlopen(libfoo.dylib) should have succeeded"); + return EXIT_SUCCESS; + } + + // execute leaks command on myself + char cmd[512]; + sprintf(cmd, "sudo leaks %u > /dev/null \n", getpid()); + int result = system(cmd); + if ( result == EXIT_SUCCESS ) + PASS("image-state-deny-leak"); + else + FAIL("image-state-deny-leak"); + + + + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/image-state-deny/bar.c b/src/dyld-apple/unit-tests/test-cases/image-state-deny/bar.c new file mode 100644 index 000000000..63c34e06c --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/image-state-deny/bar.c @@ -0,0 +1,2 @@ + +int bar = 10; diff --git a/src/dyld-apple/unit-tests/test-cases/image-state-deny/foo.c b/src/dyld-apple/unit-tests/test-cases/image-state-deny/foo.c new file mode 100644 index 000000000..6924ac69d --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/image-state-deny/foo.c @@ -0,0 +1,3 @@ + +void foo() {} + diff --git a/src/dyld-apple/unit-tests/test-cases/image-state-deny/main.c b/src/dyld-apple/unit-tests/test-cases/image-state-deny/main.c new file mode 100644 index 000000000..df8f1d993 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/image-state-deny/main.c @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include // fprintf(), NULL +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +static bool doneRegistering = false; + + +// +// By returning a string, we prevent that image from loading. +// We just prevent any image with "bar" in its name from loading. +// + +static const char* batchMappedHandler(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info info[]) +{ + for (uint32_t i=0; i < infoCount; ++i) { + //fprintf(stderr, "batch mapped: %d %s\n", i, info[i].imageFilePath); + if ( strstr(info[i].imageFilePath, "bar") != NULL ) + return "can't load bar"; + } + return NULL; +} + +static const char* singleMappedHandler(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info info[]) +{ + //fprintf(stderr, "single mapped: %s\n", info[0].imageFilePath); + if ( strstr(info[0].imageFilePath, "bar") != NULL ) + return "can't load bar"; + return NULL; +} + + + +static const char* singleInitializedHandler(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info info[]) +{ + //fprintf(stderr, "singleInitializedHandler(%s)\n", info[0].imageFilePath); + if ( doneRegistering ) { + FAIL("image-state-deny something loaded"); + exit(0); + } + return NULL; +} + +//static const char* batchBoundHandler(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info info[]) +//{ +// //for (uint32_t i=0; i < infoCount; ++i) +// // fprintf(stderr, "bound: %u %s\n", i, info[i].imageFilePath); +// return NULL; +//} + +static void load(const char* name) +{ + void* handle = dlopen(name, RTLD_LAZY); + if ( handle != NULL ) { + FAIL("image-state-deny: dlopen(%s) should have failed", name); + exit(0); + } +} + + +int main(int argc, const char* argv[]) +{ + // tell dyld we want to know when images successfully loaded + dyld_register_image_state_change_handler(dyld_image_state_initialized, false, singleInitializedHandler); + doneRegistering = true; + + // tell dyld we want to know when images successfully loaded + //dyld_register_image_state_change_handler(dyld_image_state_bound, true, batchBoundHandler); + + // tell dyld we want to know when images are mapped + if ( argc > 1 ) + dyld_register_image_state_change_handler(dyld_image_state_dependents_mapped, true, batchMappedHandler); + else + dyld_register_image_state_change_handler(dyld_image_state_mapped, false, singleMappedHandler); + + load("foo.bundle"); + + load("bar.bundle"); + + //dlopen("/usr/lib/libz.1.2.3.dylib", RTLD_LAZY); + + PASS("image-state-deny"); + + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/image-state-dependents-initialized/bar.c b/src/dyld-apple/unit-tests/test-cases/image-state-dependents-initialized/bar.c new file mode 100644 index 000000000..3b6c32395 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/image-state-dependents-initialized/bar.c @@ -0,0 +1,119 @@ +/* + * 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@ + */ + +#include // fprintf(), NULL +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + + +static enum { start, barDeps, bar, fooDeps, foo, mainDeps, main } step = start; + + +static const char* depInitHandler(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info info[]) +{ + if ( infoCount != 1 ) { + FAIL("image-state-dependents-initialized: depInitHandler infoCount != 1\n"); + exit(0); + } + + //fprintf(stderr, "depInitHandler(%s), state=%d\n", info[0].imageFilePath, step); + + if ( (step == start) && (strstr(info[0].imageFilePath, "libbar.dylib") != NULL) ) + step = barDeps; + else if ( (step == bar) && (strstr(info[0].imageFilePath, "libfoo.dylib") != NULL) ) + step = fooDeps; + else if ( (step == foo) && (strstr(info[0].imageFilePath, "/main") != NULL) ) + step = mainDeps; + else { + fprintf(stderr, "out of order depInitHandler\n"); + exit(0); + } + + + return NULL; +} + +static const char* initHandler(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info info[]) +{ + if ( infoCount != 1 ) { + FAIL("image-state-dependents-initialized: initHandler infoCount != 1\n"); + exit(0); + } + + //fprintf(stderr, "initHandler(%s), state=%d\n", info[0].imageFilePath, step); + bool isBar = (strstr(info[0].imageFilePath, "libbar.dylib") != NULL); + bool isFoo = (strstr(info[0].imageFilePath, "libfoo.dylib") != NULL); + bool isMain= (strstr(info[0].imageFilePath, "/main") != NULL); + + if ( isBar ) { + if ( step == barDeps ) { + step = bar; + } + else { + fprintf(stderr, "out of order initHandler bar\n"); + exit(0); + } + } + else if ( isFoo ) { + if ( step == fooDeps ) { + step = foo; + } + else { + fprintf(stderr, "out of order initHandler foo\n"); + exit(0); + } + } + else if ( isMain ) { + if ( step == mainDeps ) { + step = main; + } + else { + fprintf(stderr, "out of order initHandler main\n"); + exit(0); + } + } + + + return NULL; +} + + + + +void __attribute__((constructor)) setup_bar() +{ + // tell dyld we want to know when images are mapped + dyld_register_image_state_change_handler(dyld_image_state_dependents_initialized, false, depInitHandler); + dyld_register_image_state_change_handler(dyld_image_state_initialized, false, initHandler); +} + + + diff --git a/src/dyld-apple/unit-tests/test-cases/image-state-dependents-initialized/foo.c b/src/dyld-apple/unit-tests/test-cases/image-state-dependents-initialized/foo.c new file mode 100644 index 000000000..9584c6e03 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/image-state-dependents-initialized/foo.c @@ -0,0 +1,11 @@ + + +int foo_value = 1; + +void __attribute__((constructor)) setup_foo() +{ + foo_value = 2; +} + + + diff --git a/src/dyld-apple/unit-tests/test-cases/image-state-dependents-initialized/main.c b/src/dyld-apple/unit-tests/test-cases/image-state-dependents-initialized/main.c new file mode 100644 index 000000000..d4933f850 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/image-state-dependents-initialized/main.c @@ -0,0 +1,46 @@ +/* + * 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@ + */ +#include // fprintf(), NULL +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + + +int main(int argc, const char* argv[]) +{ + PASS("image-state-dependents-initialized"); + + return EXIT_SUCCESS; +} + + +int main_value = 1; + +void __attribute__((constructor)) setup_main() +{ + main_value = 2; +} + diff --git a/src/dyld-apple/unit-tests/test-cases/image-suffix/foo.c b/src/dyld-apple/unit-tests/test-cases/image-suffix/foo.c new file mode 100644 index 000000000..a267f20e3 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/image-suffix/foo.c @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include + +void foo() +{ + printf("foo"); +} diff --git a/src/dyld-apple/unit-tests/test-cases/image-suffix/main.c b/src/dyld-apple/unit-tests/test-cases/image-suffix/main.c new file mode 100644 index 000000000..204744edc --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/image-suffix/main.c @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2005-2009 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@ + */ +#include +#include +#include + +#include "test.h" + +int main(int argc, const char* argv[]) +{ + if ( argc < 2 ) { + FAIL("too few arguments to main()"); + return EXIT_SUCCESS; + } + + uint32_t imageCount = _dyld_image_count(); + dlopen(argv[1], 0); + if ( imageCount != _dyld_image_count() ) { + FAIL("image count changed"); + return EXIT_SUCCESS; + } + + PASS("images loaded properly"); + return EXIT_SUCCESS; +} + diff --git a/src/dyld-apple/unit-tests/test-cases/image_header_containing_address/foo.c b/src/dyld-apple/unit-tests/test-cases/image_header_containing_address/foo.c new file mode 100644 index 000000000..85e6cd8c3 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/image_header_containing_address/foo.c @@ -0,0 +1 @@ +void foo() {} diff --git a/src/dyld-apple/unit-tests/test-cases/image_header_containing_address/main.c b/src/dyld-apple/unit-tests/test-cases/image_header_containing_address/main.c new file mode 100644 index 000000000..56312cd90 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/image_header_containing_address/main.c @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2015 Apple Computer, 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +extern void foo(); + + +// checks dladdr() and dyld_image_header_containing_address() return same image +static void verify(void* addr) +{ + Dl_info info; + if ( dladdr(addr, &info) == 0 ) { + FAIL("dladdr(%p, xx) failed", addr); + exit(0); + } + const struct mach_header* mh = dyld_image_header_containing_address(addr); + if ( mh != info.dli_fbase ) { + FAIL("dladdr's dli_fbase != dyld_image_header_containing_address()"); + exit(0); + } +} + + +int main() +{ + verify(&main); + verify(&foo); + + int x; + if ( dyld_image_path_containing_address(&x) != NULL ) { + FAIL("dyld_image_header_containing_address() of stack variable not NULL"); + exit(0); + } + + PASS("dyld_image_header_containing_address"); + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/image_path_containing_address/foo.c b/src/dyld-apple/unit-tests/test-cases/image_path_containing_address/foo.c new file mode 100644 index 000000000..85e6cd8c3 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/image_path_containing_address/foo.c @@ -0,0 +1 @@ +void foo() {} diff --git a/src/dyld-apple/unit-tests/test-cases/image_path_containing_address/main.c b/src/dyld-apple/unit-tests/test-cases/image_path_containing_address/main.c new file mode 100644 index 000000000..2025b1d40 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/image_path_containing_address/main.c @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2005-2008 Apple Computer, 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +extern void foo(); + + +// checks dladdr() and dyld_image_path_containing_address() return same path +static void verify(void* addr) +{ + Dl_info info; + if ( dladdr(addr, &info) == 0 ) { + FAIL("dladdr(%p, xx) failed", addr); + exit(0); + } + const char* path = dyld_image_path_containing_address(addr); + if ( path != info.dli_fname ) { + FAIL("dladdr's dli_fname != dyld_image_path_containing_address()"); + exit(0); + } +} + + +int main() +{ + verify(&main); + verify(&foo); + + int x; + if ( dyld_image_path_containing_address(&x) != NULL ) { + FAIL("dyld_image_path_containing_address() of stack variable not NULL"); + exit(0); + } + + PASS("image_path_containing_address"); + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/init-libSystem-first/foo.c b/src/dyld-apple/unit-tests/test-cases/init-libSystem-first/foo.c new file mode 100644 index 000000000..1affc4348 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/init-libSystem-first/foo.c @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2008 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@ + */ +#include +#include + + +extern char*** _NSGetArgv(void); + +static bool libSystemAlreadyInited = false; + + + +// +static __attribute__((constructor)) void myInit() +{ + libSystemAlreadyInited = ( _NSGetArgv() != NULL ); +} + + + +bool foo() +{ + return libSystemAlreadyInited; +} diff --git a/src/dyld-apple/unit-tests/test-cases/init-libSystem-first/main.c b/src/dyld-apple/unit-tests/test-cases/init-libSystem-first/main.c new file mode 100644 index 000000000..28a4db3d1 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/init-libSystem-first/main.c @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2008 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +extern bool foo(); + +int main() +{ + if ( foo() ) + PASS("init-libSystem-first"); + else + FAIL("init-libSystem-first"); + + return EXIT_SUCCESS; +} + + diff --git a/src/dyld-apple/unit-tests/test-cases/init-order/base.c b/src/dyld-apple/unit-tests/test-cases/init-order/base.c new file mode 100644 index 000000000..2cdb40a9b --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/init-order/base.c @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include +#include + +#include "base.h" +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +static int state = 1; +bool badOrder = false; + +void setState(int nextState) +{ + if ( nextState == state ) + ++state; + else + badOrder = true; + +} + + +void baseCheck() +{ + if ( badOrder ) { + switch ( state ) { + case 1: + FAIL("init-order -init not run first"); + break; + case 2: + FAIL("init-order myInit1 not run second"); + break; + case 3: + FAIL("init-order myInit2 not run third"); + break; + case 4: + FAIL("init-order myInit3 not run fourth"); + break; + } + } + else + PASS("init-order"); +} + diff --git a/src/dyld-apple/unit-tests/test-cases/init-order/base.h b/src/dyld-apple/unit-tests/test-cases/init-order/base.h new file mode 100644 index 000000000..1dbf69785 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/init-order/base.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ + + +extern void setState(int); +extern void baseCheck(); + diff --git a/src/dyld-apple/unit-tests/test-cases/init-order/foo1.c b/src/dyld-apple/unit-tests/test-cases/init-order/foo1.c new file mode 100644 index 000000000..b8808ae23 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/init-order/foo1.c @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include +#include "base.h" + + + + +// should run second because first initiallzer in first .o file +static __attribute__((constructor)) void myInit1() +{ + //fprintf(stderr, "myInit1()\n"); + setState(2); +} + + +// should run thrid because second initiallzer in first .o file +static __attribute__((constructor)) void myInit2() +{ + //fprintf(stderr, "myInit2()\n"); + setState(3); +} + + +// should run first becuase -init runs first +void myDashInit() +{ + //fprintf(stderr, "myDashInit()\n"); + setState(1); +} + diff --git a/src/dyld-apple/unit-tests/test-cases/init-order/foo2.c b/src/dyld-apple/unit-tests/test-cases/init-order/foo2.c new file mode 100644 index 000000000..646358b61 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/init-order/foo2.c @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include +#include "base.h" + + +// should run fourth because first initiallzer in second .o file +static __attribute__((constructor)) void myInit3() +{ + //fprintf(stderr, "myInit3()\n"); + setState(4); +} + diff --git a/src/dyld-apple/unit-tests/test-cases/init-order/foo3.c b/src/dyld-apple/unit-tests/test-cases/init-order/foo3.c new file mode 100644 index 000000000..c92a44458 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/init-order/foo3.c @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include +#include "base.h" + +int __attribute__((weak)) coal1 = 3; +int __attribute__((weak)) coal2 = 2; + +static __attribute__((constructor)) void myinit() +{ + //fprintf(stderr, "myinit() in foo1.c\n"); + baseVerifyCoal1("in foo3", &coal1); + baseVerifyCoal2("in foo3", &coal2); +} + diff --git a/src/dyld-apple/unit-tests/test-cases/init-order/main.c b/src/dyld-apple/unit-tests/test-cases/init-order/main.c new file mode 100644 index 000000000..b210384ca --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/init-order/main.c @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +#include "base.h" + +int main() +{ + baseCheck(); + return EXIT_SUCCESS; +} + + diff --git a/src/dyld-apple/unit-tests/test-cases/initializer-args/main.c b/src/dyld-apple/unit-tests/test-cases/initializer-args/main.c new file mode 100644 index 000000000..79e43e0c7 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/initializer-args/main.c @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +static int my_argc = 0; +static char **my_argv = NULL; +static char **my_envp = NULL; +static char **my_appl = NULL; + +void +__attribute__((constructor)) +my_init(int argc, char **argv, char **envp, char **appl) +{ + my_argc = argc; + my_argv = argv; + my_envp = envp; + my_appl = appl; +} + +int +main(int argc, char **argv, char **envp, char**appl) +{ + if(argc == my_argc + && argv == my_argv + && envp == my_envp + && appl == my_appl) + { + PASS("initializer/constructor was called with arguments"); + } + else + { + FAIL("initializer/constructor was not called with arguments"); + } + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/initializer-bounds-check/bar.c b/src/dyld-apple/unit-tests/test-cases/initializer-bounds-check/bar.c new file mode 100644 index 000000000..12846f37e --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/initializer-bounds-check/bar.c @@ -0,0 +1,7 @@ + +int bar = 0; + +void altSecondInit() +{ + bar = 1; +} diff --git a/src/dyld-apple/unit-tests/test-cases/initializer-bounds-check/foo1.c b/src/dyld-apple/unit-tests/test-cases/initializer-bounds-check/foo1.c new file mode 100644 index 000000000..5333718e0 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/initializer-bounds-check/foo1.c @@ -0,0 +1,14 @@ +#include + +extern void* initializers[] __asm__("section$start$__DATA$__mod_init_func"); + +extern void altSecondInit(); + + +__attribute__((constructor)) +void firstInit() +{ + // slam second initializer to be pointer into another dylib + initializers[1] = &altSecondInit; +} + diff --git a/src/dyld-apple/unit-tests/test-cases/initializer-bounds-check/foo2.c b/src/dyld-apple/unit-tests/test-cases/initializer-bounds-check/foo2.c new file mode 100644 index 000000000..ea8cf54ab --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/initializer-bounds-check/foo2.c @@ -0,0 +1,12 @@ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +__attribute__((constructor)) +void secondInit() +{ + FAIL("initializer-bounds-check, second init called"); + exit(0); +} + diff --git a/src/dyld-apple/unit-tests/test-cases/initializer-bounds-check/main.c b/src/dyld-apple/unit-tests/test-cases/initializer-bounds-check/main.c new file mode 100644 index 000000000..bd4a512b0 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/initializer-bounds-check/main.c @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2011 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +extern int bar; + +int +main() +{ + if ( bar == 1 ) + FAIL("initializer-bounds-check, out of bounds initializer called"); + else + PASS("initializer-bounds-check"); + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/insert-libraries-weak-symbols/insert.c b/src/dyld-apple/unit-tests/test-cases/insert-libraries-weak-symbols/insert.c new file mode 100644 index 000000000..9bfcca7db --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/insert-libraries-weak-symbols/insert.c @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2009 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@ + */ +#include + + +void __attribute__((weak)) foo() +{ + +} + + +static __attribute__((constructor)) void init() +{ + // have intializer call weak function + foo(); +} + diff --git a/src/dyld-apple/unit-tests/test-cases/insert-libraries-weak-symbols/main.c b/src/dyld-apple/unit-tests/test-cases/insert-libraries-weak-symbols/main.c new file mode 100644 index 000000000..8593697a7 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/insert-libraries-weak-symbols/main.c @@ -0,0 +1,42 @@ +/* + * 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +// since both main and insert.dylib have a weak _foo, +// dyld will coalesce. +void __attribute__((weak)) foo() +{ + +} + + +int main() +{ + PASS("insert-libraries-weak-symbols"); + return EXIT_SUCCESS; +} + diff --git a/src/dyld-apple/unit-tests/test-cases/insert-libraries-with-initializer/base.c b/src/dyld-apple/unit-tests/test-cases/insert-libraries-with-initializer/base.c new file mode 100644 index 000000000..fcd63c6c0 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/insert-libraries-with-initializer/base.c @@ -0,0 +1,61 @@ +/* + * 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@ + */ +#include +#include + +#include "base.h" +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +static int state = 1; +bool badOrder = false; + +void setState(int nextState) +{ + if ( nextState == state ) + ++state; + else + badOrder = true; + +} + + +void baseCheck() +{ + if ( badOrder ) + FAIL("insert-libraries-with-initializers bad state at %d", state); + else + PASS("insert-libraries-with-initializers"); +} + + + + + + +static __attribute__((constructor)) void base_init() +{ + //fprintf(stderr, "base_init()\n"); + setState(1); +} + diff --git a/src/dyld-apple/unit-tests/test-cases/insert-libraries-with-initializer/base.h b/src/dyld-apple/unit-tests/test-cases/insert-libraries-with-initializer/base.h new file mode 100644 index 000000000..1dbf69785 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/insert-libraries-with-initializer/base.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ + + +extern void setState(int); +extern void baseCheck(); + diff --git a/src/dyld-apple/unit-tests/test-cases/insert-libraries-with-initializer/foo1.c b/src/dyld-apple/unit-tests/test-cases/insert-libraries-with-initializer/foo1.c new file mode 100644 index 000000000..ac55290e3 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/insert-libraries-with-initializer/foo1.c @@ -0,0 +1,35 @@ +/* + * 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@ + */ +#include +#include "base.h" + + + + +// should run fourth because first initiallzer in second .o file +static __attribute__((constructor)) void foo1_init() +{ + //fprintf(stderr, "foo1_init()\n"); + setState(4); +} + diff --git a/src/dyld-apple/unit-tests/test-cases/insert-libraries-with-initializer/foo2.c b/src/dyld-apple/unit-tests/test-cases/insert-libraries-with-initializer/foo2.c new file mode 100644 index 000000000..6574e02e0 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/insert-libraries-with-initializer/foo2.c @@ -0,0 +1,33 @@ +/* + * 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@ + */ +#include +#include "base.h" + + +// should run fourth because first initiallzer in second .o file +static __attribute__((constructor)) void foo2_init() +{ + //fprintf(stderr, "foo2_init()\n"); + setState(3); +} + diff --git a/src/dyld-apple/unit-tests/test-cases/insert-libraries-with-initializer/insert.c b/src/dyld-apple/unit-tests/test-cases/insert-libraries-with-initializer/insert.c new file mode 100644 index 000000000..79d78a274 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/insert-libraries-with-initializer/insert.c @@ -0,0 +1,34 @@ +/* + * 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@ + */ +#include +#include "base.h" + + + + +static __attribute__((constructor)) void insert() +{ + //fprintf(stderr, "insert()\n"); + setState(2); +} + diff --git a/src/dyld-apple/unit-tests/test-cases/insert-libraries-with-initializer/main.c b/src/dyld-apple/unit-tests/test-cases/insert-libraries-with-initializer/main.c new file mode 100644 index 000000000..64df39739 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/insert-libraries-with-initializer/main.c @@ -0,0 +1,43 @@ +/* + * 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +#include "base.h" + +int main() +{ + baseCheck(); + return EXIT_SUCCESS; +} + + +static __attribute__((constructor)) void main_init() +{ + //fprintf(stderr, "main_init()\n"); + setState(5); +} + diff --git a/src/dyld-apple/unit-tests/test-cases/insert-libraries-with-suid/main.c b/src/dyld-apple/unit-tests/test-cases/insert-libraries-with-suid/main.c new file mode 100644 index 000000000..c9d015cce --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/insert-libraries-with-suid/main.c @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2005-2006 Apple Computer, 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include // strcmp(), strncmp() + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +// +// binaries set to run as some other user id never use DYLD_INSERT_LIBRARIES +// That environment variable is cleared by dyld (its right-hand-side is set to empty) +// + +int main(int argc, const char *argv[]) +{ + const char* rhs = getenv("DYLD_INSERT_LIBRARIES"); + if ( (rhs != NULL) && (rhs[0] != '\0') ) + FAIL("insert-libraries-with-suid DYLD_INSERT_LIBRARIES not cleared"); + else + PASS("insert-libraries-with-suid"); + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/interpose-basic-prebound/foo.c b/src/dyld-apple/unit-tests/test-cases/interpose-basic-prebound/foo.c new file mode 100644 index 000000000..3b5f1ffac --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/interpose-basic-prebound/foo.c @@ -0,0 +1,12 @@ + +#include +#include + +bool check_dylib_interposing() +{ + const char* x = strdup("123"); + const char* y = strdup("456"); + + return ( (strcmp(x, "hello") == 0) && (strcmp(y, "hello") == 0) ); +} + diff --git a/src/dyld-apple/unit-tests/test-cases/interpose-basic-prebound/main.c b/src/dyld-apple/unit-tests/test-cases/interpose-basic-prebound/main.c new file mode 100644 index 000000000..ba5a95ce4 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/interpose-basic-prebound/main.c @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +extern bool check_dylib_interposing(); + +int main() +{ + if ( check_dylib_interposing() ) + PASS("interpose-basic"); + else + FAIL("interpose-basic"); + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/interpose-basic-prebound/mystrdup.c b/src/dyld-apple/unit-tests/test-cases/interpose-basic-prebound/mystrdup.c new file mode 100644 index 000000000..18041ac43 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/interpose-basic-prebound/mystrdup.c @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ + +#include +#include + +char* mystrdup(const char* in) +{ + return strdup("hello"); +} + +DYLD_INTERPOSE(mystrdup, strdup) diff --git a/src/dyld-apple/unit-tests/test-cases/interpose-basic/main.c b/src/dyld-apple/unit-tests/test-cases/interpose-basic/main.c new file mode 100644 index 000000000..ebcd74176 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/interpose-basic/main.c @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2005-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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +extern char* wrap_strdup(const char*); + +int main() +{ + const char* x = strdup("123"); + const char* y = wrap_strdup("456"); + + if ( (strcmp(x, "hello") == 0) && (strcmp(y, "hello") == 0) ) + PASS("interpose-basic"); + else + FAIL("interpose-basic"); + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/interpose-basic/mystrdup.c b/src/dyld-apple/unit-tests/test-cases/interpose-basic/mystrdup.c new file mode 100644 index 000000000..18041ac43 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/interpose-basic/mystrdup.c @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ + +#include +#include + +char* mystrdup(const char* in) +{ + return strdup("hello"); +} + +DYLD_INTERPOSE(mystrdup, strdup) diff --git a/src/dyld-apple/unit-tests/test-cases/interpose-basic/wrap.c b/src/dyld-apple/unit-tests/test-cases/interpose-basic/wrap.c new file mode 100644 index 000000000..f634ab2b9 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/interpose-basic/wrap.c @@ -0,0 +1,35 @@ +/* + * 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@ + */ + +#include + +// make a pointer statically initiallized to strdup() +// since libwrap.dylib is staticlly linked to main +// this verfies that interposing happens properly +static char* (*proc)(const char*) = strdup; + +char* wrap_strdup(const char* str) +{ + return proc(str); +} + diff --git a/src/dyld-apple/unit-tests/test-cases/interpose-chained/foo.c b/src/dyld-apple/unit-tests/test-cases/interpose-chained/foo.c new file mode 100644 index 000000000..681bc2970 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/interpose-chained/foo.c @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include +#include "foo.h" + +const char* foo(const char* str) +{ + char* result; + asprintf(&result, "foo(%s)", str); + return result; +} + diff --git a/src/dyld-apple/unit-tests/test-cases/interpose-chained/foo.h b/src/dyld-apple/unit-tests/test-cases/interpose-chained/foo.h new file mode 100644 index 000000000..2bb7c94f6 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/interpose-chained/foo.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ + +extern const char* foo(const char* str); diff --git a/src/dyld-apple/unit-tests/test-cases/interpose-chained/foo1.c b/src/dyld-apple/unit-tests/test-cases/interpose-chained/foo1.c new file mode 100644 index 000000000..634cc3b17 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/interpose-chained/foo1.c @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include +#include +#include "foo.h" + +const char* foo1(const char* str) +{ + char* result; + asprintf(&result, "foo1(%s)", foo(str)); + return result; +} + +DYLD_INTERPOSE(foo1, foo) diff --git a/src/dyld-apple/unit-tests/test-cases/interpose-chained/foo2.c b/src/dyld-apple/unit-tests/test-cases/interpose-chained/foo2.c new file mode 100644 index 000000000..fb21948bd --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/interpose-chained/foo2.c @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include +#include +#include "foo.h" + +const char* foo2(const char* str) +{ + char* result; + asprintf(&result, "foo2(%s)", foo(str)); + return result; +} + +DYLD_INTERPOSE(foo2, foo) diff --git a/src/dyld-apple/unit-tests/test-cases/interpose-chained/foo3.c b/src/dyld-apple/unit-tests/test-cases/interpose-chained/foo3.c new file mode 100644 index 000000000..b85392f19 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/interpose-chained/foo3.c @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include +#include +#include "foo.h" + +const char* foo3(const char* str) +{ + char* result; + asprintf(&result, "foo3(%s)", foo(str)); + return result; +} + +DYLD_INTERPOSE(foo3, foo) diff --git a/src/dyld-apple/unit-tests/test-cases/interpose-chained/main.c b/src/dyld-apple/unit-tests/test-cases/interpose-chained/main.c new file mode 100644 index 000000000..5209b67fc --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/interpose-chained/main.c @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() +#include "foo.h" + +int main() +{ + const char* x = foo("seed"); + + if ( strcmp(x, "foo3(foo2(foo1(foo(seed))))") == 0 ) + PASS("interpose-chained"); + else + FAIL("interpose-chained %s", x); + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/interpose-dlsym/main.c b/src/dyld-apple/unit-tests/test-cases/interpose-dlsym/main.c new file mode 100644 index 000000000..68577102b --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/interpose-dlsym/main.c @@ -0,0 +1,50 @@ +/* + * 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +int main() +{ + void* handle = dlopen("/usr/lib/libSystem.B.dylib", RTLD_LAZY); + if ( handle == NULL ) { + FAIL("interpose-dlsym: dlopen() error: %s", dlerror()); + exit(0); + } + void* dlsym_free = dlsym(handle, "free"); + if ( dlsym_free == NULL ) { + FAIL("interpose-dlsym: dlsym() error: %s", dlerror()); + exit(0); + } + + if ( dlsym_free == &free ) + PASS("interpose-dlsym"); + else + FAIL("interpose-dlsym: %p != %p", dlsym_free, &free); + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/interpose-dlsym/myfree.c b/src/dyld-apple/unit-tests/test-cases/interpose-dlsym/myfree.c new file mode 100644 index 000000000..f85647567 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/interpose-dlsym/myfree.c @@ -0,0 +1,32 @@ +/* + * 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@ + */ + +#include +#include + +void myfree(void* p) +{ + free(p); +} + +DYLD_INTERPOSE(myfree, free) diff --git a/src/dyld-apple/unit-tests/test-cases/interpose-dynamic-basic/main.c b/src/dyld-apple/unit-tests/test-cases/interpose-dynamic-basic/main.c new file mode 100644 index 000000000..b30251267 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/interpose-dynamic-basic/main.c @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2014 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include + +extern const struct mach_header __dso_handle; + + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +typedef char* (*DupProc)(const char*); + +char* mystrdup(const char* in) +{ + return "hello"; +} + +static const struct dyld_interpose_tuple sTable[] = { {&mystrdup, &strdup} }; + +int main() +{ + const char* preCall = strdup("123"); + DupProc preProc = &strdup; + + dyld_dynamic_interpose(&__dso_handle, sTable, 1); + + const char* postCall = strdup("123"); + DupProc postProc = &strdup; + + if ( strcmp(preCall, "123") != 0 ) { + FAIL("interpose-dynamic-basic control strdup failed"); + return EXIT_SUCCESS; + } + + if ( strcmp(postCall, "hello") != 0 ) { + FAIL("interpose-dynamic-basic interposed lazy strdup failed"); + return EXIT_SUCCESS; + } + + if ( preProc == postProc ) { + FAIL("interpose-dynamic-basic interposed non-lazy strdup failed"); + return EXIT_SUCCESS; + } + + PASS("interpose-dynamic-basic"); + + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/interpose-dynamic-dlsym/foo.c b/src/dyld-apple/unit-tests/test-cases/interpose-dynamic-dlsym/foo.c new file mode 100644 index 000000000..5bfa8929a --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/interpose-dynamic-dlsym/foo.c @@ -0,0 +1,26 @@ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include +#include + + +int foo() { return 0; } + +int alt_foo() { return 10; } + + + + +static const struct dyld_interpose_tuple sTable[] = { {&alt_foo, &foo} }; + + +__attribute__((constructor)) +void init() +{ + // switch main executable to use alt_foo() when it calls foo() + dyld_dynamic_interpose((const struct mach_header*)_NSGetMachExecuteHeader(), sTable, 1); + +} + diff --git a/src/dyld-apple/unit-tests/test-cases/interpose-dynamic-dlsym/main.c b/src/dyld-apple/unit-tests/test-cases/interpose-dynamic-dlsym/main.c new file mode 100644 index 000000000..da21a5cab --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/interpose-dynamic-dlsym/main.c @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2014 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +typedef int (*FooProc)(); + +int main() +{ + void* handle = dlopen("libfoo.dylib", RTLD_LAZY); + if ( handle == NULL ) { + FAIL("interpose-dynamic-dlsym: dlopen() error: %s", dlerror()); + return 0; + } + FooProc dlsym_foo = (FooProc)dlsym(handle, "foo"); + if ( dlsym_foo == NULL ) { + FAIL("interpose-dynamic-dlsym: dlsym() error: %s", dlerror()); + return 0; + } + + int result = (*dlsym_foo)(); + if ( result == 10 ) + PASS("interpose-dynamic-dlsym"); + else + FAIL("interpose-dynamic-dlsym"); + + return 0; +} diff --git a/src/dyld-apple/unit-tests/test-cases/interpose-dynamic-lazy/foo.c b/src/dyld-apple/unit-tests/test-cases/interpose-dynamic-lazy/foo.c new file mode 100644 index 000000000..49b589744 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/interpose-dynamic-lazy/foo.c @@ -0,0 +1,27 @@ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include +#include + + + +int foo() { return 0; } + +int alt_foo() { return 10; } + + + + +static const struct dyld_interpose_tuple sTable[] = { {&alt_foo, &foo} }; + + +__attribute__((constructor)) +void init() +{ + + dyld_dynamic_interpose((const struct mach_header*)_NSGetMachExecuteHeader(), sTable, 1); + +} + diff --git a/src/dyld-apple/unit-tests/test-cases/interpose-dynamic-lazy/main.c b/src/dyld-apple/unit-tests/test-cases/interpose-dynamic-lazy/main.c new file mode 100644 index 000000000..89e9ad927 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/interpose-dynamic-lazy/main.c @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2014 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +extern int foo(); + +int main() +{ + int result = foo(); + if ( result == 10 ) + PASS("interpose-dynamic-lazy"); + else + FAIL("interpose-dynamic-lazy"); + + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/interpose-multiple/base.c b/src/dyld-apple/unit-tests/test-cases/interpose-multiple/base.c new file mode 100644 index 000000000..126f99ec6 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/interpose-multiple/base.c @@ -0,0 +1,6 @@ +#include +#include "base.h" + +int base1() { return 1; } +int base2() { return 2; } + diff --git a/src/dyld-apple/unit-tests/test-cases/interpose-multiple/base.h b/src/dyld-apple/unit-tests/test-cases/interpose-multiple/base.h new file mode 100644 index 000000000..658b8a16c --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/interpose-multiple/base.h @@ -0,0 +1,3 @@ + +extern int base1(); +extern int base2(); diff --git a/src/dyld-apple/unit-tests/test-cases/interpose-multiple/foo1.c b/src/dyld-apple/unit-tests/test-cases/interpose-multiple/foo1.c new file mode 100644 index 000000000..067055995 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/interpose-multiple/foo1.c @@ -0,0 +1,26 @@ +#include +#include +#include +#include "base.h" +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +int (*p2)() = &base2; + +__attribute__((constructor)) +void myinit() +{ + if ( (*p2)() == 20 ) + PASS("interpose-multiple"); + else + FAIL("interpose-multiple"); +} + + +int mybase1() +{ + return 10; +} + + +DYLD_INTERPOSE(mybase1, base1) diff --git a/src/dyld-apple/unit-tests/test-cases/interpose-multiple/foo2.c b/src/dyld-apple/unit-tests/test-cases/interpose-multiple/foo2.c new file mode 100644 index 000000000..1f2f362b9 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/interpose-multiple/foo2.c @@ -0,0 +1,25 @@ +#include +#include +#include +#include "base.h" +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +int (*p1)() = &base1; + +__attribute__((constructor)) +void myinit() +{ + if ( (*p1)() == 10 ) + PASS("interpose-multiple"); + else + FAIL("interpose-multiple"); +} + + +int mybase2() +{ + return 20; +} + + +DYLD_INTERPOSE(mybase2, base2) diff --git a/src/dyld-apple/unit-tests/test-cases/interpose-multiple/main.c b/src/dyld-apple/unit-tests/test-cases/interpose-multiple/main.c new file mode 100644 index 000000000..e0bd1d1c1 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/interpose-multiple/main.c @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2013 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() +#include "base.h" + +int main() +{ + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/interpose-not-inserted/main.c b/src/dyld-apple/unit-tests/test-cases/interpose-not-inserted/main.c new file mode 100644 index 000000000..c71ffb6f7 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/interpose-not-inserted/main.c @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2005-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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +extern char* wrap_strdup(const char*); + +int main() +{ + const char* x = strdup("123"); + const char* y = wrap_strdup("456"); + + if ( (strcmp(x, "hello") == 0) && (strcmp(y, "hello") == 0) ) + PASS("interpose-not-inserted"); + else + FAIL("interpose-not-inserted"); + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/interpose-not-inserted/mystrdup.c b/src/dyld-apple/unit-tests/test-cases/interpose-not-inserted/mystrdup.c new file mode 100644 index 000000000..18041ac43 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/interpose-not-inserted/mystrdup.c @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ + +#include +#include + +char* mystrdup(const char* in) +{ + return strdup("hello"); +} + +DYLD_INTERPOSE(mystrdup, strdup) diff --git a/src/dyld-apple/unit-tests/test-cases/interpose-not-inserted/wrap.c b/src/dyld-apple/unit-tests/test-cases/interpose-not-inserted/wrap.c new file mode 100644 index 000000000..f634ab2b9 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/interpose-not-inserted/wrap.c @@ -0,0 +1,35 @@ +/* + * 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@ + */ + +#include + +// make a pointer statically initiallized to strdup() +// since libwrap.dylib is staticlly linked to main +// this verfies that interposing happens properly +static char* (*proc)(const char*) = strdup; + +char* wrap_strdup(const char* str) +{ + return proc(str); +} + diff --git a/src/dyld-apple/unit-tests/test-cases/interpose-shared-cache/main.c b/src/dyld-apple/unit-tests/test-cases/interpose-shared-cache/main.c new file mode 100644 index 000000000..93ef8980e --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/interpose-shared-cache/main.c @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2005-2011 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +extern bool allocationSeen(void* p); +typedef bool (*seenProc)(void*); + +int main() +{ + void* x = strdup("123"); + + seenProc seen = (seenProc)dlsym(RTLD_DEFAULT, "allocationSeen"); + + if ( (seen != NULL) && (*seen)(x) ) + PASS("interpose-basic-shared-cache"); + else + FAIL("interpose-basic-shared-cache"); + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/interpose-shared-cache/mymalloc.c b/src/dyld-apple/unit-tests/test-cases/interpose-shared-cache/mymalloc.c new file mode 100644 index 000000000..bca093882 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/interpose-shared-cache/mymalloc.c @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2005-2011 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@ + */ + +#include +#include +#include +#include +#include +#include + +static void* seenAllocations[128]; +static int seenAllocationIndex = 0; + +// record each malloc result in a ring buffer +void* my_malloc(size_t size) +{ + char* x = malloc(size); + seenAllocations[seenAllocationIndex++] = x; + seenAllocationIndex = (seenAllocationIndex & 127); //wrap around + return x; +} + +DYLD_INTERPOSE(my_malloc, malloc) + +bool allocationSeen(void* p) +{ + for(int i=0; i < 127; ++i) { + if ( seenAllocations[i] == p ) + return true; + } + return false; +} + + + diff --git a/src/dyld-apple/unit-tests/test-cases/jump-table-dynamic-lookup/foo.c b/src/dyld-apple/unit-tests/test-cases/jump-table-dynamic-lookup/foo.c new file mode 100644 index 000000000..35c9bb7f1 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/jump-table-dynamic-lookup/foo.c @@ -0,0 +1,827 @@ +#include "foo.h" + +void foo000(int p) {} +void foo001(int p) {} +void foo002(int p) {} +void foo003(int p) {} +void foo004(int p) {} +void foo005(int p) {} +void foo006(int p) {} +void foo007(int p) {} +void foo008(int p) {} +void foo009(int p) {} +void foo010(int p) {} +void foo011(int p) {} +void foo012(int p) {} +void foo013(int p) {} +void foo014(int p) {} +void foo015(int p) {} +void foo016(int p) {} +void foo017(int p) {} +void foo018(int p) {} +void foo019(int p) {} +void foo020(int p) {} +void foo021(int p) {} +void foo022(int p) {} +void foo023(int p) {} +void foo024(int p) {} +void foo025(int p) {} +void foo026(int p) {} +void foo027(int p) {} +void foo028(int p) {} +void foo029(int p) {} +void foo030(int p) {} +void foo031(int p) {} +void foo032(int p) {} +void foo033(int p) {} +void foo034(int p) {} +void foo035(int p) {} +void foo036(int p) {} +void foo037(int p) {} +void foo038(int p) {} +void foo039(int p) {} +void foo040(int p) {} +void foo041(int p) {} +void foo042(int p) {} +void foo043(int p) {} +void foo044(int p) {} +void foo045(int p) {} +void foo046(int p) {} +void foo047(int p) {} +void foo048(int p) {} +void foo049(int p) {} +void foo050(int p) {} +void foo051(int p) {} +void foo052(int p) {} +void foo053(int p) {} +void foo054(int p) {} +void foo055(int p) {} +void foo056(int p) {} +void foo057(int p) {} +void foo058(int p) {} +void foo059(int p) {} +void foo060(int p) {} +void foo061(int p) {} +void foo062(int p) {} +void foo063(int p) {} +void foo064(int p) {} +void foo065(int p) {} +void foo066(int p) {} +void foo067(int p) {} +void foo068(int p) {} +void foo069(int p) {} +void foo070(int p) {} +void foo071(int p) {} +void foo072(int p) {} +void foo073(int p) {} +void foo074(int p) {} +void foo075(int p) {} +void foo076(int p) {} +void foo077(int p) {} +void foo078(int p) {} +void foo079(int p) {} +void foo080(int p) {} +void foo081(int p) {} +void foo082(int p) {} +void foo083(int p) {} +void foo084(int p) {} +void foo085(int p) {} +void foo086(int p) {} +void foo087(int p) {} +void foo088(int p) {} +void foo089(int p) {} +void foo090(int p) {} +void foo091(int p) {} +void foo092(int p) {} +void foo093(int p) {} +void foo094(int p) {} +void foo095(int p) {} +void foo096(int p) {} +void foo097(int p) {} +void foo098(int p) {} +void foo099(int p) {} +void foo100(int p) {} +void foo101(int p) {} +void foo102(int p) {} +void foo103(int p) {} +void foo104(int p) {} +void foo105(int p) {} +void foo106(int p) {} +void foo107(int p) {} +void foo108(int p) {} +void foo109(int p) {} +void foo110(int p) {} +void foo111(int p) {} +void foo112(int p) {} +void foo113(int p) {} +void foo114(int p) {} +void foo115(int p) {} +void foo116(int p) {} +void foo117(int p) {} +void foo118(int p) {} +void foo119(int p) {} +void foo120(int p) {} +void foo121(int p) {} +void foo122(int p) {} +void foo123(int p) {} +void foo124(int p) {} +void foo125(int p) {} +void foo126(int p) {} +void foo127(int p) {} +void foo128(int p) {} +void foo129(int p) {} +void foo130(int p) {} +void foo131(int p) {} +void foo132(int p) {} +void foo133(int p) {} +void foo134(int p) {} +void foo135(int p) {} +void foo136(int p) {} +void foo137(int p) {} +void foo138(int p) {} +void foo139(int p) {} +void foo140(int p) {} +void foo141(int p) {} +void foo142(int p) {} +void foo143(int p) {} +void foo144(int p) {} +void foo145(int p) {} +void foo146(int p) {} +void foo147(int p) {} +void foo148(int p) {} +void foo149(int p) {} +void foo150(int p) {} +void foo151(int p) {} +void foo152(int p) {} +void foo153(int p) {} +void foo154(int p) {} +void foo155(int p) {} +void foo156(int p) {} +void foo157(int p) {} +void foo158(int p) {} +void foo159(int p) {} +void foo160(int p) {} +void foo161(int p) {} +void foo162(int p) {} +void foo163(int p) {} +void foo164(int p) {} +void foo165(int p) {} +void foo166(int p) {} +void foo167(int p) {} +void foo168(int p) {} +void foo169(int p) {} +void foo170(int p) {} +void foo171(int p) {} +void foo172(int p) {} +void foo173(int p) {} +void foo174(int p) {} +void foo175(int p) {} +void foo176(int p) {} +void foo177(int p) {} +void foo178(int p) {} +void foo179(int p) {} +void foo180(int p) {} +void foo181(int p) {} +void foo182(int p) {} +void foo183(int p) {} +void foo184(int p) {} +void foo185(int p) {} +void foo186(int p) {} +void foo187(int p) {} +void foo188(int p) {} +void foo189(int p) {} +void foo190(int p) {} +void foo191(int p) {} +void foo192(int p) {} +void foo193(int p) {} +void foo194(int p) {} +void foo195(int p) {} +void foo196(int p) {} +void foo197(int p) {} +void foo198(int p) {} +void foo199(int p) {} +void foo200(int p) {} +void foo201(int p) {} +void foo202(int p) {} +void foo203(int p) {} +void foo204(int p) {} +void foo205(int p) {} +void foo206(int p) {} +void foo207(int p) {} +void foo208(int p) {} +void foo209(int p) {} +void foo210(int p) {} +void foo211(int p) {} +void foo212(int p) {} +void foo213(int p) {} +void foo214(int p) {} +void foo215(int p) {} +void foo216(int p) {} +void foo217(int p) {} +void foo218(int p) {} +void foo219(int p) {} +void foo220(int p) {} +void foo221(int p) {} +void foo222(int p) {} +void foo223(int p) {} +void foo224(int p) {} +void foo225(int p) {} +void foo226(int p) {} +void foo227(int p) {} +void foo228(int p) {} +void foo229(int p) {} +void foo230(int p) {} +void foo231(int p) {} +void foo232(int p) {} +void foo233(int p) {} +void foo234(int p) {} +void foo235(int p) {} +void foo236(int p) {} +void foo237(int p) {} +void foo238(int p) {} +void foo239(int p) {} +void foo240(int p) {} +void foo241(int p) {} +void foo242(int p) {} +void foo243(int p) {} +void foo244(int p) {} +void foo245(int p) {} +void foo246(int p) {} +void foo247(int p) {} +void foo248(int p) {} +void foo249(int p) {} +void foo250(int p) {} +void foo251(int p) {} +void foo252(int p) {} +void foo253(int p) {} +void foo254(int p) {} +void foo255(int p) {} +void foo256(int p) {} +void foo257(int p) {} +void foo258(int p) {} +void foo259(int p) {} +void foo260(int p) {} +void foo261(int p) {} +void foo262(int p) {} +void foo263(int p) {} +void foo264(int p) {} +void foo265(int p) {} +void foo266(int p) {} +void foo267(int p) {} +void foo268(int p) {} +void foo269(int p) {} +void foo270(int p) {} +void foo271(int p) {} +void foo272(int p) {} +void foo273(int p) {} +void foo274(int p) {} +void foo275(int p) {} +void foo276(int p) {} +void foo277(int p) {} +void foo278(int p) {} +void foo279(int p) {} +void foo280(int p) {} +void foo281(int p) {} +void foo282(int p) {} +void foo283(int p) {} +void foo284(int p) {} +void foo285(int p) {} +void foo286(int p) {} +void foo287(int p) {} +void foo288(int p) {} +void foo289(int p) {} +void foo290(int p) {} +void foo291(int p) {} +void foo292(int p) {} +void foo293(int p) {} +void foo294(int p) {} +void foo295(int p) {} +void foo296(int p) {} +void foo297(int p) {} +void foo298(int p) {} +void foo299(int p) {} +void foo300(int p) {} +void foo301(int p) {} +void foo302(int p) {} +void foo303(int p) {} +void foo304(int p) {} +void foo305(int p) {} +void foo306(int p) {} +void foo307(int p) {} +void foo308(int p) {} +void foo309(int p) {} +void foo310(int p) {} +void foo311(int p) {} +void foo312(int p) {} +void foo313(int p) {} +void foo314(int p) {} +void foo315(int p) {} +void foo316(int p) {} +void foo317(int p) {} +void foo318(int p) {} +void foo319(int p) {} +void foo320(int p) {} +void foo321(int p) {} +void foo322(int p) {} +void foo323(int p) {} +void foo324(int p) {} +void foo325(int p) {} +void foo326(int p) {} +void foo327(int p) {} +void foo328(int p) {} +void foo329(int p) {} +void foo330(int p) {} +void foo331(int p) {} +void foo332(int p) {} +void foo333(int p) {} +void foo334(int p) {} +void foo335(int p) {} +void foo336(int p) {} +void foo337(int p) {} +void foo338(int p) {} +void foo339(int p) {} +void foo340(int p) {} +void foo341(int p) {} +void foo342(int p) {} +void foo343(int p) {} +void foo344(int p) {} +void foo345(int p) {} +void foo346(int p) {} +void foo347(int p) {} +void foo348(int p) {} +void foo349(int p) {} +void foo350(int p) {} +void foo351(int p) {} +void foo352(int p) {} +void foo353(int p) {} +void foo354(int p) {} +void foo355(int p) {} +void foo356(int p) {} +void foo357(int p) {} +void foo358(int p) {} +void foo359(int p) {} +void foo360(int p) {} +void foo361(int p) {} +void foo362(int p) {} +void foo363(int p) {} +void foo364(int p) {} +void foo365(int p) {} +void foo366(int p) {} +void foo367(int p) {} +void foo368(int p) {} +void foo369(int p) {} +void foo370(int p) {} +void foo371(int p) {} +void foo372(int p) {} +void foo373(int p) {} +void foo374(int p) {} +void foo375(int p) {} +void foo376(int p) {} +void foo377(int p) {} +void foo378(int p) {} +void foo379(int p) {} +void foo380(int p) {} +void foo381(int p) {} +void foo382(int p) {} +void foo383(int p) {} +void foo384(int p) {} +void foo385(int p) {} +void foo386(int p) {} +void foo387(int p) {} +void foo388(int p) {} +void foo389(int p) {} +void foo390(int p) {} +void foo391(int p) {} +void foo392(int p) {} +void foo393(int p) {} +void foo394(int p) {} +void foo395(int p) {} +void foo396(int p) {} +void foo397(int p) {} +void foo398(int p) {} +void foo399(int p) {} +void foo400(int p) {} +void foo401(int p) {} +void foo402(int p) {} +void foo403(int p) {} +void foo404(int p) {} +void foo405(int p) {} +void foo406(int p) {} +void foo407(int p) {} +void foo408(int p) {} +void foo409(int p) {} +void foo410(int p) {} +void foo411(int p) {} +void foo412(int p) {} +void foo413(int p) {} +void foo414(int p) {} +void foo415(int p) {} +void foo416(int p) {} +void foo417(int p) {} +void foo418(int p) {} +void foo419(int p) {} +void foo420(int p) {} +void foo421(int p) {} +void foo422(int p) {} +void foo423(int p) {} +void foo424(int p) {} +void foo425(int p) {} +void foo426(int p) {} +void foo427(int p) {} +void foo428(int p) {} +void foo429(int p) {} +void foo430(int p) {} +void foo431(int p) {} +void foo432(int p) {} +void foo433(int p) {} +void foo434(int p) {} +void foo435(int p) {} +void foo436(int p) {} +void foo437(int p) {} +void foo438(int p) {} +void foo439(int p) {} +void foo440(int p) {} +void foo441(int p) {} +void foo442(int p) {} +void foo443(int p) {} +void foo444(int p) {} +void foo445(int p) {} +void foo446(int p) {} +void foo447(int p) {} +void foo448(int p) {} +void foo449(int p) {} +void foo450(int p) {} +void foo451(int p) {} +void foo452(int p) {} +void foo453(int p) {} +void foo454(int p) {} +void foo455(int p) {} +void foo456(int p) {} +void foo457(int p) {} +void foo458(int p) {} +void foo459(int p) {} +void foo460(int p) {} +void foo461(int p) {} +void foo462(int p) {} +void foo463(int p) {} +void foo464(int p) {} +void foo465(int p) {} +void foo466(int p) {} +void foo467(int p) {} +void foo468(int p) {} +void foo469(int p) {} +void foo470(int p) {} +void foo471(int p) {} +void foo472(int p) {} +void foo473(int p) {} +void foo474(int p) {} +void foo475(int p) {} +void foo476(int p) {} +void foo477(int p) {} +void foo478(int p) {} +void foo479(int p) {} +void foo480(int p) {} +void foo481(int p) {} +void foo482(int p) {} +void foo483(int p) {} +void foo484(int p) {} +void foo485(int p) {} +void foo486(int p) {} +void foo487(int p) {} +void foo488(int p) {} +void foo489(int p) {} +void foo490(int p) {} +void foo491(int p) {} +void foo492(int p) {} +void foo493(int p) {} +void foo494(int p) {} +void foo495(int p) {} +void foo496(int p) {} +void foo497(int p) {} +void foo498(int p) {} +void foo499(int p) {} +void foo500(int p) {} +void foo501(int p) {} +void foo502(int p) {} +void foo503(int p) {} +void foo504(int p) {} +void foo505(int p) {} +void foo506(int p) {} +void foo507(int p) {} +void foo508(int p) {} +void foo509(int p) {} +void foo510(int p) {} +void foo511(int p) {} +void foo512(int p) {} +void foo513(int p) {} +void foo514(int p) {} +void foo515(int p) {} +void foo516(int p) {} +void foo517(int p) {} +void foo518(int p) {} +void foo519(int p) {} +void foo520(int p) {} +void foo521(int p) {} +void foo522(int p) {} +void foo523(int p) {} +void foo524(int p) {} +void foo525(int p) {} +void foo526(int p) {} +void foo527(int p) {} +void foo528(int p) {} +void foo529(int p) {} +void foo530(int p) {} +void foo531(int p) {} +void foo532(int p) {} +void foo533(int p) {} +void foo534(int p) {} +void foo535(int p) {} +void foo536(int p) {} +void foo537(int p) {} +void foo538(int p) {} +void foo539(int p) {} +void foo540(int p) {} +void foo541(int p) {} +void foo542(int p) {} +void foo543(int p) {} +void foo544(int p) {} +void foo545(int p) {} +void foo546(int p) {} +void foo547(int p) {} +void foo548(int p) {} +void foo549(int p) {} +void foo550(int p) {} +void foo551(int p) {} +void foo552(int p) {} +void foo553(int p) {} +void foo554(int p) {} +void foo555(int p) {} +void foo556(int p) {} +void foo557(int p) {} +void foo558(int p) {} +void foo559(int p) {} +void foo560(int p) {} +void foo561(int p) {} +void foo562(int p) {} +void foo563(int p) {} +void foo564(int p) {} +void foo565(int p) {} +void foo566(int p) {} +void foo567(int p) {} +void foo568(int p) {} +void foo569(int p) {} +void foo570(int p) {} +void foo571(int p) {} +void foo572(int p) {} +void foo573(int p) {} +void foo574(int p) {} +void foo575(int p) {} +void foo576(int p) {} +void foo577(int p) {} +void foo578(int p) {} +void foo579(int p) {} +void foo580(int p) {} +void foo581(int p) {} +void foo582(int p) {} +void foo583(int p) {} +void foo584(int p) {} +void foo585(int p) {} +void foo586(int p) {} +void foo587(int p) {} +void foo588(int p) {} +void foo589(int p) {} +void foo590(int p) {} +void foo591(int p) {} +void foo592(int p) {} +void foo593(int p) {} +void foo594(int p) {} +void foo595(int p) {} +void foo596(int p) {} +void foo597(int p) {} +void foo598(int p) {} +void foo599(int p) {} +void foo600(int p) {} +void foo601(int p) {} +void foo602(int p) {} +void foo603(int p) {} +void foo604(int p) {} +void foo605(int p) {} +void foo606(int p) {} +void foo607(int p) {} +void foo608(int p) {} +void foo609(int p) {} +void foo610(int p) {} +void foo611(int p) {} +void foo612(int p) {} +void foo613(int p) {} +void foo614(int p) {} +void foo615(int p) {} +void foo616(int p) {} +void foo617(int p) {} +void foo618(int p) {} +void foo619(int p) {} +void foo620(int p) {} +void foo621(int p) {} +void foo622(int p) {} +void foo623(int p) {} +void foo624(int p) {} +void foo625(int p) {} +void foo626(int p) {} +void foo627(int p) {} +void foo628(int p) {} +void foo629(int p) {} +void foo630(int p) {} +void foo631(int p) {} +void foo632(int p) {} +void foo633(int p) {} +void foo634(int p) {} +void foo635(int p) {} +void foo636(int p) {} +void foo637(int p) {} +void foo638(int p) {} +void foo639(int p) {} +void foo640(int p) {} +void foo641(int p) {} +void foo642(int p) {} +void foo643(int p) {} +void foo644(int p) {} +void foo645(int p) {} +void foo646(int p) {} +void foo647(int p) {} +void foo648(int p) {} +void foo649(int p) {} +void foo650(int p) {} +void foo651(int p) {} +void foo652(int p) {} +void foo653(int p) {} +void foo654(int p) {} +void foo655(int p) {} +void foo656(int p) {} +void foo657(int p) {} +void foo658(int p) {} +void foo659(int p) {} +void foo660(int p) {} +void foo661(int p) {} +void foo662(int p) {} +void foo663(int p) {} +void foo664(int p) {} +void foo665(int p) {} +void foo666(int p) {} +void foo667(int p) {} +void foo668(int p) {} +void foo669(int p) {} +void foo670(int p) {} +void foo671(int p) {} +void foo672(int p) {} +void foo673(int p) {} +void foo674(int p) {} +void foo675(int p) {} +void foo676(int p) {} +void foo677(int p) {} +void foo678(int p) {} +void foo679(int p) {} +void foo680(int p) {} +void foo681(int p) {} +void foo682(int p) {} +void foo683(int p) {} +void foo684(int p) {} +void foo685(int p) {} +void foo686(int p) {} +void foo687(int p) {} +void foo688(int p) {} +void foo689(int p) {} +void foo690(int p) {} +void foo691(int p) {} +void foo692(int p) {} +void foo693(int p) {} +void foo694(int p) {} +void foo695(int p) {} +void foo696(int p) {} +void foo697(int p) {} +void foo698(int p) {} +void foo699(int p) {} +void foo700(int p) {} +void foo701(int p) {} +void foo702(int p) {} +void foo703(int p) {} +void foo704(int p) {} +void foo705(int p) {} +void foo706(int p) {} +void foo707(int p) {} +void foo708(int p) {} +void foo709(int p) {} +void foo710(int p) {} +void foo711(int p) {} +void foo712(int p) {} +void foo713(int p) {} +void foo714(int p) {} +void foo715(int p) {} +void foo716(int p) {} +void foo717(int p) {} +void foo718(int p) {} +void foo719(int p) {} +void foo720(int p) {} +void foo721(int p) {} +void foo722(int p) {} +void foo723(int p) {} +void foo724(int p) {} +void foo725(int p) {} +void foo726(int p) {} +void foo727(int p) {} +void foo728(int p) {} +void foo729(int p) {} +void foo730(int p) {} +void foo731(int p) {} +void foo732(int p) {} +void foo733(int p) {} +void foo734(int p) {} +void foo735(int p) {} +void foo736(int p) {} +void foo737(int p) {} +void foo738(int p) {} +void foo739(int p) {} +void foo740(int p) {} +void foo741(int p) {} +void foo742(int p) {} +void foo743(int p) {} +void foo744(int p) {} +void foo745(int p) {} +void foo746(int p) {} +void foo747(int p) {} +void foo748(int p) {} +void foo749(int p) {} +void foo750(int p) {} +void foo751(int p) {} +void foo752(int p) {} +void foo753(int p) {} +void foo754(int p) {} +void foo755(int p) {} +void foo756(int p) {} +void foo757(int p) {} +void foo758(int p) {} +void foo759(int p) {} +void foo760(int p) {} +void foo761(int p) {} +void foo762(int p) {} +void foo763(int p) {} +void foo764(int p) {} +void foo765(int p) {} +void foo766(int p) {} +void foo767(int p) {} +void foo768(int p) {} +void foo769(int p) {} +void foo770(int p) {} +void foo771(int p) {} +void foo772(int p) {} +void foo773(int p) {} +void foo774(int p) {} +void foo775(int p) {} +void foo776(int p) {} +void foo777(int p) {} +void foo778(int p) {} +void foo779(int p) {} +void foo780(int p) {} +void foo781(int p) {} +void foo782(int p) {} +void foo783(int p) {} +void foo784(int p) {} +void foo785(int p) {} +void foo786(int p) {} +void foo787(int p) {} +void foo788(int p) {} +void foo789(int p) {} +void foo790(int p) {} +void foo791(int p) {} +void foo792(int p) {} +void foo793(int p) {} +void foo794(int p) {} +void foo795(int p) {} +void foo796(int p) {} +void foo797(int p) {} +void foo798(int p) {} +void foo799(int p) {} +void foo800(int p) {} +void foo801(int p) {} +void foo802(int p) {} +void foo803(int p) {} +void foo804(int p) {} +void foo805(int p) {} +void foo806(int p) {} +void foo807(int p) {} +void foo808(int p) {} +void foo809(int p) {} +void foo810(int p) {} +void foo811(int p) {} +void foo812(int p) {} +void foo813(int p) {} +void foo814(int p) {} +void foo815(int p) {} +void foo816(int p) {} +void foo817(int p) {} +void foo818(int p) {} +void foo819(int p) {} +void foo820(int p) {} +void foo821(int p) {} +void foo822(int p) {} +void foo823(int p) {} +void foo824(int p) {} diff --git a/src/dyld-apple/unit-tests/test-cases/jump-table-dynamic-lookup/foo.h b/src/dyld-apple/unit-tests/test-cases/jump-table-dynamic-lookup/foo.h new file mode 100644 index 000000000..33a9be484 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/jump-table-dynamic-lookup/foo.h @@ -0,0 +1,825 @@ +extern void foo000(int); +extern void foo001(int); +extern void foo002(int); +extern void foo003(int); +extern void foo004(int); +extern void foo005(int); +extern void foo006(int); +extern void foo007(int); +extern void foo008(int); +extern void foo009(int); +extern void foo010(int); +extern void foo011(int); +extern void foo012(int); +extern void foo013(int); +extern void foo014(int); +extern void foo015(int); +extern void foo016(int); +extern void foo017(int); +extern void foo018(int); +extern void foo019(int); +extern void foo020(int); +extern void foo021(int); +extern void foo022(int); +extern void foo023(int); +extern void foo024(int); +extern void foo025(int); +extern void foo026(int); +extern void foo027(int); +extern void foo028(int); +extern void foo029(int); +extern void foo030(int); +extern void foo031(int); +extern void foo032(int); +extern void foo033(int); +extern void foo034(int); +extern void foo035(int); +extern void foo036(int); +extern void foo037(int); +extern void foo038(int); +extern void foo039(int); +extern void foo040(int); +extern void foo041(int); +extern void foo042(int); +extern void foo043(int); +extern void foo044(int); +extern void foo045(int); +extern void foo046(int); +extern void foo047(int); +extern void foo048(int); +extern void foo049(int); +extern void foo050(int); +extern void foo051(int); +extern void foo052(int); +extern void foo053(int); +extern void foo054(int); +extern void foo055(int); +extern void foo056(int); +extern void foo057(int); +extern void foo058(int); +extern void foo059(int); +extern void foo060(int); +extern void foo061(int); +extern void foo062(int); +extern void foo063(int); +extern void foo064(int); +extern void foo065(int); +extern void foo066(int); +extern void foo067(int); +extern void foo068(int); +extern void foo069(int); +extern void foo070(int); +extern void foo071(int); +extern void foo072(int); +extern void foo073(int); +extern void foo074(int); +extern void foo075(int); +extern void foo076(int); +extern void foo077(int); +extern void foo078(int); +extern void foo079(int); +extern void foo080(int); +extern void foo081(int); +extern void foo082(int); +extern void foo083(int); +extern void foo084(int); +extern void foo085(int); +extern void foo086(int); +extern void foo087(int); +extern void foo088(int); +extern void foo089(int); +extern void foo090(int); +extern void foo091(int); +extern void foo092(int); +extern void foo093(int); +extern void foo094(int); +extern void foo095(int); +extern void foo096(int); +extern void foo097(int); +extern void foo098(int); +extern void foo099(int); +extern void foo100(int); +extern void foo101(int); +extern void foo102(int); +extern void foo103(int); +extern void foo104(int); +extern void foo105(int); +extern void foo106(int); +extern void foo107(int); +extern void foo108(int); +extern void foo109(int); +extern void foo110(int); +extern void foo111(int); +extern void foo112(int); +extern void foo113(int); +extern void foo114(int); +extern void foo115(int); +extern void foo116(int); +extern void foo117(int); +extern void foo118(int); +extern void foo119(int); +extern void foo120(int); +extern void foo121(int); +extern void foo122(int); +extern void foo123(int); +extern void foo124(int); +extern void foo125(int); +extern void foo126(int); +extern void foo127(int); +extern void foo128(int); +extern void foo129(int); +extern void foo130(int); +extern void foo131(int); +extern void foo132(int); +extern void foo133(int); +extern void foo134(int); +extern void foo135(int); +extern void foo136(int); +extern void foo137(int); +extern void foo138(int); +extern void foo139(int); +extern void foo140(int); +extern void foo141(int); +extern void foo142(int); +extern void foo143(int); +extern void foo144(int); +extern void foo145(int); +extern void foo146(int); +extern void foo147(int); +extern void foo148(int); +extern void foo149(int); +extern void foo150(int); +extern void foo151(int); +extern void foo152(int); +extern void foo153(int); +extern void foo154(int); +extern void foo155(int); +extern void foo156(int); +extern void foo157(int); +extern void foo158(int); +extern void foo159(int); +extern void foo160(int); +extern void foo161(int); +extern void foo162(int); +extern void foo163(int); +extern void foo164(int); +extern void foo165(int); +extern void foo166(int); +extern void foo167(int); +extern void foo168(int); +extern void foo169(int); +extern void foo170(int); +extern void foo171(int); +extern void foo172(int); +extern void foo173(int); +extern void foo174(int); +extern void foo175(int); +extern void foo176(int); +extern void foo177(int); +extern void foo178(int); +extern void foo179(int); +extern void foo180(int); +extern void foo181(int); +extern void foo182(int); +extern void foo183(int); +extern void foo184(int); +extern void foo185(int); +extern void foo186(int); +extern void foo187(int); +extern void foo188(int); +extern void foo189(int); +extern void foo190(int); +extern void foo191(int); +extern void foo192(int); +extern void foo193(int); +extern void foo194(int); +extern void foo195(int); +extern void foo196(int); +extern void foo197(int); +extern void foo198(int); +extern void foo199(int); +extern void foo200(int); +extern void foo201(int); +extern void foo202(int); +extern void foo203(int); +extern void foo204(int); +extern void foo205(int); +extern void foo206(int); +extern void foo207(int); +extern void foo208(int); +extern void foo209(int); +extern void foo210(int); +extern void foo211(int); +extern void foo212(int); +extern void foo213(int); +extern void foo214(int); +extern void foo215(int); +extern void foo216(int); +extern void foo217(int); +extern void foo218(int); +extern void foo219(int); +extern void foo220(int); +extern void foo221(int); +extern void foo222(int); +extern void foo223(int); +extern void foo224(int); +extern void foo225(int); +extern void foo226(int); +extern void foo227(int); +extern void foo228(int); +extern void foo229(int); +extern void foo230(int); +extern void foo231(int); +extern void foo232(int); +extern void foo233(int); +extern void foo234(int); +extern void foo235(int); +extern void foo236(int); +extern void foo237(int); +extern void foo238(int); +extern void foo239(int); +extern void foo240(int); +extern void foo241(int); +extern void foo242(int); +extern void foo243(int); +extern void foo244(int); +extern void foo245(int); +extern void foo246(int); +extern void foo247(int); +extern void foo248(int); +extern void foo249(int); +extern void foo250(int); +extern void foo251(int); +extern void foo252(int); +extern void foo253(int); +extern void foo254(int); +extern void foo255(int); +extern void foo256(int); +extern void foo257(int); +extern void foo258(int); +extern void foo259(int); +extern void foo260(int); +extern void foo261(int); +extern void foo262(int); +extern void foo263(int); +extern void foo264(int); +extern void foo265(int); +extern void foo266(int); +extern void foo267(int); +extern void foo268(int); +extern void foo269(int); +extern void foo270(int); +extern void foo271(int); +extern void foo272(int); +extern void foo273(int); +extern void foo274(int); +extern void foo275(int); +extern void foo276(int); +extern void foo277(int); +extern void foo278(int); +extern void foo279(int); +extern void foo280(int); +extern void foo281(int); +extern void foo282(int); +extern void foo283(int); +extern void foo284(int); +extern void foo285(int); +extern void foo286(int); +extern void foo287(int); +extern void foo288(int); +extern void foo289(int); +extern void foo290(int); +extern void foo291(int); +extern void foo292(int); +extern void foo293(int); +extern void foo294(int); +extern void foo295(int); +extern void foo296(int); +extern void foo297(int); +extern void foo298(int); +extern void foo299(int); +extern void foo300(int); +extern void foo301(int); +extern void foo302(int); +extern void foo303(int); +extern void foo304(int); +extern void foo305(int); +extern void foo306(int); +extern void foo307(int); +extern void foo308(int); +extern void foo309(int); +extern void foo310(int); +extern void foo311(int); +extern void foo312(int); +extern void foo313(int); +extern void foo314(int); +extern void foo315(int); +extern void foo316(int); +extern void foo317(int); +extern void foo318(int); +extern void foo319(int); +extern void foo320(int); +extern void foo321(int); +extern void foo322(int); +extern void foo323(int); +extern void foo324(int); +extern void foo325(int); +extern void foo326(int); +extern void foo327(int); +extern void foo328(int); +extern void foo329(int); +extern void foo330(int); +extern void foo331(int); +extern void foo332(int); +extern void foo333(int); +extern void foo334(int); +extern void foo335(int); +extern void foo336(int); +extern void foo337(int); +extern void foo338(int); +extern void foo339(int); +extern void foo340(int); +extern void foo341(int); +extern void foo342(int); +extern void foo343(int); +extern void foo344(int); +extern void foo345(int); +extern void foo346(int); +extern void foo347(int); +extern void foo348(int); +extern void foo349(int); +extern void foo350(int); +extern void foo351(int); +extern void foo352(int); +extern void foo353(int); +extern void foo354(int); +extern void foo355(int); +extern void foo356(int); +extern void foo357(int); +extern void foo358(int); +extern void foo359(int); +extern void foo360(int); +extern void foo361(int); +extern void foo362(int); +extern void foo363(int); +extern void foo364(int); +extern void foo365(int); +extern void foo366(int); +extern void foo367(int); +extern void foo368(int); +extern void foo369(int); +extern void foo370(int); +extern void foo371(int); +extern void foo372(int); +extern void foo373(int); +extern void foo374(int); +extern void foo375(int); +extern void foo376(int); +extern void foo377(int); +extern void foo378(int); +extern void foo379(int); +extern void foo380(int); +extern void foo381(int); +extern void foo382(int); +extern void foo383(int); +extern void foo384(int); +extern void foo385(int); +extern void foo386(int); +extern void foo387(int); +extern void foo388(int); +extern void foo389(int); +extern void foo390(int); +extern void foo391(int); +extern void foo392(int); +extern void foo393(int); +extern void foo394(int); +extern void foo395(int); +extern void foo396(int); +extern void foo397(int); +extern void foo398(int); +extern void foo399(int); +extern void foo400(int); +extern void foo401(int); +extern void foo402(int); +extern void foo403(int); +extern void foo404(int); +extern void foo405(int); +extern void foo406(int); +extern void foo407(int); +extern void foo408(int); +extern void foo409(int); +extern void foo410(int); +extern void foo411(int); +extern void foo412(int); +extern void foo413(int); +extern void foo414(int); +extern void foo415(int); +extern void foo416(int); +extern void foo417(int); +extern void foo418(int); +extern void foo419(int); +extern void foo420(int); +extern void foo421(int); +extern void foo422(int); +extern void foo423(int); +extern void foo424(int); +extern void foo425(int); +extern void foo426(int); +extern void foo427(int); +extern void foo428(int); +extern void foo429(int); +extern void foo430(int); +extern void foo431(int); +extern void foo432(int); +extern void foo433(int); +extern void foo434(int); +extern void foo435(int); +extern void foo436(int); +extern void foo437(int); +extern void foo438(int); +extern void foo439(int); +extern void foo440(int); +extern void foo441(int); +extern void foo442(int); +extern void foo443(int); +extern void foo444(int); +extern void foo445(int); +extern void foo446(int); +extern void foo447(int); +extern void foo448(int); +extern void foo449(int); +extern void foo450(int); +extern void foo451(int); +extern void foo452(int); +extern void foo453(int); +extern void foo454(int); +extern void foo455(int); +extern void foo456(int); +extern void foo457(int); +extern void foo458(int); +extern void foo459(int); +extern void foo460(int); +extern void foo461(int); +extern void foo462(int); +extern void foo463(int); +extern void foo464(int); +extern void foo465(int); +extern void foo466(int); +extern void foo467(int); +extern void foo468(int); +extern void foo469(int); +extern void foo470(int); +extern void foo471(int); +extern void foo472(int); +extern void foo473(int); +extern void foo474(int); +extern void foo475(int); +extern void foo476(int); +extern void foo477(int); +extern void foo478(int); +extern void foo479(int); +extern void foo480(int); +extern void foo481(int); +extern void foo482(int); +extern void foo483(int); +extern void foo484(int); +extern void foo485(int); +extern void foo486(int); +extern void foo487(int); +extern void foo488(int); +extern void foo489(int); +extern void foo490(int); +extern void foo491(int); +extern void foo492(int); +extern void foo493(int); +extern void foo494(int); +extern void foo495(int); +extern void foo496(int); +extern void foo497(int); +extern void foo498(int); +extern void foo499(int); +extern void foo500(int); +extern void foo501(int); +extern void foo502(int); +extern void foo503(int); +extern void foo504(int); +extern void foo505(int); +extern void foo506(int); +extern void foo507(int); +extern void foo508(int); +extern void foo509(int); +extern void foo510(int); +extern void foo511(int); +extern void foo512(int); +extern void foo513(int); +extern void foo514(int); +extern void foo515(int); +extern void foo516(int); +extern void foo517(int); +extern void foo518(int); +extern void foo519(int); +extern void foo520(int); +extern void foo521(int); +extern void foo522(int); +extern void foo523(int); +extern void foo524(int); +extern void foo525(int); +extern void foo526(int); +extern void foo527(int); +extern void foo528(int); +extern void foo529(int); +extern void foo530(int); +extern void foo531(int); +extern void foo532(int); +extern void foo533(int); +extern void foo534(int); +extern void foo535(int); +extern void foo536(int); +extern void foo537(int); +extern void foo538(int); +extern void foo539(int); +extern void foo540(int); +extern void foo541(int); +extern void foo542(int); +extern void foo543(int); +extern void foo544(int); +extern void foo545(int); +extern void foo546(int); +extern void foo547(int); +extern void foo548(int); +extern void foo549(int); +extern void foo550(int); +extern void foo551(int); +extern void foo552(int); +extern void foo553(int); +extern void foo554(int); +extern void foo555(int); +extern void foo556(int); +extern void foo557(int); +extern void foo558(int); +extern void foo559(int); +extern void foo560(int); +extern void foo561(int); +extern void foo562(int); +extern void foo563(int); +extern void foo564(int); +extern void foo565(int); +extern void foo566(int); +extern void foo567(int); +extern void foo568(int); +extern void foo569(int); +extern void foo570(int); +extern void foo571(int); +extern void foo572(int); +extern void foo573(int); +extern void foo574(int); +extern void foo575(int); +extern void foo576(int); +extern void foo577(int); +extern void foo578(int); +extern void foo579(int); +extern void foo580(int); +extern void foo581(int); +extern void foo582(int); +extern void foo583(int); +extern void foo584(int); +extern void foo585(int); +extern void foo586(int); +extern void foo587(int); +extern void foo588(int); +extern void foo589(int); +extern void foo590(int); +extern void foo591(int); +extern void foo592(int); +extern void foo593(int); +extern void foo594(int); +extern void foo595(int); +extern void foo596(int); +extern void foo597(int); +extern void foo598(int); +extern void foo599(int); +extern void foo600(int); +extern void foo601(int); +extern void foo602(int); +extern void foo603(int); +extern void foo604(int); +extern void foo605(int); +extern void foo606(int); +extern void foo607(int); +extern void foo608(int); +extern void foo609(int); +extern void foo610(int); +extern void foo611(int); +extern void foo612(int); +extern void foo613(int); +extern void foo614(int); +extern void foo615(int); +extern void foo616(int); +extern void foo617(int); +extern void foo618(int); +extern void foo619(int); +extern void foo620(int); +extern void foo621(int); +extern void foo622(int); +extern void foo623(int); +extern void foo624(int); +extern void foo625(int); +extern void foo626(int); +extern void foo627(int); +extern void foo628(int); +extern void foo629(int); +extern void foo630(int); +extern void foo631(int); +extern void foo632(int); +extern void foo633(int); +extern void foo634(int); +extern void foo635(int); +extern void foo636(int); +extern void foo637(int); +extern void foo638(int); +extern void foo639(int); +extern void foo640(int); +extern void foo641(int); +extern void foo642(int); +extern void foo643(int); +extern void foo644(int); +extern void foo645(int); +extern void foo646(int); +extern void foo647(int); +extern void foo648(int); +extern void foo649(int); +extern void foo650(int); +extern void foo651(int); +extern void foo652(int); +extern void foo653(int); +extern void foo654(int); +extern void foo655(int); +extern void foo656(int); +extern void foo657(int); +extern void foo658(int); +extern void foo659(int); +extern void foo660(int); +extern void foo661(int); +extern void foo662(int); +extern void foo663(int); +extern void foo664(int); +extern void foo665(int); +extern void foo666(int); +extern void foo667(int); +extern void foo668(int); +extern void foo669(int); +extern void foo670(int); +extern void foo671(int); +extern void foo672(int); +extern void foo673(int); +extern void foo674(int); +extern void foo675(int); +extern void foo676(int); +extern void foo677(int); +extern void foo678(int); +extern void foo679(int); +extern void foo680(int); +extern void foo681(int); +extern void foo682(int); +extern void foo683(int); +extern void foo684(int); +extern void foo685(int); +extern void foo686(int); +extern void foo687(int); +extern void foo688(int); +extern void foo689(int); +extern void foo690(int); +extern void foo691(int); +extern void foo692(int); +extern void foo693(int); +extern void foo694(int); +extern void foo695(int); +extern void foo696(int); +extern void foo697(int); +extern void foo698(int); +extern void foo699(int); +extern void foo700(int); +extern void foo701(int); +extern void foo702(int); +extern void foo703(int); +extern void foo704(int); +extern void foo705(int); +extern void foo706(int); +extern void foo707(int); +extern void foo708(int); +extern void foo709(int); +extern void foo710(int); +extern void foo711(int); +extern void foo712(int); +extern void foo713(int); +extern void foo714(int); +extern void foo715(int); +extern void foo716(int); +extern void foo717(int); +extern void foo718(int); +extern void foo719(int); +extern void foo720(int); +extern void foo721(int); +extern void foo722(int); +extern void foo723(int); +extern void foo724(int); +extern void foo725(int); +extern void foo726(int); +extern void foo727(int); +extern void foo728(int); +extern void foo729(int); +extern void foo730(int); +extern void foo731(int); +extern void foo732(int); +extern void foo733(int); +extern void foo734(int); +extern void foo735(int); +extern void foo736(int); +extern void foo737(int); +extern void foo738(int); +extern void foo739(int); +extern void foo740(int); +extern void foo741(int); +extern void foo742(int); +extern void foo743(int); +extern void foo744(int); +extern void foo745(int); +extern void foo746(int); +extern void foo747(int); +extern void foo748(int); +extern void foo749(int); +extern void foo750(int); +extern void foo751(int); +extern void foo752(int); +extern void foo753(int); +extern void foo754(int); +extern void foo755(int); +extern void foo756(int); +extern void foo757(int); +extern void foo758(int); +extern void foo759(int); +extern void foo760(int); +extern void foo761(int); +extern void foo762(int); +extern void foo763(int); +extern void foo764(int); +extern void foo765(int); +extern void foo766(int); +extern void foo767(int); +extern void foo768(int); +extern void foo769(int); +extern void foo770(int); +extern void foo771(int); +extern void foo772(int); +extern void foo773(int); +extern void foo774(int); +extern void foo775(int); +extern void foo776(int); +extern void foo777(int); +extern void foo778(int); +extern void foo779(int); +extern void foo780(int); +extern void foo781(int); +extern void foo782(int); +extern void foo783(int); +extern void foo784(int); +extern void foo785(int); +extern void foo786(int); +extern void foo787(int); +extern void foo788(int); +extern void foo789(int); +extern void foo790(int); +extern void foo791(int); +extern void foo792(int); +extern void foo793(int); +extern void foo794(int); +extern void foo795(int); +extern void foo796(int); +extern void foo797(int); +extern void foo798(int); +extern void foo799(int); +extern void foo800(int); +extern void foo801(int); +extern void foo802(int); +extern void foo803(int); +extern void foo804(int); +extern void foo805(int); +extern void foo806(int); +extern void foo807(int); +extern void foo808(int); +extern void foo809(int); +extern void foo810(int); +extern void foo811(int); +extern void foo812(int); +extern void foo813(int); +extern void foo814(int); +extern void foo815(int); +extern void foo816(int); +extern void foo817(int); +extern void foo818(int); +extern void foo819(int); +extern void foo820(int); +extern void foo821(int); +extern void foo822(int); +extern void foo823(int); +extern void foo824(int); diff --git a/src/dyld-apple/unit-tests/test-cases/jump-table-dynamic-lookup/main.c b/src/dyld-apple/unit-tests/test-cases/jump-table-dynamic-lookup/main.c new file mode 100644 index 000000000..6325af75d --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/jump-table-dynamic-lookup/main.c @@ -0,0 +1,80 @@ +/* + * 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@ + */ +#include // fprintf(0x12345678), NULL +#include // exit(0x12345678), EXIT_SUCCESS +#include + +#include "test.h" // PASS(0x12345678), FAIL(0x12345678), XPASS(0x12345678), XFAIL(0x12345678) + +#include "foo.h" + +static void* callAll(void* p) +{ + foo001(0x12345678); + foo002(0x12345678); + foo003(0x12345678); + foo004(0x12345678); + foo005(0x12345678); + foo006(0x12345678); + foo007(0x12345678); + foo008(0x12345678); + foo009(0x12345678); + foo010(0x12345678); + foo011(0x12345678); + foo012(0x12345678); + foo013(0x12345678); + foo014(0x12345678); + foo015(0x12345678); + foo016(0x12345678); + foo017(0x12345678); + foo018(0x12345678); + foo019(0x12345678); + foo020(0x12345678); + foo021(0x12345678); + foo022(0x12345678); + foo023(0x12345678); + foo024(0x12345678); + foo025(0x12345678); + foo026(0x12345678); + foo027(0x12345678); + foo028(0x12345678); + foo029(0x12345678); + foo030(0x12345678); + foo031(0x12345678); + foo032(0x12345678); + return NULL; +} + +int main() +{ + // load libfoo so that functions can be found dynamically + dlopen("libfoo.dylib", RTLD_LAZY); + + // call all foos + callAll(NULL); + + PASS("jump-table-dynamic-lookup"); + return EXIT_SUCCESS; +} + + diff --git a/src/dyld-apple/unit-tests/test-cases/jump-table-race/foo.c b/src/dyld-apple/unit-tests/test-cases/jump-table-race/foo.c new file mode 100644 index 000000000..35c9bb7f1 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/jump-table-race/foo.c @@ -0,0 +1,827 @@ +#include "foo.h" + +void foo000(int p) {} +void foo001(int p) {} +void foo002(int p) {} +void foo003(int p) {} +void foo004(int p) {} +void foo005(int p) {} +void foo006(int p) {} +void foo007(int p) {} +void foo008(int p) {} +void foo009(int p) {} +void foo010(int p) {} +void foo011(int p) {} +void foo012(int p) {} +void foo013(int p) {} +void foo014(int p) {} +void foo015(int p) {} +void foo016(int p) {} +void foo017(int p) {} +void foo018(int p) {} +void foo019(int p) {} +void foo020(int p) {} +void foo021(int p) {} +void foo022(int p) {} +void foo023(int p) {} +void foo024(int p) {} +void foo025(int p) {} +void foo026(int p) {} +void foo027(int p) {} +void foo028(int p) {} +void foo029(int p) {} +void foo030(int p) {} +void foo031(int p) {} +void foo032(int p) {} +void foo033(int p) {} +void foo034(int p) {} +void foo035(int p) {} +void foo036(int p) {} +void foo037(int p) {} +void foo038(int p) {} +void foo039(int p) {} +void foo040(int p) {} +void foo041(int p) {} +void foo042(int p) {} +void foo043(int p) {} +void foo044(int p) {} +void foo045(int p) {} +void foo046(int p) {} +void foo047(int p) {} +void foo048(int p) {} +void foo049(int p) {} +void foo050(int p) {} +void foo051(int p) {} +void foo052(int p) {} +void foo053(int p) {} +void foo054(int p) {} +void foo055(int p) {} +void foo056(int p) {} +void foo057(int p) {} +void foo058(int p) {} +void foo059(int p) {} +void foo060(int p) {} +void foo061(int p) {} +void foo062(int p) {} +void foo063(int p) {} +void foo064(int p) {} +void foo065(int p) {} +void foo066(int p) {} +void foo067(int p) {} +void foo068(int p) {} +void foo069(int p) {} +void foo070(int p) {} +void foo071(int p) {} +void foo072(int p) {} +void foo073(int p) {} +void foo074(int p) {} +void foo075(int p) {} +void foo076(int p) {} +void foo077(int p) {} +void foo078(int p) {} +void foo079(int p) {} +void foo080(int p) {} +void foo081(int p) {} +void foo082(int p) {} +void foo083(int p) {} +void foo084(int p) {} +void foo085(int p) {} +void foo086(int p) {} +void foo087(int p) {} +void foo088(int p) {} +void foo089(int p) {} +void foo090(int p) {} +void foo091(int p) {} +void foo092(int p) {} +void foo093(int p) {} +void foo094(int p) {} +void foo095(int p) {} +void foo096(int p) {} +void foo097(int p) {} +void foo098(int p) {} +void foo099(int p) {} +void foo100(int p) {} +void foo101(int p) {} +void foo102(int p) {} +void foo103(int p) {} +void foo104(int p) {} +void foo105(int p) {} +void foo106(int p) {} +void foo107(int p) {} +void foo108(int p) {} +void foo109(int p) {} +void foo110(int p) {} +void foo111(int p) {} +void foo112(int p) {} +void foo113(int p) {} +void foo114(int p) {} +void foo115(int p) {} +void foo116(int p) {} +void foo117(int p) {} +void foo118(int p) {} +void foo119(int p) {} +void foo120(int p) {} +void foo121(int p) {} +void foo122(int p) {} +void foo123(int p) {} +void foo124(int p) {} +void foo125(int p) {} +void foo126(int p) {} +void foo127(int p) {} +void foo128(int p) {} +void foo129(int p) {} +void foo130(int p) {} +void foo131(int p) {} +void foo132(int p) {} +void foo133(int p) {} +void foo134(int p) {} +void foo135(int p) {} +void foo136(int p) {} +void foo137(int p) {} +void foo138(int p) {} +void foo139(int p) {} +void foo140(int p) {} +void foo141(int p) {} +void foo142(int p) {} +void foo143(int p) {} +void foo144(int p) {} +void foo145(int p) {} +void foo146(int p) {} +void foo147(int p) {} +void foo148(int p) {} +void foo149(int p) {} +void foo150(int p) {} +void foo151(int p) {} +void foo152(int p) {} +void foo153(int p) {} +void foo154(int p) {} +void foo155(int p) {} +void foo156(int p) {} +void foo157(int p) {} +void foo158(int p) {} +void foo159(int p) {} +void foo160(int p) {} +void foo161(int p) {} +void foo162(int p) {} +void foo163(int p) {} +void foo164(int p) {} +void foo165(int p) {} +void foo166(int p) {} +void foo167(int p) {} +void foo168(int p) {} +void foo169(int p) {} +void foo170(int p) {} +void foo171(int p) {} +void foo172(int p) {} +void foo173(int p) {} +void foo174(int p) {} +void foo175(int p) {} +void foo176(int p) {} +void foo177(int p) {} +void foo178(int p) {} +void foo179(int p) {} +void foo180(int p) {} +void foo181(int p) {} +void foo182(int p) {} +void foo183(int p) {} +void foo184(int p) {} +void foo185(int p) {} +void foo186(int p) {} +void foo187(int p) {} +void foo188(int p) {} +void foo189(int p) {} +void foo190(int p) {} +void foo191(int p) {} +void foo192(int p) {} +void foo193(int p) {} +void foo194(int p) {} +void foo195(int p) {} +void foo196(int p) {} +void foo197(int p) {} +void foo198(int p) {} +void foo199(int p) {} +void foo200(int p) {} +void foo201(int p) {} +void foo202(int p) {} +void foo203(int p) {} +void foo204(int p) {} +void foo205(int p) {} +void foo206(int p) {} +void foo207(int p) {} +void foo208(int p) {} +void foo209(int p) {} +void foo210(int p) {} +void foo211(int p) {} +void foo212(int p) {} +void foo213(int p) {} +void foo214(int p) {} +void foo215(int p) {} +void foo216(int p) {} +void foo217(int p) {} +void foo218(int p) {} +void foo219(int p) {} +void foo220(int p) {} +void foo221(int p) {} +void foo222(int p) {} +void foo223(int p) {} +void foo224(int p) {} +void foo225(int p) {} +void foo226(int p) {} +void foo227(int p) {} +void foo228(int p) {} +void foo229(int p) {} +void foo230(int p) {} +void foo231(int p) {} +void foo232(int p) {} +void foo233(int p) {} +void foo234(int p) {} +void foo235(int p) {} +void foo236(int p) {} +void foo237(int p) {} +void foo238(int p) {} +void foo239(int p) {} +void foo240(int p) {} +void foo241(int p) {} +void foo242(int p) {} +void foo243(int p) {} +void foo244(int p) {} +void foo245(int p) {} +void foo246(int p) {} +void foo247(int p) {} +void foo248(int p) {} +void foo249(int p) {} +void foo250(int p) {} +void foo251(int p) {} +void foo252(int p) {} +void foo253(int p) {} +void foo254(int p) {} +void foo255(int p) {} +void foo256(int p) {} +void foo257(int p) {} +void foo258(int p) {} +void foo259(int p) {} +void foo260(int p) {} +void foo261(int p) {} +void foo262(int p) {} +void foo263(int p) {} +void foo264(int p) {} +void foo265(int p) {} +void foo266(int p) {} +void foo267(int p) {} +void foo268(int p) {} +void foo269(int p) {} +void foo270(int p) {} +void foo271(int p) {} +void foo272(int p) {} +void foo273(int p) {} +void foo274(int p) {} +void foo275(int p) {} +void foo276(int p) {} +void foo277(int p) {} +void foo278(int p) {} +void foo279(int p) {} +void foo280(int p) {} +void foo281(int p) {} +void foo282(int p) {} +void foo283(int p) {} +void foo284(int p) {} +void foo285(int p) {} +void foo286(int p) {} +void foo287(int p) {} +void foo288(int p) {} +void foo289(int p) {} +void foo290(int p) {} +void foo291(int p) {} +void foo292(int p) {} +void foo293(int p) {} +void foo294(int p) {} +void foo295(int p) {} +void foo296(int p) {} +void foo297(int p) {} +void foo298(int p) {} +void foo299(int p) {} +void foo300(int p) {} +void foo301(int p) {} +void foo302(int p) {} +void foo303(int p) {} +void foo304(int p) {} +void foo305(int p) {} +void foo306(int p) {} +void foo307(int p) {} +void foo308(int p) {} +void foo309(int p) {} +void foo310(int p) {} +void foo311(int p) {} +void foo312(int p) {} +void foo313(int p) {} +void foo314(int p) {} +void foo315(int p) {} +void foo316(int p) {} +void foo317(int p) {} +void foo318(int p) {} +void foo319(int p) {} +void foo320(int p) {} +void foo321(int p) {} +void foo322(int p) {} +void foo323(int p) {} +void foo324(int p) {} +void foo325(int p) {} +void foo326(int p) {} +void foo327(int p) {} +void foo328(int p) {} +void foo329(int p) {} +void foo330(int p) {} +void foo331(int p) {} +void foo332(int p) {} +void foo333(int p) {} +void foo334(int p) {} +void foo335(int p) {} +void foo336(int p) {} +void foo337(int p) {} +void foo338(int p) {} +void foo339(int p) {} +void foo340(int p) {} +void foo341(int p) {} +void foo342(int p) {} +void foo343(int p) {} +void foo344(int p) {} +void foo345(int p) {} +void foo346(int p) {} +void foo347(int p) {} +void foo348(int p) {} +void foo349(int p) {} +void foo350(int p) {} +void foo351(int p) {} +void foo352(int p) {} +void foo353(int p) {} +void foo354(int p) {} +void foo355(int p) {} +void foo356(int p) {} +void foo357(int p) {} +void foo358(int p) {} +void foo359(int p) {} +void foo360(int p) {} +void foo361(int p) {} +void foo362(int p) {} +void foo363(int p) {} +void foo364(int p) {} +void foo365(int p) {} +void foo366(int p) {} +void foo367(int p) {} +void foo368(int p) {} +void foo369(int p) {} +void foo370(int p) {} +void foo371(int p) {} +void foo372(int p) {} +void foo373(int p) {} +void foo374(int p) {} +void foo375(int p) {} +void foo376(int p) {} +void foo377(int p) {} +void foo378(int p) {} +void foo379(int p) {} +void foo380(int p) {} +void foo381(int p) {} +void foo382(int p) {} +void foo383(int p) {} +void foo384(int p) {} +void foo385(int p) {} +void foo386(int p) {} +void foo387(int p) {} +void foo388(int p) {} +void foo389(int p) {} +void foo390(int p) {} +void foo391(int p) {} +void foo392(int p) {} +void foo393(int p) {} +void foo394(int p) {} +void foo395(int p) {} +void foo396(int p) {} +void foo397(int p) {} +void foo398(int p) {} +void foo399(int p) {} +void foo400(int p) {} +void foo401(int p) {} +void foo402(int p) {} +void foo403(int p) {} +void foo404(int p) {} +void foo405(int p) {} +void foo406(int p) {} +void foo407(int p) {} +void foo408(int p) {} +void foo409(int p) {} +void foo410(int p) {} +void foo411(int p) {} +void foo412(int p) {} +void foo413(int p) {} +void foo414(int p) {} +void foo415(int p) {} +void foo416(int p) {} +void foo417(int p) {} +void foo418(int p) {} +void foo419(int p) {} +void foo420(int p) {} +void foo421(int p) {} +void foo422(int p) {} +void foo423(int p) {} +void foo424(int p) {} +void foo425(int p) {} +void foo426(int p) {} +void foo427(int p) {} +void foo428(int p) {} +void foo429(int p) {} +void foo430(int p) {} +void foo431(int p) {} +void foo432(int p) {} +void foo433(int p) {} +void foo434(int p) {} +void foo435(int p) {} +void foo436(int p) {} +void foo437(int p) {} +void foo438(int p) {} +void foo439(int p) {} +void foo440(int p) {} +void foo441(int p) {} +void foo442(int p) {} +void foo443(int p) {} +void foo444(int p) {} +void foo445(int p) {} +void foo446(int p) {} +void foo447(int p) {} +void foo448(int p) {} +void foo449(int p) {} +void foo450(int p) {} +void foo451(int p) {} +void foo452(int p) {} +void foo453(int p) {} +void foo454(int p) {} +void foo455(int p) {} +void foo456(int p) {} +void foo457(int p) {} +void foo458(int p) {} +void foo459(int p) {} +void foo460(int p) {} +void foo461(int p) {} +void foo462(int p) {} +void foo463(int p) {} +void foo464(int p) {} +void foo465(int p) {} +void foo466(int p) {} +void foo467(int p) {} +void foo468(int p) {} +void foo469(int p) {} +void foo470(int p) {} +void foo471(int p) {} +void foo472(int p) {} +void foo473(int p) {} +void foo474(int p) {} +void foo475(int p) {} +void foo476(int p) {} +void foo477(int p) {} +void foo478(int p) {} +void foo479(int p) {} +void foo480(int p) {} +void foo481(int p) {} +void foo482(int p) {} +void foo483(int p) {} +void foo484(int p) {} +void foo485(int p) {} +void foo486(int p) {} +void foo487(int p) {} +void foo488(int p) {} +void foo489(int p) {} +void foo490(int p) {} +void foo491(int p) {} +void foo492(int p) {} +void foo493(int p) {} +void foo494(int p) {} +void foo495(int p) {} +void foo496(int p) {} +void foo497(int p) {} +void foo498(int p) {} +void foo499(int p) {} +void foo500(int p) {} +void foo501(int p) {} +void foo502(int p) {} +void foo503(int p) {} +void foo504(int p) {} +void foo505(int p) {} +void foo506(int p) {} +void foo507(int p) {} +void foo508(int p) {} +void foo509(int p) {} +void foo510(int p) {} +void foo511(int p) {} +void foo512(int p) {} +void foo513(int p) {} +void foo514(int p) {} +void foo515(int p) {} +void foo516(int p) {} +void foo517(int p) {} +void foo518(int p) {} +void foo519(int p) {} +void foo520(int p) {} +void foo521(int p) {} +void foo522(int p) {} +void foo523(int p) {} +void foo524(int p) {} +void foo525(int p) {} +void foo526(int p) {} +void foo527(int p) {} +void foo528(int p) {} +void foo529(int p) {} +void foo530(int p) {} +void foo531(int p) {} +void foo532(int p) {} +void foo533(int p) {} +void foo534(int p) {} +void foo535(int p) {} +void foo536(int p) {} +void foo537(int p) {} +void foo538(int p) {} +void foo539(int p) {} +void foo540(int p) {} +void foo541(int p) {} +void foo542(int p) {} +void foo543(int p) {} +void foo544(int p) {} +void foo545(int p) {} +void foo546(int p) {} +void foo547(int p) {} +void foo548(int p) {} +void foo549(int p) {} +void foo550(int p) {} +void foo551(int p) {} +void foo552(int p) {} +void foo553(int p) {} +void foo554(int p) {} +void foo555(int p) {} +void foo556(int p) {} +void foo557(int p) {} +void foo558(int p) {} +void foo559(int p) {} +void foo560(int p) {} +void foo561(int p) {} +void foo562(int p) {} +void foo563(int p) {} +void foo564(int p) {} +void foo565(int p) {} +void foo566(int p) {} +void foo567(int p) {} +void foo568(int p) {} +void foo569(int p) {} +void foo570(int p) {} +void foo571(int p) {} +void foo572(int p) {} +void foo573(int p) {} +void foo574(int p) {} +void foo575(int p) {} +void foo576(int p) {} +void foo577(int p) {} +void foo578(int p) {} +void foo579(int p) {} +void foo580(int p) {} +void foo581(int p) {} +void foo582(int p) {} +void foo583(int p) {} +void foo584(int p) {} +void foo585(int p) {} +void foo586(int p) {} +void foo587(int p) {} +void foo588(int p) {} +void foo589(int p) {} +void foo590(int p) {} +void foo591(int p) {} +void foo592(int p) {} +void foo593(int p) {} +void foo594(int p) {} +void foo595(int p) {} +void foo596(int p) {} +void foo597(int p) {} +void foo598(int p) {} +void foo599(int p) {} +void foo600(int p) {} +void foo601(int p) {} +void foo602(int p) {} +void foo603(int p) {} +void foo604(int p) {} +void foo605(int p) {} +void foo606(int p) {} +void foo607(int p) {} +void foo608(int p) {} +void foo609(int p) {} +void foo610(int p) {} +void foo611(int p) {} +void foo612(int p) {} +void foo613(int p) {} +void foo614(int p) {} +void foo615(int p) {} +void foo616(int p) {} +void foo617(int p) {} +void foo618(int p) {} +void foo619(int p) {} +void foo620(int p) {} +void foo621(int p) {} +void foo622(int p) {} +void foo623(int p) {} +void foo624(int p) {} +void foo625(int p) {} +void foo626(int p) {} +void foo627(int p) {} +void foo628(int p) {} +void foo629(int p) {} +void foo630(int p) {} +void foo631(int p) {} +void foo632(int p) {} +void foo633(int p) {} +void foo634(int p) {} +void foo635(int p) {} +void foo636(int p) {} +void foo637(int p) {} +void foo638(int p) {} +void foo639(int p) {} +void foo640(int p) {} +void foo641(int p) {} +void foo642(int p) {} +void foo643(int p) {} +void foo644(int p) {} +void foo645(int p) {} +void foo646(int p) {} +void foo647(int p) {} +void foo648(int p) {} +void foo649(int p) {} +void foo650(int p) {} +void foo651(int p) {} +void foo652(int p) {} +void foo653(int p) {} +void foo654(int p) {} +void foo655(int p) {} +void foo656(int p) {} +void foo657(int p) {} +void foo658(int p) {} +void foo659(int p) {} +void foo660(int p) {} +void foo661(int p) {} +void foo662(int p) {} +void foo663(int p) {} +void foo664(int p) {} +void foo665(int p) {} +void foo666(int p) {} +void foo667(int p) {} +void foo668(int p) {} +void foo669(int p) {} +void foo670(int p) {} +void foo671(int p) {} +void foo672(int p) {} +void foo673(int p) {} +void foo674(int p) {} +void foo675(int p) {} +void foo676(int p) {} +void foo677(int p) {} +void foo678(int p) {} +void foo679(int p) {} +void foo680(int p) {} +void foo681(int p) {} +void foo682(int p) {} +void foo683(int p) {} +void foo684(int p) {} +void foo685(int p) {} +void foo686(int p) {} +void foo687(int p) {} +void foo688(int p) {} +void foo689(int p) {} +void foo690(int p) {} +void foo691(int p) {} +void foo692(int p) {} +void foo693(int p) {} +void foo694(int p) {} +void foo695(int p) {} +void foo696(int p) {} +void foo697(int p) {} +void foo698(int p) {} +void foo699(int p) {} +void foo700(int p) {} +void foo701(int p) {} +void foo702(int p) {} +void foo703(int p) {} +void foo704(int p) {} +void foo705(int p) {} +void foo706(int p) {} +void foo707(int p) {} +void foo708(int p) {} +void foo709(int p) {} +void foo710(int p) {} +void foo711(int p) {} +void foo712(int p) {} +void foo713(int p) {} +void foo714(int p) {} +void foo715(int p) {} +void foo716(int p) {} +void foo717(int p) {} +void foo718(int p) {} +void foo719(int p) {} +void foo720(int p) {} +void foo721(int p) {} +void foo722(int p) {} +void foo723(int p) {} +void foo724(int p) {} +void foo725(int p) {} +void foo726(int p) {} +void foo727(int p) {} +void foo728(int p) {} +void foo729(int p) {} +void foo730(int p) {} +void foo731(int p) {} +void foo732(int p) {} +void foo733(int p) {} +void foo734(int p) {} +void foo735(int p) {} +void foo736(int p) {} +void foo737(int p) {} +void foo738(int p) {} +void foo739(int p) {} +void foo740(int p) {} +void foo741(int p) {} +void foo742(int p) {} +void foo743(int p) {} +void foo744(int p) {} +void foo745(int p) {} +void foo746(int p) {} +void foo747(int p) {} +void foo748(int p) {} +void foo749(int p) {} +void foo750(int p) {} +void foo751(int p) {} +void foo752(int p) {} +void foo753(int p) {} +void foo754(int p) {} +void foo755(int p) {} +void foo756(int p) {} +void foo757(int p) {} +void foo758(int p) {} +void foo759(int p) {} +void foo760(int p) {} +void foo761(int p) {} +void foo762(int p) {} +void foo763(int p) {} +void foo764(int p) {} +void foo765(int p) {} +void foo766(int p) {} +void foo767(int p) {} +void foo768(int p) {} +void foo769(int p) {} +void foo770(int p) {} +void foo771(int p) {} +void foo772(int p) {} +void foo773(int p) {} +void foo774(int p) {} +void foo775(int p) {} +void foo776(int p) {} +void foo777(int p) {} +void foo778(int p) {} +void foo779(int p) {} +void foo780(int p) {} +void foo781(int p) {} +void foo782(int p) {} +void foo783(int p) {} +void foo784(int p) {} +void foo785(int p) {} +void foo786(int p) {} +void foo787(int p) {} +void foo788(int p) {} +void foo789(int p) {} +void foo790(int p) {} +void foo791(int p) {} +void foo792(int p) {} +void foo793(int p) {} +void foo794(int p) {} +void foo795(int p) {} +void foo796(int p) {} +void foo797(int p) {} +void foo798(int p) {} +void foo799(int p) {} +void foo800(int p) {} +void foo801(int p) {} +void foo802(int p) {} +void foo803(int p) {} +void foo804(int p) {} +void foo805(int p) {} +void foo806(int p) {} +void foo807(int p) {} +void foo808(int p) {} +void foo809(int p) {} +void foo810(int p) {} +void foo811(int p) {} +void foo812(int p) {} +void foo813(int p) {} +void foo814(int p) {} +void foo815(int p) {} +void foo816(int p) {} +void foo817(int p) {} +void foo818(int p) {} +void foo819(int p) {} +void foo820(int p) {} +void foo821(int p) {} +void foo822(int p) {} +void foo823(int p) {} +void foo824(int p) {} diff --git a/src/dyld-apple/unit-tests/test-cases/jump-table-race/foo.h b/src/dyld-apple/unit-tests/test-cases/jump-table-race/foo.h new file mode 100644 index 000000000..33a9be484 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/jump-table-race/foo.h @@ -0,0 +1,825 @@ +extern void foo000(int); +extern void foo001(int); +extern void foo002(int); +extern void foo003(int); +extern void foo004(int); +extern void foo005(int); +extern void foo006(int); +extern void foo007(int); +extern void foo008(int); +extern void foo009(int); +extern void foo010(int); +extern void foo011(int); +extern void foo012(int); +extern void foo013(int); +extern void foo014(int); +extern void foo015(int); +extern void foo016(int); +extern void foo017(int); +extern void foo018(int); +extern void foo019(int); +extern void foo020(int); +extern void foo021(int); +extern void foo022(int); +extern void foo023(int); +extern void foo024(int); +extern void foo025(int); +extern void foo026(int); +extern void foo027(int); +extern void foo028(int); +extern void foo029(int); +extern void foo030(int); +extern void foo031(int); +extern void foo032(int); +extern void foo033(int); +extern void foo034(int); +extern void foo035(int); +extern void foo036(int); +extern void foo037(int); +extern void foo038(int); +extern void foo039(int); +extern void foo040(int); +extern void foo041(int); +extern void foo042(int); +extern void foo043(int); +extern void foo044(int); +extern void foo045(int); +extern void foo046(int); +extern void foo047(int); +extern void foo048(int); +extern void foo049(int); +extern void foo050(int); +extern void foo051(int); +extern void foo052(int); +extern void foo053(int); +extern void foo054(int); +extern void foo055(int); +extern void foo056(int); +extern void foo057(int); +extern void foo058(int); +extern void foo059(int); +extern void foo060(int); +extern void foo061(int); +extern void foo062(int); +extern void foo063(int); +extern void foo064(int); +extern void foo065(int); +extern void foo066(int); +extern void foo067(int); +extern void foo068(int); +extern void foo069(int); +extern void foo070(int); +extern void foo071(int); +extern void foo072(int); +extern void foo073(int); +extern void foo074(int); +extern void foo075(int); +extern void foo076(int); +extern void foo077(int); +extern void foo078(int); +extern void foo079(int); +extern void foo080(int); +extern void foo081(int); +extern void foo082(int); +extern void foo083(int); +extern void foo084(int); +extern void foo085(int); +extern void foo086(int); +extern void foo087(int); +extern void foo088(int); +extern void foo089(int); +extern void foo090(int); +extern void foo091(int); +extern void foo092(int); +extern void foo093(int); +extern void foo094(int); +extern void foo095(int); +extern void foo096(int); +extern void foo097(int); +extern void foo098(int); +extern void foo099(int); +extern void foo100(int); +extern void foo101(int); +extern void foo102(int); +extern void foo103(int); +extern void foo104(int); +extern void foo105(int); +extern void foo106(int); +extern void foo107(int); +extern void foo108(int); +extern void foo109(int); +extern void foo110(int); +extern void foo111(int); +extern void foo112(int); +extern void foo113(int); +extern void foo114(int); +extern void foo115(int); +extern void foo116(int); +extern void foo117(int); +extern void foo118(int); +extern void foo119(int); +extern void foo120(int); +extern void foo121(int); +extern void foo122(int); +extern void foo123(int); +extern void foo124(int); +extern void foo125(int); +extern void foo126(int); +extern void foo127(int); +extern void foo128(int); +extern void foo129(int); +extern void foo130(int); +extern void foo131(int); +extern void foo132(int); +extern void foo133(int); +extern void foo134(int); +extern void foo135(int); +extern void foo136(int); +extern void foo137(int); +extern void foo138(int); +extern void foo139(int); +extern void foo140(int); +extern void foo141(int); +extern void foo142(int); +extern void foo143(int); +extern void foo144(int); +extern void foo145(int); +extern void foo146(int); +extern void foo147(int); +extern void foo148(int); +extern void foo149(int); +extern void foo150(int); +extern void foo151(int); +extern void foo152(int); +extern void foo153(int); +extern void foo154(int); +extern void foo155(int); +extern void foo156(int); +extern void foo157(int); +extern void foo158(int); +extern void foo159(int); +extern void foo160(int); +extern void foo161(int); +extern void foo162(int); +extern void foo163(int); +extern void foo164(int); +extern void foo165(int); +extern void foo166(int); +extern void foo167(int); +extern void foo168(int); +extern void foo169(int); +extern void foo170(int); +extern void foo171(int); +extern void foo172(int); +extern void foo173(int); +extern void foo174(int); +extern void foo175(int); +extern void foo176(int); +extern void foo177(int); +extern void foo178(int); +extern void foo179(int); +extern void foo180(int); +extern void foo181(int); +extern void foo182(int); +extern void foo183(int); +extern void foo184(int); +extern void foo185(int); +extern void foo186(int); +extern void foo187(int); +extern void foo188(int); +extern void foo189(int); +extern void foo190(int); +extern void foo191(int); +extern void foo192(int); +extern void foo193(int); +extern void foo194(int); +extern void foo195(int); +extern void foo196(int); +extern void foo197(int); +extern void foo198(int); +extern void foo199(int); +extern void foo200(int); +extern void foo201(int); +extern void foo202(int); +extern void foo203(int); +extern void foo204(int); +extern void foo205(int); +extern void foo206(int); +extern void foo207(int); +extern void foo208(int); +extern void foo209(int); +extern void foo210(int); +extern void foo211(int); +extern void foo212(int); +extern void foo213(int); +extern void foo214(int); +extern void foo215(int); +extern void foo216(int); +extern void foo217(int); +extern void foo218(int); +extern void foo219(int); +extern void foo220(int); +extern void foo221(int); +extern void foo222(int); +extern void foo223(int); +extern void foo224(int); +extern void foo225(int); +extern void foo226(int); +extern void foo227(int); +extern void foo228(int); +extern void foo229(int); +extern void foo230(int); +extern void foo231(int); +extern void foo232(int); +extern void foo233(int); +extern void foo234(int); +extern void foo235(int); +extern void foo236(int); +extern void foo237(int); +extern void foo238(int); +extern void foo239(int); +extern void foo240(int); +extern void foo241(int); +extern void foo242(int); +extern void foo243(int); +extern void foo244(int); +extern void foo245(int); +extern void foo246(int); +extern void foo247(int); +extern void foo248(int); +extern void foo249(int); +extern void foo250(int); +extern void foo251(int); +extern void foo252(int); +extern void foo253(int); +extern void foo254(int); +extern void foo255(int); +extern void foo256(int); +extern void foo257(int); +extern void foo258(int); +extern void foo259(int); +extern void foo260(int); +extern void foo261(int); +extern void foo262(int); +extern void foo263(int); +extern void foo264(int); +extern void foo265(int); +extern void foo266(int); +extern void foo267(int); +extern void foo268(int); +extern void foo269(int); +extern void foo270(int); +extern void foo271(int); +extern void foo272(int); +extern void foo273(int); +extern void foo274(int); +extern void foo275(int); +extern void foo276(int); +extern void foo277(int); +extern void foo278(int); +extern void foo279(int); +extern void foo280(int); +extern void foo281(int); +extern void foo282(int); +extern void foo283(int); +extern void foo284(int); +extern void foo285(int); +extern void foo286(int); +extern void foo287(int); +extern void foo288(int); +extern void foo289(int); +extern void foo290(int); +extern void foo291(int); +extern void foo292(int); +extern void foo293(int); +extern void foo294(int); +extern void foo295(int); +extern void foo296(int); +extern void foo297(int); +extern void foo298(int); +extern void foo299(int); +extern void foo300(int); +extern void foo301(int); +extern void foo302(int); +extern void foo303(int); +extern void foo304(int); +extern void foo305(int); +extern void foo306(int); +extern void foo307(int); +extern void foo308(int); +extern void foo309(int); +extern void foo310(int); +extern void foo311(int); +extern void foo312(int); +extern void foo313(int); +extern void foo314(int); +extern void foo315(int); +extern void foo316(int); +extern void foo317(int); +extern void foo318(int); +extern void foo319(int); +extern void foo320(int); +extern void foo321(int); +extern void foo322(int); +extern void foo323(int); +extern void foo324(int); +extern void foo325(int); +extern void foo326(int); +extern void foo327(int); +extern void foo328(int); +extern void foo329(int); +extern void foo330(int); +extern void foo331(int); +extern void foo332(int); +extern void foo333(int); +extern void foo334(int); +extern void foo335(int); +extern void foo336(int); +extern void foo337(int); +extern void foo338(int); +extern void foo339(int); +extern void foo340(int); +extern void foo341(int); +extern void foo342(int); +extern void foo343(int); +extern void foo344(int); +extern void foo345(int); +extern void foo346(int); +extern void foo347(int); +extern void foo348(int); +extern void foo349(int); +extern void foo350(int); +extern void foo351(int); +extern void foo352(int); +extern void foo353(int); +extern void foo354(int); +extern void foo355(int); +extern void foo356(int); +extern void foo357(int); +extern void foo358(int); +extern void foo359(int); +extern void foo360(int); +extern void foo361(int); +extern void foo362(int); +extern void foo363(int); +extern void foo364(int); +extern void foo365(int); +extern void foo366(int); +extern void foo367(int); +extern void foo368(int); +extern void foo369(int); +extern void foo370(int); +extern void foo371(int); +extern void foo372(int); +extern void foo373(int); +extern void foo374(int); +extern void foo375(int); +extern void foo376(int); +extern void foo377(int); +extern void foo378(int); +extern void foo379(int); +extern void foo380(int); +extern void foo381(int); +extern void foo382(int); +extern void foo383(int); +extern void foo384(int); +extern void foo385(int); +extern void foo386(int); +extern void foo387(int); +extern void foo388(int); +extern void foo389(int); +extern void foo390(int); +extern void foo391(int); +extern void foo392(int); +extern void foo393(int); +extern void foo394(int); +extern void foo395(int); +extern void foo396(int); +extern void foo397(int); +extern void foo398(int); +extern void foo399(int); +extern void foo400(int); +extern void foo401(int); +extern void foo402(int); +extern void foo403(int); +extern void foo404(int); +extern void foo405(int); +extern void foo406(int); +extern void foo407(int); +extern void foo408(int); +extern void foo409(int); +extern void foo410(int); +extern void foo411(int); +extern void foo412(int); +extern void foo413(int); +extern void foo414(int); +extern void foo415(int); +extern void foo416(int); +extern void foo417(int); +extern void foo418(int); +extern void foo419(int); +extern void foo420(int); +extern void foo421(int); +extern void foo422(int); +extern void foo423(int); +extern void foo424(int); +extern void foo425(int); +extern void foo426(int); +extern void foo427(int); +extern void foo428(int); +extern void foo429(int); +extern void foo430(int); +extern void foo431(int); +extern void foo432(int); +extern void foo433(int); +extern void foo434(int); +extern void foo435(int); +extern void foo436(int); +extern void foo437(int); +extern void foo438(int); +extern void foo439(int); +extern void foo440(int); +extern void foo441(int); +extern void foo442(int); +extern void foo443(int); +extern void foo444(int); +extern void foo445(int); +extern void foo446(int); +extern void foo447(int); +extern void foo448(int); +extern void foo449(int); +extern void foo450(int); +extern void foo451(int); +extern void foo452(int); +extern void foo453(int); +extern void foo454(int); +extern void foo455(int); +extern void foo456(int); +extern void foo457(int); +extern void foo458(int); +extern void foo459(int); +extern void foo460(int); +extern void foo461(int); +extern void foo462(int); +extern void foo463(int); +extern void foo464(int); +extern void foo465(int); +extern void foo466(int); +extern void foo467(int); +extern void foo468(int); +extern void foo469(int); +extern void foo470(int); +extern void foo471(int); +extern void foo472(int); +extern void foo473(int); +extern void foo474(int); +extern void foo475(int); +extern void foo476(int); +extern void foo477(int); +extern void foo478(int); +extern void foo479(int); +extern void foo480(int); +extern void foo481(int); +extern void foo482(int); +extern void foo483(int); +extern void foo484(int); +extern void foo485(int); +extern void foo486(int); +extern void foo487(int); +extern void foo488(int); +extern void foo489(int); +extern void foo490(int); +extern void foo491(int); +extern void foo492(int); +extern void foo493(int); +extern void foo494(int); +extern void foo495(int); +extern void foo496(int); +extern void foo497(int); +extern void foo498(int); +extern void foo499(int); +extern void foo500(int); +extern void foo501(int); +extern void foo502(int); +extern void foo503(int); +extern void foo504(int); +extern void foo505(int); +extern void foo506(int); +extern void foo507(int); +extern void foo508(int); +extern void foo509(int); +extern void foo510(int); +extern void foo511(int); +extern void foo512(int); +extern void foo513(int); +extern void foo514(int); +extern void foo515(int); +extern void foo516(int); +extern void foo517(int); +extern void foo518(int); +extern void foo519(int); +extern void foo520(int); +extern void foo521(int); +extern void foo522(int); +extern void foo523(int); +extern void foo524(int); +extern void foo525(int); +extern void foo526(int); +extern void foo527(int); +extern void foo528(int); +extern void foo529(int); +extern void foo530(int); +extern void foo531(int); +extern void foo532(int); +extern void foo533(int); +extern void foo534(int); +extern void foo535(int); +extern void foo536(int); +extern void foo537(int); +extern void foo538(int); +extern void foo539(int); +extern void foo540(int); +extern void foo541(int); +extern void foo542(int); +extern void foo543(int); +extern void foo544(int); +extern void foo545(int); +extern void foo546(int); +extern void foo547(int); +extern void foo548(int); +extern void foo549(int); +extern void foo550(int); +extern void foo551(int); +extern void foo552(int); +extern void foo553(int); +extern void foo554(int); +extern void foo555(int); +extern void foo556(int); +extern void foo557(int); +extern void foo558(int); +extern void foo559(int); +extern void foo560(int); +extern void foo561(int); +extern void foo562(int); +extern void foo563(int); +extern void foo564(int); +extern void foo565(int); +extern void foo566(int); +extern void foo567(int); +extern void foo568(int); +extern void foo569(int); +extern void foo570(int); +extern void foo571(int); +extern void foo572(int); +extern void foo573(int); +extern void foo574(int); +extern void foo575(int); +extern void foo576(int); +extern void foo577(int); +extern void foo578(int); +extern void foo579(int); +extern void foo580(int); +extern void foo581(int); +extern void foo582(int); +extern void foo583(int); +extern void foo584(int); +extern void foo585(int); +extern void foo586(int); +extern void foo587(int); +extern void foo588(int); +extern void foo589(int); +extern void foo590(int); +extern void foo591(int); +extern void foo592(int); +extern void foo593(int); +extern void foo594(int); +extern void foo595(int); +extern void foo596(int); +extern void foo597(int); +extern void foo598(int); +extern void foo599(int); +extern void foo600(int); +extern void foo601(int); +extern void foo602(int); +extern void foo603(int); +extern void foo604(int); +extern void foo605(int); +extern void foo606(int); +extern void foo607(int); +extern void foo608(int); +extern void foo609(int); +extern void foo610(int); +extern void foo611(int); +extern void foo612(int); +extern void foo613(int); +extern void foo614(int); +extern void foo615(int); +extern void foo616(int); +extern void foo617(int); +extern void foo618(int); +extern void foo619(int); +extern void foo620(int); +extern void foo621(int); +extern void foo622(int); +extern void foo623(int); +extern void foo624(int); +extern void foo625(int); +extern void foo626(int); +extern void foo627(int); +extern void foo628(int); +extern void foo629(int); +extern void foo630(int); +extern void foo631(int); +extern void foo632(int); +extern void foo633(int); +extern void foo634(int); +extern void foo635(int); +extern void foo636(int); +extern void foo637(int); +extern void foo638(int); +extern void foo639(int); +extern void foo640(int); +extern void foo641(int); +extern void foo642(int); +extern void foo643(int); +extern void foo644(int); +extern void foo645(int); +extern void foo646(int); +extern void foo647(int); +extern void foo648(int); +extern void foo649(int); +extern void foo650(int); +extern void foo651(int); +extern void foo652(int); +extern void foo653(int); +extern void foo654(int); +extern void foo655(int); +extern void foo656(int); +extern void foo657(int); +extern void foo658(int); +extern void foo659(int); +extern void foo660(int); +extern void foo661(int); +extern void foo662(int); +extern void foo663(int); +extern void foo664(int); +extern void foo665(int); +extern void foo666(int); +extern void foo667(int); +extern void foo668(int); +extern void foo669(int); +extern void foo670(int); +extern void foo671(int); +extern void foo672(int); +extern void foo673(int); +extern void foo674(int); +extern void foo675(int); +extern void foo676(int); +extern void foo677(int); +extern void foo678(int); +extern void foo679(int); +extern void foo680(int); +extern void foo681(int); +extern void foo682(int); +extern void foo683(int); +extern void foo684(int); +extern void foo685(int); +extern void foo686(int); +extern void foo687(int); +extern void foo688(int); +extern void foo689(int); +extern void foo690(int); +extern void foo691(int); +extern void foo692(int); +extern void foo693(int); +extern void foo694(int); +extern void foo695(int); +extern void foo696(int); +extern void foo697(int); +extern void foo698(int); +extern void foo699(int); +extern void foo700(int); +extern void foo701(int); +extern void foo702(int); +extern void foo703(int); +extern void foo704(int); +extern void foo705(int); +extern void foo706(int); +extern void foo707(int); +extern void foo708(int); +extern void foo709(int); +extern void foo710(int); +extern void foo711(int); +extern void foo712(int); +extern void foo713(int); +extern void foo714(int); +extern void foo715(int); +extern void foo716(int); +extern void foo717(int); +extern void foo718(int); +extern void foo719(int); +extern void foo720(int); +extern void foo721(int); +extern void foo722(int); +extern void foo723(int); +extern void foo724(int); +extern void foo725(int); +extern void foo726(int); +extern void foo727(int); +extern void foo728(int); +extern void foo729(int); +extern void foo730(int); +extern void foo731(int); +extern void foo732(int); +extern void foo733(int); +extern void foo734(int); +extern void foo735(int); +extern void foo736(int); +extern void foo737(int); +extern void foo738(int); +extern void foo739(int); +extern void foo740(int); +extern void foo741(int); +extern void foo742(int); +extern void foo743(int); +extern void foo744(int); +extern void foo745(int); +extern void foo746(int); +extern void foo747(int); +extern void foo748(int); +extern void foo749(int); +extern void foo750(int); +extern void foo751(int); +extern void foo752(int); +extern void foo753(int); +extern void foo754(int); +extern void foo755(int); +extern void foo756(int); +extern void foo757(int); +extern void foo758(int); +extern void foo759(int); +extern void foo760(int); +extern void foo761(int); +extern void foo762(int); +extern void foo763(int); +extern void foo764(int); +extern void foo765(int); +extern void foo766(int); +extern void foo767(int); +extern void foo768(int); +extern void foo769(int); +extern void foo770(int); +extern void foo771(int); +extern void foo772(int); +extern void foo773(int); +extern void foo774(int); +extern void foo775(int); +extern void foo776(int); +extern void foo777(int); +extern void foo778(int); +extern void foo779(int); +extern void foo780(int); +extern void foo781(int); +extern void foo782(int); +extern void foo783(int); +extern void foo784(int); +extern void foo785(int); +extern void foo786(int); +extern void foo787(int); +extern void foo788(int); +extern void foo789(int); +extern void foo790(int); +extern void foo791(int); +extern void foo792(int); +extern void foo793(int); +extern void foo794(int); +extern void foo795(int); +extern void foo796(int); +extern void foo797(int); +extern void foo798(int); +extern void foo799(int); +extern void foo800(int); +extern void foo801(int); +extern void foo802(int); +extern void foo803(int); +extern void foo804(int); +extern void foo805(int); +extern void foo806(int); +extern void foo807(int); +extern void foo808(int); +extern void foo809(int); +extern void foo810(int); +extern void foo811(int); +extern void foo812(int); +extern void foo813(int); +extern void foo814(int); +extern void foo815(int); +extern void foo816(int); +extern void foo817(int); +extern void foo818(int); +extern void foo819(int); +extern void foo820(int); +extern void foo821(int); +extern void foo822(int); +extern void foo823(int); +extern void foo824(int); diff --git a/src/dyld-apple/unit-tests/test-cases/jump-table-race/main.c b/src/dyld-apple/unit-tests/test-cases/jump-table-race/main.c new file mode 100644 index 000000000..202fe28b0 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/jump-table-race/main.c @@ -0,0 +1,865 @@ +/* + * 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@ + */ +#include // fprintf(0x12345678), NULL +#include // exit(0x12345678), EXIT_SUCCESS +#include + +#include "test.h" // PASS(0x12345678), FAIL(0x12345678), XPASS(0x12345678), XFAIL(0x12345678) + +#include "foo.h" + +static void* callAll(void* p) +{ + foo002(0x12345678); + foo003(0x12345678); + foo004(0x12345678); + foo005(0x12345678); + foo006(0x12345678); + foo007(0x12345678); + foo008(0x12345678); + foo009(0x12345678); + foo010(0x12345678); + foo011(0x12345678); + foo012(0x12345678); + foo013(0x12345678); + foo014(0x12345678); + foo015(0x12345678); + foo016(0x12345678); + foo017(0x12345678); + foo018(0x12345678); + foo019(0x12345678); + foo020(0x12345678); + foo021(0x12345678); + foo022(0x12345678); + foo023(0x12345678); + foo024(0x12345678); + foo025(0x12345678); + foo026(0x12345678); + foo027(0x12345678); + foo028(0x12345678); + foo029(0x12345678); + foo030(0x12345678); + foo031(0x12345678); + foo032(0x12345678); + foo033(0x12345678); + foo034(0x12345678); + foo035(0x12345678); + foo036(0x12345678); + foo037(0x12345678); + foo038(0x12345678); + foo039(0x12345678); + foo040(0x12345678); + foo041(0x12345678); + foo042(0x12345678); + foo043(0x12345678); + foo044(0x12345678); + foo045(0x12345678); + foo046(0x12345678); + foo047(0x12345678); + foo048(0x12345678); + foo049(0x12345678); + foo050(0x12345678); + foo051(0x12345678); + foo052(0x12345678); + foo053(0x12345678); + foo054(0x12345678); + foo055(0x12345678); + foo056(0x12345678); + foo057(0x12345678); + foo058(0x12345678); + foo059(0x12345678); + foo060(0x12345678); + foo061(0x12345678); + foo062(0x12345678); + foo063(0x12345678); + foo064(0x12345678); + foo065(0x12345678); + foo066(0x12345678); + foo067(0x12345678); + foo068(0x12345678); + foo069(0x12345678); + foo070(0x12345678); + foo071(0x12345678); + foo072(0x12345678); + foo073(0x12345678); + foo074(0x12345678); + foo075(0x12345678); + foo076(0x12345678); + foo077(0x12345678); + foo078(0x12345678); + foo079(0x12345678); + foo080(0x12345678); + foo081(0x12345678); + foo082(0x12345678); + foo083(0x12345678); + foo084(0x12345678); + foo085(0x12345678); + foo086(0x12345678); + foo087(0x12345678); + foo088(0x12345678); + foo089(0x12345678); + foo090(0x12345678); + foo091(0x12345678); + foo092(0x12345678); + foo093(0x12345678); + foo094(0x12345678); + foo095(0x12345678); + foo096(0x12345678); + foo097(0x12345678); + foo098(0x12345678); + foo099(0x12345678); + foo100(0x12345678); + foo101(0x12345678); + foo102(0x12345678); + foo103(0x12345678); + foo104(0x12345678); + foo105(0x12345678); + foo106(0x12345678); + foo107(0x12345678); + foo108(0x12345678); + foo109(0x12345678); + foo110(0x12345678); + foo111(0x12345678); + foo112(0x12345678); + foo113(0x12345678); + foo114(0x12345678); + foo115(0x12345678); + foo116(0x12345678); + foo117(0x12345678); + foo118(0x12345678); + foo119(0x12345678); + foo120(0x12345678); + foo121(0x12345678); + foo122(0x12345678); + foo123(0x12345678); + foo124(0x12345678); + foo125(0x12345678); + foo126(0x12345678); + foo127(0x12345678); + foo128(0x12345678); + foo129(0x12345678); + foo130(0x12345678); + foo131(0x12345678); + foo132(0x12345678); + foo133(0x12345678); + foo134(0x12345678); + foo135(0x12345678); + foo136(0x12345678); + foo137(0x12345678); + foo138(0x12345678); + foo139(0x12345678); + foo140(0x12345678); + foo141(0x12345678); + foo142(0x12345678); + foo143(0x12345678); + foo144(0x12345678); + foo145(0x12345678); + foo146(0x12345678); + foo147(0x12345678); + foo148(0x12345678); + foo149(0x12345678); + foo150(0x12345678); + foo151(0x12345678); + foo152(0x12345678); + foo153(0x12345678); + foo154(0x12345678); + foo155(0x12345678); + foo156(0x12345678); + foo157(0x12345678); + foo158(0x12345678); + foo159(0x12345678); + foo160(0x12345678); + foo161(0x12345678); + foo162(0x12345678); + foo163(0x12345678); + foo164(0x12345678); + foo165(0x12345678); + foo166(0x12345678); + foo167(0x12345678); + foo168(0x12345678); + foo169(0x12345678); + foo170(0x12345678); + foo171(0x12345678); + foo172(0x12345678); + foo173(0x12345678); + foo174(0x12345678); + foo175(0x12345678); + foo176(0x12345678); + foo177(0x12345678); + foo178(0x12345678); + foo179(0x12345678); + foo180(0x12345678); + foo181(0x12345678); + foo182(0x12345678); + foo183(0x12345678); + foo184(0x12345678); + foo185(0x12345678); + foo186(0x12345678); + foo187(0x12345678); + foo188(0x12345678); + foo189(0x12345678); + foo190(0x12345678); + foo191(0x12345678); + foo192(0x12345678); + foo193(0x12345678); + foo194(0x12345678); + foo195(0x12345678); + foo196(0x12345678); + foo197(0x12345678); + foo198(0x12345678); + foo199(0x12345678); + foo200(0x12345678); + foo201(0x12345678); + foo202(0x12345678); + foo203(0x12345678); + foo204(0x12345678); + foo205(0x12345678); + foo206(0x12345678); + foo207(0x12345678); + foo208(0x12345678); + foo209(0x12345678); + foo210(0x12345678); + foo211(0x12345678); + foo212(0x12345678); + foo213(0x12345678); + foo214(0x12345678); + foo215(0x12345678); + foo216(0x12345678); + foo217(0x12345678); + foo218(0x12345678); + foo219(0x12345678); + foo220(0x12345678); + foo221(0x12345678); + foo222(0x12345678); + foo223(0x12345678); + foo224(0x12345678); + foo225(0x12345678); + foo226(0x12345678); + foo227(0x12345678); + foo228(0x12345678); + foo229(0x12345678); + foo230(0x12345678); + foo231(0x12345678); + foo232(0x12345678); + foo233(0x12345678); + foo234(0x12345678); + foo235(0x12345678); + foo236(0x12345678); + foo237(0x12345678); + foo238(0x12345678); + foo239(0x12345678); + foo240(0x12345678); + foo241(0x12345678); + foo242(0x12345678); + foo243(0x12345678); + foo244(0x12345678); + foo245(0x12345678); + foo246(0x12345678); + foo247(0x12345678); + foo248(0x12345678); + foo249(0x12345678); + foo250(0x12345678); + foo251(0x12345678); + foo252(0x12345678); + foo253(0x12345678); + foo254(0x12345678); + foo255(0x12345678); + foo256(0x12345678); + foo257(0x12345678); + foo258(0x12345678); + foo259(0x12345678); + foo260(0x12345678); + foo261(0x12345678); + foo262(0x12345678); + foo263(0x12345678); + foo264(0x12345678); + foo265(0x12345678); + foo266(0x12345678); + foo267(0x12345678); + foo268(0x12345678); + foo269(0x12345678); + foo270(0x12345678); + foo271(0x12345678); + foo272(0x12345678); + foo273(0x12345678); + foo274(0x12345678); + foo275(0x12345678); + foo276(0x12345678); + foo277(0x12345678); + foo278(0x12345678); + foo279(0x12345678); + foo280(0x12345678); + foo281(0x12345678); + foo282(0x12345678); + foo283(0x12345678); + foo284(0x12345678); + foo285(0x12345678); + foo286(0x12345678); + foo287(0x12345678); + foo288(0x12345678); + foo289(0x12345678); + foo290(0x12345678); + foo291(0x12345678); + foo292(0x12345678); + foo293(0x12345678); + foo294(0x12345678); + foo295(0x12345678); + foo296(0x12345678); + foo297(0x12345678); + foo298(0x12345678); + foo299(0x12345678); + foo300(0x12345678); + foo301(0x12345678); + foo302(0x12345678); + foo303(0x12345678); + foo304(0x12345678); + foo305(0x12345678); + foo306(0x12345678); + foo307(0x12345678); + foo308(0x12345678); + foo309(0x12345678); + foo310(0x12345678); + foo311(0x12345678); + foo312(0x12345678); + foo313(0x12345678); + foo314(0x12345678); + foo315(0x12345678); + foo316(0x12345678); + foo317(0x12345678); + foo318(0x12345678); + foo319(0x12345678); + foo320(0x12345678); + foo321(0x12345678); + foo322(0x12345678); + foo323(0x12345678); + foo324(0x12345678); + foo325(0x12345678); + foo326(0x12345678); + foo327(0x12345678); + foo328(0x12345678); + foo329(0x12345678); + foo330(0x12345678); + foo331(0x12345678); + foo332(0x12345678); + foo333(0x12345678); + foo334(0x12345678); + foo335(0x12345678); + foo336(0x12345678); + foo337(0x12345678); + foo338(0x12345678); + foo339(0x12345678); + foo340(0x12345678); + foo341(0x12345678); + foo342(0x12345678); + foo343(0x12345678); + foo344(0x12345678); + foo345(0x12345678); + foo346(0x12345678); + foo347(0x12345678); + foo348(0x12345678); + foo349(0x12345678); + foo350(0x12345678); + foo351(0x12345678); + foo352(0x12345678); + foo353(0x12345678); + foo354(0x12345678); + foo355(0x12345678); + foo356(0x12345678); + foo357(0x12345678); + foo358(0x12345678); + foo359(0x12345678); + foo360(0x12345678); + foo361(0x12345678); + foo362(0x12345678); + foo363(0x12345678); + foo364(0x12345678); + foo365(0x12345678); + foo366(0x12345678); + foo367(0x12345678); + foo368(0x12345678); + foo369(0x12345678); + foo370(0x12345678); + foo371(0x12345678); + foo372(0x12345678); + foo373(0x12345678); + foo374(0x12345678); + foo375(0x12345678); + foo376(0x12345678); + foo377(0x12345678); + foo378(0x12345678); + foo379(0x12345678); + foo380(0x12345678); + foo381(0x12345678); + foo382(0x12345678); + foo383(0x12345678); + foo384(0x12345678); + foo385(0x12345678); + foo386(0x12345678); + foo387(0x12345678); + foo388(0x12345678); + foo389(0x12345678); + foo390(0x12345678); + foo391(0x12345678); + foo392(0x12345678); + foo393(0x12345678); + foo394(0x12345678); + foo395(0x12345678); + foo396(0x12345678); + foo397(0x12345678); + foo398(0x12345678); + foo399(0x12345678); + foo400(0x12345678); + foo401(0x12345678); + foo402(0x12345678); + foo403(0x12345678); + foo404(0x12345678); + foo405(0x12345678); + foo406(0x12345678); + foo407(0x12345678); + foo408(0x12345678); + foo409(0x12345678); + foo410(0x12345678); + foo411(0x12345678); + foo412(0x12345678); + foo413(0x12345678); + foo414(0x12345678); + foo415(0x12345678); + foo416(0x12345678); + foo417(0x12345678); + foo418(0x12345678); + foo419(0x12345678); + foo420(0x12345678); + foo421(0x12345678); + foo422(0x12345678); + foo423(0x12345678); + foo424(0x12345678); + foo425(0x12345678); + foo426(0x12345678); + foo427(0x12345678); + foo428(0x12345678); + foo429(0x12345678); + foo430(0x12345678); + foo431(0x12345678); + foo432(0x12345678); + foo433(0x12345678); + foo434(0x12345678); + foo435(0x12345678); + foo436(0x12345678); + foo437(0x12345678); + foo438(0x12345678); + foo439(0x12345678); + foo440(0x12345678); + foo441(0x12345678); + foo442(0x12345678); + foo443(0x12345678); + foo444(0x12345678); + foo445(0x12345678); + foo446(0x12345678); + foo447(0x12345678); + foo448(0x12345678); + foo449(0x12345678); + foo450(0x12345678); + foo451(0x12345678); + foo452(0x12345678); + foo453(0x12345678); + foo454(0x12345678); + foo455(0x12345678); + foo456(0x12345678); + foo457(0x12345678); + foo458(0x12345678); + foo459(0x12345678); + foo460(0x12345678); + foo461(0x12345678); + foo462(0x12345678); + foo463(0x12345678); + foo464(0x12345678); + foo465(0x12345678); + foo466(0x12345678); + foo467(0x12345678); + foo468(0x12345678); + foo469(0x12345678); + foo470(0x12345678); + foo471(0x12345678); + foo472(0x12345678); + foo473(0x12345678); + foo474(0x12345678); + foo475(0x12345678); + foo476(0x12345678); + foo477(0x12345678); + foo478(0x12345678); + foo479(0x12345678); + foo480(0x12345678); + foo481(0x12345678); + foo482(0x12345678); + foo483(0x12345678); + foo484(0x12345678); + foo485(0x12345678); + foo486(0x12345678); + foo487(0x12345678); + foo488(0x12345678); + foo489(0x12345678); + foo490(0x12345678); + foo491(0x12345678); + foo492(0x12345678); + foo493(0x12345678); + foo494(0x12345678); + foo495(0x12345678); + foo496(0x12345678); + foo497(0x12345678); + foo498(0x12345678); + foo499(0x12345678); + foo500(0x12345678); + foo501(0x12345678); + foo502(0x12345678); + foo503(0x12345678); + foo504(0x12345678); + foo505(0x12345678); + foo506(0x12345678); + foo507(0x12345678); + foo508(0x12345678); + foo509(0x12345678); + foo510(0x12345678); + foo511(0x12345678); + foo512(0x12345678); + foo513(0x12345678); + foo514(0x12345678); + foo515(0x12345678); + foo516(0x12345678); + foo517(0x12345678); + foo518(0x12345678); + foo519(0x12345678); + foo520(0x12345678); + foo521(0x12345678); + foo522(0x12345678); + foo523(0x12345678); + foo524(0x12345678); + foo525(0x12345678); + foo526(0x12345678); + foo527(0x12345678); + foo528(0x12345678); + foo529(0x12345678); + foo530(0x12345678); + foo531(0x12345678); + foo532(0x12345678); + foo533(0x12345678); + foo534(0x12345678); + foo535(0x12345678); + foo536(0x12345678); + foo537(0x12345678); + foo538(0x12345678); + foo539(0x12345678); + foo540(0x12345678); + foo541(0x12345678); + foo542(0x12345678); + foo543(0x12345678); + foo544(0x12345678); + foo545(0x12345678); + foo546(0x12345678); + foo547(0x12345678); + foo548(0x12345678); + foo549(0x12345678); + foo550(0x12345678); + foo551(0x12345678); + foo552(0x12345678); + foo553(0x12345678); + foo554(0x12345678); + foo555(0x12345678); + foo556(0x12345678); + foo557(0x12345678); + foo558(0x12345678); + foo559(0x12345678); + foo560(0x12345678); + foo561(0x12345678); + foo562(0x12345678); + foo563(0x12345678); + foo564(0x12345678); + foo565(0x12345678); + foo566(0x12345678); + foo567(0x12345678); + foo568(0x12345678); + foo569(0x12345678); + foo570(0x12345678); + foo571(0x12345678); + foo572(0x12345678); + foo573(0x12345678); + foo574(0x12345678); + foo575(0x12345678); + foo576(0x12345678); + foo577(0x12345678); + foo578(0x12345678); + foo579(0x12345678); + foo580(0x12345678); + foo581(0x12345678); + foo582(0x12345678); + foo583(0x12345678); + foo584(0x12345678); + foo585(0x12345678); + foo586(0x12345678); + foo587(0x12345678); + foo588(0x12345678); + foo589(0x12345678); + foo590(0x12345678); + foo591(0x12345678); + foo592(0x12345678); + foo593(0x12345678); + foo594(0x12345678); + foo595(0x12345678); + foo596(0x12345678); + foo597(0x12345678); + foo598(0x12345678); + foo599(0x12345678); + foo600(0x12345678); + foo601(0x12345678); + foo602(0x12345678); + foo603(0x12345678); + foo604(0x12345678); + foo605(0x12345678); + foo606(0x12345678); + foo607(0x12345678); + foo608(0x12345678); + foo609(0x12345678); + foo610(0x12345678); + foo611(0x12345678); + foo612(0x12345678); + foo613(0x12345678); + foo614(0x12345678); + foo615(0x12345678); + foo616(0x12345678); + foo617(0x12345678); + foo618(0x12345678); + foo619(0x12345678); + foo620(0x12345678); + foo621(0x12345678); + foo622(0x12345678); + foo623(0x12345678); + foo624(0x12345678); + foo625(0x12345678); + foo626(0x12345678); + foo627(0x12345678); + foo628(0x12345678); + foo629(0x12345678); + foo630(0x12345678); + foo631(0x12345678); + foo632(0x12345678); + foo633(0x12345678); + foo634(0x12345678); + foo635(0x12345678); + foo636(0x12345678); + foo637(0x12345678); + foo638(0x12345678); + foo639(0x12345678); + foo640(0x12345678); + foo641(0x12345678); + foo642(0x12345678); + foo643(0x12345678); + foo644(0x12345678); + foo645(0x12345678); + foo646(0x12345678); + foo647(0x12345678); + foo648(0x12345678); + foo649(0x12345678); + foo650(0x12345678); + foo651(0x12345678); + foo652(0x12345678); + foo653(0x12345678); + foo654(0x12345678); + foo655(0x12345678); + foo656(0x12345678); + foo657(0x12345678); + foo658(0x12345678); + foo659(0x12345678); + foo660(0x12345678); + foo661(0x12345678); + foo662(0x12345678); + foo663(0x12345678); + foo664(0x12345678); + foo665(0x12345678); + foo666(0x12345678); + foo667(0x12345678); + foo668(0x12345678); + foo669(0x12345678); + foo670(0x12345678); + foo671(0x12345678); + foo672(0x12345678); + foo673(0x12345678); + foo674(0x12345678); + foo675(0x12345678); + foo676(0x12345678); + foo677(0x12345678); + foo678(0x12345678); + foo679(0x12345678); + foo680(0x12345678); + foo681(0x12345678); + foo682(0x12345678); + foo683(0x12345678); + foo684(0x12345678); + foo685(0x12345678); + foo686(0x12345678); + foo687(0x12345678); + foo688(0x12345678); + foo689(0x12345678); + foo690(0x12345678); + foo691(0x12345678); + foo692(0x12345678); + foo693(0x12345678); + foo694(0x12345678); + foo695(0x12345678); + foo696(0x12345678); + foo697(0x12345678); + foo698(0x12345678); + foo699(0x12345678); + foo700(0x12345678); + foo701(0x12345678); + foo702(0x12345678); + foo703(0x12345678); + foo704(0x12345678); + foo705(0x12345678); + foo706(0x12345678); + foo707(0x12345678); + foo708(0x12345678); + foo709(0x12345678); + foo710(0x12345678); + foo711(0x12345678); + foo712(0x12345678); + foo713(0x12345678); + foo714(0x12345678); + foo715(0x12345678); + foo716(0x12345678); + foo717(0x12345678); + foo718(0x12345678); + foo719(0x12345678); + foo720(0x12345678); + foo721(0x12345678); + foo722(0x12345678); + foo723(0x12345678); + foo724(0x12345678); + foo725(0x12345678); + foo726(0x12345678); + foo727(0x12345678); + foo728(0x12345678); + foo729(0x12345678); + foo730(0x12345678); + foo731(0x12345678); + foo732(0x12345678); + foo733(0x12345678); + foo734(0x12345678); + foo735(0x12345678); + foo736(0x12345678); + foo737(0x12345678); + foo738(0x12345678); + foo739(0x12345678); + foo740(0x12345678); + foo741(0x12345678); + foo742(0x12345678); + foo743(0x12345678); + foo744(0x12345678); + foo745(0x12345678); + foo746(0x12345678); + foo747(0x12345678); + foo748(0x12345678); + foo749(0x12345678); + foo750(0x12345678); + foo751(0x12345678); + foo752(0x12345678); + foo753(0x12345678); + foo754(0x12345678); + foo755(0x12345678); + foo756(0x12345678); + foo757(0x12345678); + foo758(0x12345678); + foo759(0x12345678); + foo760(0x12345678); + foo761(0x12345678); + foo762(0x12345678); + foo763(0x12345678); + foo764(0x12345678); + foo765(0x12345678); + foo766(0x12345678); + foo767(0x12345678); + foo768(0x12345678); + foo769(0x12345678); + foo770(0x12345678); + foo771(0x12345678); + foo772(0x12345678); + foo773(0x12345678); + foo774(0x12345678); + foo775(0x12345678); + foo776(0x12345678); + foo777(0x12345678); + foo778(0x12345678); + foo779(0x12345678); + foo780(0x12345678); + foo781(0x12345678); + foo782(0x12345678); + foo783(0x12345678); + foo784(0x12345678); + foo785(0x12345678); + foo786(0x12345678); + foo787(0x12345678); + foo788(0x12345678); + foo789(0x12345678); + foo790(0x12345678); + foo791(0x12345678); + foo792(0x12345678); + foo793(0x12345678); + foo794(0x12345678); + foo795(0x12345678); + foo796(0x12345678); + foo797(0x12345678); + foo798(0x12345678); + foo799(0x12345678); + foo800(0x12345678); + foo801(0x12345678); + foo802(0x12345678); + foo803(0x12345678); + foo804(0x12345678); + foo805(0x12345678); + foo806(0x12345678); + foo807(0x12345678); + foo808(0x12345678); + foo809(0x12345678); + foo810(0x12345678); + foo811(0x12345678); + foo812(0x12345678); + foo813(0x12345678); + foo814(0x12345678); + foo815(0x12345678); + foo816(0x12345678); + foo817(0x12345678); + return NULL; +} + +int main() +{ + // call all foo's on another thread + pthread_t otherThread; + pthread_create(&otherThread, NULL, &callAll, NULL); + + // call all foo's on this thread + callAll(NULL); + + PASS("jump-table-race"); + return EXIT_SUCCESS; +} + + diff --git a/src/dyld-apple/unit-tests/test-cases/lazy-binding-reg-params/foo.c b/src/dyld-apple/unit-tests/test-cases/lazy-binding-reg-params/foo.c new file mode 100644 index 000000000..e2683b398 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/lazy-binding-reg-params/foo.c @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2005 Apple Computer p1, 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@ + */ +#include +#include "foo.h" + + +#if __i386__ +__attribute__((regparm(3))) +#endif +bool dointtest(int p1, int p2, int p3, int p4, int p5) +{ + if ( p1 != 123) + return false; + if ( p2 != 456) + return false; + if ( p3 != 789) + return false; + if ( p4 != 4444) + return false; + if ( p5 != 55555) + return false; + return true; +} + +#if __ppc__ || __ppc64__ +bool dofloattest(double p1, double p2, double p3, double p4, double p5, double p6, double p7, + double p8, double p9, double p10, double p11, double p12, double p13) +{ + if ( p1 != 1.0 ) + return false; + if ( p2 != 2.0 ) + return false; + if ( p3 != 3.0 ) + return false; + if ( p4 != 4.0 ) + return false; + if ( p5 != 5.0 ) + return false; + if ( p6 != 6.0 ) + return false; + if ( p7 != 7.0 ) + return false; + if ( p8 != 8.0 ) + return false; + if ( p9 != 9.0 ) + return false; + if ( p10 != 10.0) + return false; + if ( p11 != 11.0) + return false; + if ( p12 != 12.0) + return false; + if ( p13 != 13.0) + return false; + return true; +} +#endif + + + +#if __i386__ || __x86_64__ || __ppc__ || __ppc64__ + +static bool comparevFloat(vFloat p1, vFloat p2) +{ + return (memcmp(&p1, &p2, 16) == 0); +} + +bool dovectortest(vFloat p1, vFloat p2, vFloat p3, vFloat p4, vFloat p5) +{ + vFloat r1 = { 1.1, 1.2, 1.3, 1.4 }; + vFloat r2 = { 2.1, 2.2, 2.3, 2.4 }; + vFloat r3 = { 3.1, 3.2, 3.3, 3.4 }; + vFloat r4 = { 4.1, 4.2, 4.3, 4.4 }; + vFloat r5 = { 5.1, 5.2, 5.3, 5.4 }; + + if ( !comparevFloat(p1, r1) ) + return false; + if ( !comparevFloat(p2, r2) ) + return false; + if ( !comparevFloat(p3, r3) ) + return false; + if ( !comparevFloat(p4, r4) ) + return false; + if ( !comparevFloat(p5, r5) ) + return false; + return true; +} + +#endif + + + + diff --git a/src/dyld-apple/unit-tests/test-cases/lazy-binding-reg-params/foo.h b/src/dyld-apple/unit-tests/test-cases/lazy-binding-reg-params/foo.h new file mode 100644 index 000000000..8b76407db --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/lazy-binding-reg-params/foo.h @@ -0,0 +1,25 @@ +#include // fprintf(), NULL + + +extern +#if __i386__ +__attribute__((regparm(3))) +#endif +bool dointtest(int p1, int p2, int p3, int p4, int p5); + +#if __ppc__ || __ppc64__ +extern bool dofloattest(double,double,double,double,double,double,double,double,double,double,double,double,double); +#endif + + +#if __i386__ || __x86_64__ || __ppc__ || __ppc64__ + + #if __i386__ || __x86_64__ + typedef float vFloat __attribute__ ((__vector_size__ (16))); + #elif __ppc__ || __ppc64__ + typedef __vector float vFloat; + #endif + + extern bool dovectortest(vFloat, vFloat, vFloat, vFloat, vFloat); + +#endif \ No newline at end of file diff --git a/src/dyld-apple/unit-tests/test-cases/lazy-binding-reg-params/main.c b/src/dyld-apple/unit-tests/test-cases/lazy-binding-reg-params/main.c new file mode 100644 index 000000000..fc66bd17a --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/lazy-binding-reg-params/main.c @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +/// +/// rdar://problem/4132378 support __attribute__((regparm())) +/// +/// The stub binding helper for dyld needs to preserve registers +/// + + +#include "foo.h" + + +static bool inttest() +{ + return dointtest(123,456,789,4444,55555); +} + +static bool floattest() +{ +#if __ppc__ || __ppc64__ + return dofloattest(1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0,10.0,11.0,12.0,13.0); +#elif __i386__ || __x86_64__ || __arm__ + return true; +#else + #error unknown arch +#endif +} + +#if __i386__ || __x86_64__ || __ppc__ || __ppc64__ +static bool vectorSupport() +{ +#if __ppc__ + uint32_t value; + size_t size=sizeof(uint32_t); + int err = sysctlbyname("hw.optional.altivec", &value, &size, NULL, 0); + //fprintf(stderr, "sysctlbyname() = %d\n", err); + return ( err == 0 ); +#else + return true; +#endif +} + +static bool vectortest() +{ + vFloat p1 = { 1.1, 1.2, 1.3, 1.4 }; + vFloat p2 = { 2.1, 2.2, 2.3, 2.4 }; + vFloat p3 = { 3.1, 3.2, 3.3, 3.4 }; + vFloat p4 = { 4.1, 4.2, 4.3, 4.4 }; + vFloat p5 = { 5.1, 5.2, 5.3, 5.4 }; + return dovectortest(p1, p2, p3, p4, p5); +} +#endif + +int main() +{ + if ( ! inttest() ) { + FAIL("lazy-binding-reg-params int parameters"); + return EXIT_SUCCESS; + } + + if ( ! floattest() ) { + FAIL("lazy-binding-reg-params float parameters"); + return EXIT_SUCCESS; + } + +#if __i386__ || __x86_64__ || __ppc__ || __ppc64__ + if ( vectorSupport() && ! vectortest() ) { + FAIL("lazy-binding-reg-params vector parameters"); + return EXIT_SUCCESS; + } +#endif + + PASS("lazy-binding-reg-params"); + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/lazy-dylib-init-order/expected.out b/src/dyld-apple/unit-tests/test-cases/lazy-dylib-init-order/expected.out new file mode 100644 index 000000000..330219072 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/lazy-dylib-init-order/expected.out @@ -0,0 +1,6 @@ +main_init +main +foo_init +foo +foo_term +main_term diff --git a/src/dyld-apple/unit-tests/test-cases/lazy-dylib-init-order/foo.c b/src/dyld-apple/unit-tests/test-cases/lazy-dylib-init-order/foo.c new file mode 100644 index 000000000..1047dce94 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/lazy-dylib-init-order/foo.c @@ -0,0 +1,21 @@ + +#include + +int foo() +{ + printf("foo\n"); + return 1; +} +int bar() { return 1; } + + +static __attribute__((constructor)) void foo_init() +{ + printf("foo_init\n"); +} + +static __attribute__((destructor)) void foo_term() +{ + printf("foo_term\n"); +} + diff --git a/src/dyld-apple/unit-tests/test-cases/lazy-dylib-init-order/main.c b/src/dyld-apple/unit-tests/test-cases/lazy-dylib-init-order/main.c new file mode 100644 index 000000000..06b33ce99 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/lazy-dylib-init-order/main.c @@ -0,0 +1,25 @@ +#include +#include + + +extern int foo(); +extern int bar(); + +static __attribute__((constructor)) void main_init() +{ + printf("main_init\n"); +} + +static __attribute__((destructor)) void main_term() +{ + printf("main_term\n"); +} + + +int main() +{ + printf("main\n"); + foo(); + return 0; +} + diff --git a/src/dyld-apple/unit-tests/test-cases/lazy-dylib-missing-dylib/foo.c b/src/dyld-apple/unit-tests/test-cases/lazy-dylib-missing-dylib/foo.c new file mode 100644 index 000000000..f41dbba79 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/lazy-dylib-missing-dylib/foo.c @@ -0,0 +1,6 @@ + + +int foo() +{ + return 1; +} diff --git a/src/dyld-apple/unit-tests/test-cases/lazy-dylib-missing-dylib/main.c b/src/dyld-apple/unit-tests/test-cases/lazy-dylib-missing-dylib/main.c new file mode 100644 index 000000000..9ff00323a --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/lazy-dylib-missing-dylib/main.c @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2008 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@ + */ +#include +#include +#include + +#include "test.h" + + +#if LAZY_HANDLER +// If a linkage unit defines dyld_lazy_dylib_proxy() +// then when binding lazy symbols, if one +// cannot be bound, it is bound to this function +// instead. +int dyld_lazy_dylib_proxy() +{ + return 7; +} +#endif + + +extern int foo(); // should not exist + +int main() +{ +#if LAZY_HANDLER + // sanity check that foo was not found + // dyld_lazy_dylib_proxy returns 7 when function cannot be found + if ( foo() != 7 ) { + FAIL("lazy-dylib-missing-dylib: dyld_lazy_dylib_proxy() not used for foo"); + return 0; + } +#else + // sanity check that foo was not found + // lazy loading return 0 when function cannot be found + if ( foo() != 0 ) { + FAIL("lazy-dylib-missing-dylib: foo found"); + return 0; + } +#endif + PASS("lazy-dylib-missing-dylib"); + return 0; +} + diff --git a/src/dyld-apple/unit-tests/test-cases/lazy-dylib-missing-symbol/foo.c b/src/dyld-apple/unit-tests/test-cases/lazy-dylib-missing-symbol/foo.c new file mode 100644 index 000000000..2c56b3983 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/lazy-dylib-missing-symbol/foo.c @@ -0,0 +1,15 @@ + + +int foo() +{ + return 1; +} + +#if BAR +int bar() +{ + return 1; +} +#endif + + diff --git a/src/dyld-apple/unit-tests/test-cases/lazy-dylib-missing-symbol/main.c b/src/dyld-apple/unit-tests/test-cases/lazy-dylib-missing-symbol/main.c new file mode 100644 index 000000000..934e48ac0 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/lazy-dylib-missing-symbol/main.c @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2008 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@ + */ +#include +#include +#include + +#include "test.h" + + +#if LAZY_HANDLER +// If a linkage unit defines dyld_lazy_dylib_proxy() +// then when binding lazy symbols, if one +// cannot be bound, it is bound to this function +// instead. +int dyld_lazy_dylib_proxy() +{ + return 7; +} +#endif + + +extern int foo(); // should exist +extern int bar(); // should not exist + +int main() +{ + // sanity check that foo was found + if ( foo() != 1 ) { + FAIL("lazy-dylib-missing-symbol: foo not found"); + return 0; + } + +#if LAZY_HANDLER + // sanity check that bar was not found + // lazy loading return 7 when function cannot be found + if ( bar() != 7 ) { + FAIL("lazy-dylib-missing-symbol: dyld_lazy_dylib_proxy() not used for bar"); + return 0; + } +#else + // sanity check that bar was not found + // lazy loading return 0 when function cannot be found + if ( bar() != 0 ) { + FAIL("lazy-dylib-missing-symbol: bar found"); + return 0; + } +#endif + PASS("lazy-dylib-missing-symbol"); + return 0; +} + diff --git a/src/dyld-apple/unit-tests/test-cases/lazy-pointer-binding/foo.c b/src/dyld-apple/unit-tests/test-cases/lazy-pointer-binding/foo.c new file mode 100644 index 000000000..53d2618cc --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/lazy-pointer-binding/foo.c @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include // fprintf(), NULL + +bool floattest(double p1, float p2, double p3, float p4, double p5, float p6, + double p7, float p8, double p9, float p10, double p11, float p12, + double p13, float p14) +{ + if ( p1 != 1.0 ) + return false; + if ( p2 != 2.0 ) + return false; + if ( p3 != 3.0 ) + return false; + if ( p4 != 4.0 ) + return false; + if ( p5 != 5.0 ) + return false; + if ( p6 != 6.0 ) + return false; + if ( p7 != 7.0 ) + return false; + if ( p8 != 8.0 ) + return false; + if ( p9 != 9.0 ) + return false; + if ( p10 != 10.0 ) + return false; + if ( p11 != 11.0 ) + return false; + if ( p12 != 12.0 ) + return false; + if ( p13 != 13.0 ) + return false; + if ( p14 != 14.0 ) + return false; + return true; +} + +bool inttest(long long p1, long long p2, long long p3, long long p4, long long p5) +{ + if ( p1 != 0x100000002) + return false; + if ( p2 != 0x300000004) + return false; + if ( p3 != 0x500000006) + return false; + if ( p4 != 0x700000008) + return false; + if ( p5 != 0x90000000A) + return false; + return true; +} + + diff --git a/src/dyld-apple/unit-tests/test-cases/lazy-pointer-binding/main.c b/src/dyld-apple/unit-tests/test-cases/lazy-pointer-binding/main.c new file mode 100644 index 000000000..c2554b274 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/lazy-pointer-binding/main.c @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +extern bool floattest(double p1, float p2, double p3, float p4, double p5, float p6, + double p7, float p8, double p9, float p10, double p11, float p12, + double p13, float p14); + +extern bool inttest(long long p1, long long p2, long long p3, long long p4, long long p5); + + + +int main() +{ + if ( ! floattest(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0) ) { + FAIL("lazy-pointer-binding float parameters"); + return EXIT_SUCCESS; + } + + if ( ! inttest(0x100000002, 0x300000004, 0x500000006, 0x700000008, 0x90000000A) ) { + FAIL("lazy-pointer-binding int parameters"); + return EXIT_SUCCESS; + } + + PASS("lazy-pointer-binding"); + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/lib-name-overload/foo.c b/src/dyld-apple/unit-tests/test-cases/lib-name-overload/foo.c new file mode 100644 index 000000000..4b2ea985c --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/lib-name-overload/foo.c @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ + +int foo = 0; + diff --git a/src/dyld-apple/unit-tests/test-cases/lib-name-overload/foo2.c b/src/dyld-apple/unit-tests/test-cases/lib-name-overload/foo2.c new file mode 100644 index 000000000..b34dc76bb --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/lib-name-overload/foo2.c @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ + +int foo2 = 0; + + diff --git a/src/dyld-apple/unit-tests/test-cases/lib-name-overload/main.c b/src/dyld-apple/unit-tests/test-cases/lib-name-overload/main.c new file mode 100644 index 000000000..73cc95aff --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/lib-name-overload/main.c @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include +#include + +#include "test.h" + +/// +/// rdar://problem/3684168 +/// + + + +extern int foo2; + +int main() +{ + return foo2; +} diff --git a/src/dyld-apple/unit-tests/test-cases/loader_path-dup/bar.c b/src/dyld-apple/unit-tests/test-cases/loader_path-dup/bar.c new file mode 100644 index 000000000..edd9f3571 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/loader_path-dup/bar.c @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include + +extern int base; + +bool bar() +{ + return (base == 2); +} diff --git a/src/dyld-apple/unit-tests/test-cases/loader_path-dup/base.c b/src/dyld-apple/unit-tests/test-cases/loader_path-dup/base.c new file mode 100644 index 000000000..03b9160f1 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/loader_path-dup/base.c @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ + +#if FOO +int base = 1; +#else +int base = 2; +#endif + diff --git a/src/dyld-apple/unit-tests/test-cases/loader_path-dup/foo.c b/src/dyld-apple/unit-tests/test-cases/loader_path-dup/foo.c new file mode 100644 index 000000000..7f0cfbb03 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/loader_path-dup/foo.c @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include + + +extern int base; + +bool foo() +{ + return (base == 1); +} diff --git a/src/dyld-apple/unit-tests/test-cases/loader_path-dup/main.c b/src/dyld-apple/unit-tests/test-cases/loader_path-dup/main.c new file mode 100644 index 000000000..9c12ccca8 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/loader_path-dup/main.c @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include +#include +#include +#include + +#include "test.h" + +extern bool foo(); +extern bool bar(); + +int main() +{ + if ( foo() && bar() ) + PASS("loader_path"); + else + FAIL("loader_path-dup both libraries not loaded"); + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/loader_path-symlink/bar.c b/src/dyld-apple/unit-tests/test-cases/loader_path-symlink/bar.c new file mode 100644 index 000000000..b72a1a516 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/loader_path-symlink/bar.c @@ -0,0 +1,3 @@ +void bar() +{ +} diff --git a/src/dyld-apple/unit-tests/test-cases/loader_path-symlink/foo.c b/src/dyld-apple/unit-tests/test-cases/loader_path-symlink/foo.c new file mode 100644 index 000000000..3695dc91e --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/loader_path-symlink/foo.c @@ -0,0 +1,3 @@ +void foo() +{ +} diff --git a/src/dyld-apple/unit-tests/test-cases/loader_path-symlink/main.c b/src/dyld-apple/unit-tests/test-cases/loader_path-symlink/main.c new file mode 100644 index 000000000..4c91a7c12 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/loader_path-symlink/main.c @@ -0,0 +1,14 @@ +#include +#include +#include + +#include "test.h" + +extern void foo(); + +int main() +{ + foo(); + PASS("loader_path-symlink"); + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/loader_path/bar.c b/src/dyld-apple/unit-tests/test-cases/loader_path/bar.c new file mode 100644 index 000000000..902e56356 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/loader_path/bar.c @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +void bar() {} diff --git a/src/dyld-apple/unit-tests/test-cases/loader_path/foo.c b/src/dyld-apple/unit-tests/test-cases/loader_path/foo.c new file mode 100644 index 000000000..1bcf3c224 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/loader_path/foo.c @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +void foo() +{ +} diff --git a/src/dyld-apple/unit-tests/test-cases/loader_path/main.c b/src/dyld-apple/unit-tests/test-cases/loader_path/main.c new file mode 100644 index 000000000..f8600b477 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/loader_path/main.c @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2005-2009 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@ + */ +#include +#include +#include +#include +#include + +#include "test.h" + + +int main() +{ +#if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && (__MAC_OS_X_VERSION_MIN_REQUIRED < __MAC_10_5) + NSAddImage("@loader_path/hide/libfoo3.dylib", 0); +#else + if ( dlopen("@loader_path/hide/libfoo3.dylib", 0) == NULL ) + FAIL("loader_path"); + else +#endif + PASS("loader_path"); + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/missing-symlink-framework-fallback-path/Foo.c b/src/dyld-apple/unit-tests/test-cases/missing-symlink-framework-fallback-path/Foo.c new file mode 100644 index 000000000..100cfc671 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/missing-symlink-framework-fallback-path/Foo.c @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ diff --git a/src/dyld-apple/unit-tests/test-cases/missing-symlink-framework-fallback-path/main.c b/src/dyld-apple/unit-tests/test-cases/missing-symlink-framework-fallback-path/main.c new file mode 100644 index 000000000..a2233c801 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/missing-symlink-framework-fallback-path/main.c @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +int +main(int argc, const char* argv[]) +{ + + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/non-lazy-slide/bar.c b/src/dyld-apple/unit-tests/test-cases/non-lazy-slide/bar.c new file mode 100644 index 000000000..fa8deff82 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/non-lazy-slide/bar.c @@ -0,0 +1,3 @@ + + +int bar = 10; diff --git a/src/dyld-apple/unit-tests/test-cases/non-lazy-slide/foo.c b/src/dyld-apple/unit-tests/test-cases/non-lazy-slide/foo.c new file mode 100644 index 000000000..1b2c9f738 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/non-lazy-slide/foo.c @@ -0,0 +1,7 @@ + +extern int bar; + +int foo() +{ + return bar; +} diff --git a/src/dyld-apple/unit-tests/test-cases/non-lazy-slide/main.c b/src/dyld-apple/unit-tests/test-cases/non-lazy-slide/main.c new file mode 100644 index 000000000..a73a1db05 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/non-lazy-slide/main.c @@ -0,0 +1,23 @@ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +// libfoo.dylib is built from multiple source files +// It internally uses non-lazy pointer to compute +// the result for foo() + + +extern int foo(); + +int main() +{ + if ( foo() == 10 ) + PASS("non-lazy-slide"); + else + FAIL("non-lazy-slide"); + return EXIT_SUCCESS; +} + + diff --git a/src/dyld-apple/unit-tests/test-cases/non-lazy-weak/bar.c b/src/dyld-apple/unit-tests/test-cases/non-lazy-weak/bar.c new file mode 100644 index 000000000..91a216d07 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/non-lazy-weak/bar.c @@ -0,0 +1,3 @@ + + +__attribute__((weak)) int bar = 10; diff --git a/src/dyld-apple/unit-tests/test-cases/non-lazy-weak/foo.c b/src/dyld-apple/unit-tests/test-cases/non-lazy-weak/foo.c new file mode 100644 index 000000000..1b2c9f738 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/non-lazy-weak/foo.c @@ -0,0 +1,7 @@ + +extern int bar; + +int foo() +{ + return bar; +} diff --git a/src/dyld-apple/unit-tests/test-cases/non-lazy-weak/main.c b/src/dyld-apple/unit-tests/test-cases/non-lazy-weak/main.c new file mode 100644 index 000000000..16a78b705 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/non-lazy-weak/main.c @@ -0,0 +1,20 @@ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +// libfoo.dylib has a weak global variable that foo() returns + +extern int foo(); + +int main() +{ + if ( foo() == 10 ) + PASS("non-lazy-weak"); + else + FAIL("non-lazy-weak"); + return EXIT_SUCCESS; +} + + diff --git a/src/dyld-apple/unit-tests/test-cases/non-weak-library/foo.c b/src/dyld-apple/unit-tests/test-cases/non-weak-library/foo.c new file mode 100644 index 000000000..c57adba0f --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/non-weak-library/foo.c @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +int foo() +{ + return 10; +} diff --git a/src/dyld-apple/unit-tests/test-cases/non-weak-library/main.c b/src/dyld-apple/unit-tests/test-cases/non-weak-library/main.c new file mode 100644 index 000000000..9487058b1 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/non-weak-library/main.c @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include +#include + +#include "test.h" + +int +main(int argc, char **argv) +{ +#if defined(LIB_PRESENT) + PASS("non-weak-library"); +#endif +#if defined(LIB_ABSENT) + FAIL("non-weak-library"); +#endif + + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/operator-new-dylib/foo.cxx b/src/dyld-apple/unit-tests/test-cases/operator-new-dylib/foo.cxx new file mode 100644 index 000000000..f91f88cfd --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/operator-new-dylib/foo.cxx @@ -0,0 +1,13 @@ + +#include +#include + + + + + +char* foo() +{ + return new char[24]; +} + diff --git a/src/dyld-apple/unit-tests/test-cases/operator-new-dylib/main.cxx b/src/dyld-apple/unit-tests/test-cases/operator-new-dylib/main.cxx new file mode 100644 index 000000000..25be30d2f --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/operator-new-dylib/main.cxx @@ -0,0 +1,31 @@ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +#include +#include + + +extern char* foo(); + +static char all[] = "hello"; + +void* operator new[](std::size_t) throw (std::bad_alloc) +{ + return all; +} + + +int main() +{ + char* newArray = new char[24]; + char* fromFoo = foo(); + + if ( fromFoo == newArray ) + PASS("operator-new-dylib"); + else + FAIL("operator-new-dylib"); + return EXIT_SUCCESS; +} + diff --git a/src/dyld-apple/unit-tests/test-cases/operator-new/main.cxx b/src/dyld-apple/unit-tests/test-cases/operator-new/main.cxx new file mode 100644 index 000000000..a0c25ce26 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/operator-new/main.cxx @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2006 Apple Computer, 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +#include +#include + +// +// This test case verifies that calling operator new[] in libstdc++.dylib +// will turn around and call operator new in this main exectuable +// + +static void* ptr; + +void* operator new(size_t s) throw (std::bad_alloc) +{ + ptr = malloc(s); + return ptr; +} + + +int main() +{ + char* stuff = new char[24]; + if ( (void*)stuff == ptr ) + PASS("operator-new"); + else + FAIL("operator-new"); + return EXIT_SUCCESS; +} + diff --git a/src/dyld-apple/unit-tests/test-cases/partial-library-load/bar.c b/src/dyld-apple/unit-tests/test-cases/partial-library-load/bar.c new file mode 100644 index 000000000..400e1bf65 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/partial-library-load/bar.c @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ + + +int bar1 = 1; +#if HAS_BAR2 +int bar2 = 2; +#endif +int bar3 = 3; + diff --git a/src/dyld-apple/unit-tests/test-cases/partial-library-load/bundle.c b/src/dyld-apple/unit-tests/test-cases/partial-library-load/bundle.c new file mode 100644 index 000000000..a7aad9e13 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/partial-library-load/bundle.c @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +extern int foo(); + +void bun() +{ + foo(); + +} + diff --git a/src/dyld-apple/unit-tests/test-cases/partial-library-load/foo.c b/src/dyld-apple/unit-tests/test-cases/partial-library-load/foo.c new file mode 100644 index 000000000..8abc708be --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/partial-library-load/foo.c @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ + +extern int bar1; +extern int bar2; +extern int bar3; + + + +int foo() +{ + return bar1 + bar2 + bar3; +} diff --git a/src/dyld-apple/unit-tests/test-cases/partial-library-load/main.c b/src/dyld-apple/unit-tests/test-cases/partial-library-load/main.c new file mode 100644 index 000000000..9993ac408 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/partial-library-load/main.c @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include +#include +#include + +#include "test.h" // PASS(), FAIL() + + +int main() +{ +// NSCreateObjectFileImageFromMemory is only available on Mac OS X - not iPhone OS +#if __MAC_OS_X_VERSION_MIN_REQUIRED + // load bundle which indirectly loads libfoo and libbar + NSObjectFileImage ofi; + if ( NSCreateObjectFileImageFromFile("test.bundle", &ofi) != NSObjectFileImageSuccess ) { + FAIL("NSCreateObjectFileImageFromFile failed"); + return 1; + } + + // link bundle, this will fail because bar2 is missing from libbar + NSModule mod = NSLinkModule(ofi, "test.bundle", NSLINKMODULE_OPTION_RETURN_ON_ERROR); + if ( mod != NULL ) { + FAIL("NSLinkModule succeeded but should have failed"); + return 1; + } + + // load libfoo, this should fail because it can't be loaded + const struct mach_header* mh = NSAddImage("libfoo.dylib", NSADDIMAGE_OPTION_RETURN_ON_ERROR); + if ( mh != NULL ) { + return 1; + } +#endif +#if 0 + // find foo + NSSymbol sym = NSLookupSymbolInImage(mh, "_foo", NSLOOKUPSYMBOLINIMAGE_OPTION_BIND); + if ( sym == NULL ) { + FAIL("NSLookupSymbolInImage failed"); + return 1; + } + + // if foo() was only partially bound, this will crash + int (*fooPtr)() = NSAddressOfSymbol(sym); + (*fooPtr)(); +#endif + return 0; +} \ No newline at end of file diff --git a/src/dyld-apple/unit-tests/test-cases/pie-basic/main.c b/src/dyld-apple/unit-tests/test-cases/pie-basic/main.c new file mode 100644 index 000000000..3a6571d1b --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/pie-basic/main.c @@ -0,0 +1,33 @@ +/* + * 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@ + */ +#include +#include + +int main() +{ + //int local; + + printf("&main=%p\n", &main); + //printf("&local=%p\n",&local); + return 0; +} diff --git a/src/dyld-apple/unit-tests/test-cases/pie-big/main.c b/src/dyld-apple/unit-tests/test-cases/pie-big/main.c new file mode 100644 index 000000000..49d9a8826 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/pie-big/main.c @@ -0,0 +1,44 @@ +/* + * 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@ + */ +#include +#include +#include + +#if __IPHONE_OS_VERSION_MIN_REQUIRED +char bigarray[0x10000000]; // 0.25GB +#else + #if __LP64__ + char bigarray[0xF0000000]; // 4GB + #else + char bigarray[0x30000000]; // 0.75GB + #endif +#endif + +int main() +{ + //int local; + + printf("&main=%p\n", &main); + //printf("&local=%p\n",&local); + return 0; +} diff --git a/src/dyld-apple/unit-tests/test-cases/pie-custom-stack/main.c b/src/dyld-apple/unit-tests/test-cases/pie-custom-stack/main.c new file mode 100644 index 000000000..3a6571d1b --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/pie-custom-stack/main.c @@ -0,0 +1,33 @@ +/* + * 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@ + */ +#include +#include + +int main() +{ + //int local; + + printf("&main=%p\n", &main); + //printf("&local=%p\n",&local); + return 0; +} diff --git a/src/dyld-apple/unit-tests/test-cases/pie-dylib/foo.c b/src/dyld-apple/unit-tests/test-cases/pie-dylib/foo.c new file mode 100644 index 000000000..0c9d0805e --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/pie-dylib/foo.c @@ -0,0 +1,7 @@ +#include +#include + +void foo() +{ + printf("&foo=%p\n", &foo); +} diff --git a/src/dyld-apple/unit-tests/test-cases/pie-dylib/main.c b/src/dyld-apple/unit-tests/test-cases/pie-dylib/main.c new file mode 100644 index 000000000..d36c9b85b --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/pie-dylib/main.c @@ -0,0 +1,10 @@ +#include +#include + +extern void foo(); + +int main() +{ + foo(); + return 0; +} diff --git a/src/dyld-apple/unit-tests/test-cases/pie-text-reloc/main.c b/src/dyld-apple/unit-tests/test-cases/pie-text-reloc/main.c new file mode 100644 index 000000000..f1c8b8584 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/pie-text-reloc/main.c @@ -0,0 +1,33 @@ +/* + * 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@ + */ +#include +#include + + +static int data=0; + +int main() +{ + printf("&data=%p\n", &data); + return 0; +} diff --git a/src/dyld-apple/unit-tests/test-cases/prebased-performance/foo.c b/src/dyld-apple/unit-tests/test-cases/prebased-performance/foo.c new file mode 100644 index 000000000..1836973e1 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/prebased-performance/foo.c @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2006 Apple Computer, 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@ + */ + +#include +#include +#include +#include +#include +#include + +struct page +{ + struct page* other; + char pad[4096-sizeof(void*)]; +}; + + +struct page p1 = { &p1 }; +struct page p2 = { &p1 }; +struct page p3 = { &p1 }; +struct page p4 = { &p1 }; +struct page p5 = { &p1 }; +struct page p6 = { &p1 }; +struct page p7 = { &p1 }; +struct page p8 = { &p1 }; +struct page p9 = { &p1 }; +struct page p10 = { &p1 }; + +// +// This dylib has 10 pages of data and each page will be adjusted +// by dyld if this dylib is not loaded at its preferred address. +// +bool checkRebasing() +{ + int dirtyPageCount = 0; + vm_address_t start = (((uintptr_t)&p1) + 4095) & (-4096); + vm_address_t end = (((uintptr_t)&p10) + 4095) & (-4096); + task_port_t task; + task_for_pid(mach_task_self(), getpid(), &task); + + for (vm_address_t addr = start; addr < end; addr += 4096) { + integer_t disposition = 0; + integer_t ref_count = 0; + vm_map_page_query(task, start, &disposition, &ref_count); + if ( disposition & VM_PAGE_QUERY_PAGE_DIRTY ) + ++dirtyPageCount; + } + + //fprintf(stderr, "dirtyPageCount=%d\n", dirtyPageCount); + + // if there are too many dirty pages, then dyld is inefficient + return ( dirtyPageCount < 2 ); +} + + + + + diff --git a/src/dyld-apple/unit-tests/test-cases/prebased-performance/main.c b/src/dyld-apple/unit-tests/test-cases/prebased-performance/main.c new file mode 100644 index 000000000..98fe608cd --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/prebased-performance/main.c @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2006 Apple Computer, 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +extern bool checkRebasing(); + +int main(int argc, const char* argv[]) +{ + if ( checkRebasing() ) + PASS("prebased-performance"); + else + FAIL("prebased-performance"); + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/progname/main.c b/src/dyld-apple/unit-tests/test-cases/progname/main.c new file mode 100644 index 000000000..1f051117e --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/progname/main.c @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2006 Apple Computer, 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +static const char* name = NULL; + + +static void checker() __attribute__((constructor)); +static void checker() +{ + name = strdup(getprogname()); +} + + + +int +main(int argc, const char* argv[]) +{ + // verify that getprogname() works during initializers + if ( strcmp(name, getprogname()) == 0 ) + PASS("progname"); + else + FAIL("progname"); + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/pthread-keys/main.c b/src/dyld-apple/unit-tests/test-cases/pthread-keys/main.c new file mode 100644 index 000000000..e1832d8bb --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/pthread-keys/main.c @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include +#include +#include + +#include "test.h" + +/// +/// rdar://problem/4313830 +/// The key index for main executables starts at 4 +/// + +int main() +{ + pthread_key_t key; + + int result = pthread_key_create(&key, NULL); + //printf("key=%u\n", key); + if ( result != 0 ) + FAIL("pthread-keys creation failure"); + else if ( key < 4 ) + FAIL("pthread-keys key out of bounds"); + else + PASS("pthread-keys"); + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/re-export-dylib/bar.c b/src/dyld-apple/unit-tests/test-cases/re-export-dylib/bar.c new file mode 100644 index 000000000..b0faf2f0b --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/re-export-dylib/bar.c @@ -0,0 +1,6 @@ +int bar() +{ + return 1; +} + + diff --git a/src/dyld-apple/unit-tests/test-cases/re-export-dylib/foo.c b/src/dyld-apple/unit-tests/test-cases/re-export-dylib/foo.c new file mode 100644 index 000000000..2486c7ae2 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/re-export-dylib/foo.c @@ -0,0 +1,5 @@ +int foo() +{ + return 0; +} + diff --git a/src/dyld-apple/unit-tests/test-cases/re-export-dylib/main.c b/src/dyld-apple/unit-tests/test-cases/re-export-dylib/main.c new file mode 100644 index 000000000..2d54c7456 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/re-export-dylib/main.c @@ -0,0 +1,39 @@ +/* + * 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@ + */ +#include +#include + +#include "test.h" + + + +extern int foo(); +extern int bar(); + +int main() +{ + foo(); + bar(); + PASS("re-export-dylib"); + return 0; +} diff --git a/src/dyld-apple/unit-tests/test-cases/re-export-framework/bar.c b/src/dyld-apple/unit-tests/test-cases/re-export-framework/bar.c new file mode 100644 index 000000000..b0faf2f0b --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/re-export-framework/bar.c @@ -0,0 +1,6 @@ +int bar() +{ + return 1; +} + + diff --git a/src/dyld-apple/unit-tests/test-cases/re-export-framework/foo.c b/src/dyld-apple/unit-tests/test-cases/re-export-framework/foo.c new file mode 100644 index 000000000..2486c7ae2 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/re-export-framework/foo.c @@ -0,0 +1,5 @@ +int foo() +{ + return 0; +} + diff --git a/src/dyld-apple/unit-tests/test-cases/re-export-framework/main.c b/src/dyld-apple/unit-tests/test-cases/re-export-framework/main.c new file mode 100644 index 000000000..7ad3fed44 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/re-export-framework/main.c @@ -0,0 +1,39 @@ +/* + * 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@ + */ +#include +#include + +#include "test.h" + + + +extern int foo(); +extern int bar(); + +int main() +{ + foo(); + bar(); + PASS("re-export-framework"); + return 0; +} diff --git a/src/dyld-apple/unit-tests/test-cases/re-export-sub-framework/bar.c b/src/dyld-apple/unit-tests/test-cases/re-export-sub-framework/bar.c new file mode 100644 index 000000000..b0faf2f0b --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/re-export-sub-framework/bar.c @@ -0,0 +1,6 @@ +int bar() +{ + return 1; +} + + diff --git a/src/dyld-apple/unit-tests/test-cases/re-export-sub-framework/foo.c b/src/dyld-apple/unit-tests/test-cases/re-export-sub-framework/foo.c new file mode 100644 index 000000000..2486c7ae2 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/re-export-sub-framework/foo.c @@ -0,0 +1,5 @@ +int foo() +{ + return 0; +} + diff --git a/src/dyld-apple/unit-tests/test-cases/re-export-sub-framework/main.c b/src/dyld-apple/unit-tests/test-cases/re-export-sub-framework/main.c new file mode 100644 index 000000000..fe2ae5e38 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/re-export-sub-framework/main.c @@ -0,0 +1,39 @@ +/* + * 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@ + */ +#include +#include + +#include "test.h" + + + +extern int foo(); +extern int bar(); + +int main() +{ + foo(); + bar(); + PASS("re-export-sub-framework"); + return 0; +} diff --git a/src/dyld-apple/unit-tests/test-cases/re-export-symbol-dylib/bar.c b/src/dyld-apple/unit-tests/test-cases/re-export-symbol-dylib/bar.c new file mode 100644 index 000000000..18d763346 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/re-export-symbol-dylib/bar.c @@ -0,0 +1,5 @@ + +int bar(void) +{ + return 2; +} diff --git a/src/dyld-apple/unit-tests/test-cases/re-export-symbol-dylib/baz.c b/src/dyld-apple/unit-tests/test-cases/re-export-symbol-dylib/baz.c new file mode 100644 index 000000000..cb3081820 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/re-export-symbol-dylib/baz.c @@ -0,0 +1,5 @@ +int baz() +{ + return 3; +} + diff --git a/src/dyld-apple/unit-tests/test-cases/re-export-symbol-dylib/baz.exp b/src/dyld-apple/unit-tests/test-cases/re-export-symbol-dylib/baz.exp new file mode 100644 index 000000000..5fbf7f497 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/re-export-symbol-dylib/baz.exp @@ -0,0 +1,2 @@ +_baz +_frob diff --git a/src/dyld-apple/unit-tests/test-cases/re-export-symbol-dylib/foo.c b/src/dyld-apple/unit-tests/test-cases/re-export-symbol-dylib/foo.c new file mode 100644 index 000000000..72b3c29f8 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/re-export-symbol-dylib/foo.c @@ -0,0 +1,4 @@ +int foo(void) +{ + return 1; +} diff --git a/src/dyld-apple/unit-tests/test-cases/re-export-symbol-dylib/foo.exp b/src/dyld-apple/unit-tests/test-cases/re-export-symbol-dylib/foo.exp new file mode 100644 index 000000000..aff96a11e --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/re-export-symbol-dylib/foo.exp @@ -0,0 +1,4 @@ +_foo +_bar +_baz +_frob diff --git a/src/dyld-apple/unit-tests/test-cases/re-export-symbol-dylib/frob.c b/src/dyld-apple/unit-tests/test-cases/re-export-symbol-dylib/frob.c new file mode 100644 index 000000000..3df9d3d1a --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/re-export-symbol-dylib/frob.c @@ -0,0 +1,5 @@ +int frob() +{ + return 4; +} + diff --git a/src/dyld-apple/unit-tests/test-cases/re-export-symbol-dylib/main.c b/src/dyld-apple/unit-tests/test-cases/re-export-symbol-dylib/main.c new file mode 100644 index 000000000..f12dd108b --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/re-export-symbol-dylib/main.c @@ -0,0 +1,44 @@ + +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + + +extern int foo(); +extern int bar(); +extern int baz(); +extern int frob(); + +int (*pfoo)() = &foo; +int (*pbar)() = &bar; +int (*pbaz)() = &baz; +int (*pfrob)() = &frob; + + +int main() +{ + if ( foo() != 1 ) + FAIL("re-export-symbol-dylib: foo() returned wrong value"); + else if ( bar() != 2 ) + FAIL("re-export-symbol-dylib: bar() returned wrong value"); + else if ( baz() != 3 ) + FAIL("re-export-symbol-dylib: baz() returned wrong value"); + else if ( frob() != 4 ) + FAIL("re-export-symbol-dylib: frob() returned wrong value"); + + else if ( (*pfoo)() != 1 ) + FAIL("re-export-symbol-dylib: (*pfoo)() returned wrong value"); + else if ( (*pbar)() != 2 ) + FAIL("re-export-symbol-dylib: (*pbar)() returned wrong value"); + else if ( (*pbaz)() != 3 ) + FAIL("re-export-symbol-dylib: (*pbaz)() returned wrong value"); + else if ( (*pfrob)() != 4 ) + FAIL("re-export-symbol-dylib: (*pfrob)() returned wrong value"); + else + PASS("re-export-symbol-dylib"); + return 0; +} + diff --git a/src/dyld-apple/unit-tests/test-cases/re-export-symbol/bar.c b/src/dyld-apple/unit-tests/test-cases/re-export-symbol/bar.c new file mode 100644 index 000000000..f5837c128 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/re-export-symbol/bar.c @@ -0,0 +1,5 @@ + +int bar(void) +{ + return 10; +} diff --git a/src/dyld-apple/unit-tests/test-cases/re-export-symbol/foo.c b/src/dyld-apple/unit-tests/test-cases/re-export-symbol/foo.c new file mode 100644 index 000000000..9232d4c7c --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/re-export-symbol/foo.c @@ -0,0 +1,4 @@ +int foo(void) +{ + return 10; +} diff --git a/src/dyld-apple/unit-tests/test-cases/re-export-symbol/foo.exp b/src/dyld-apple/unit-tests/test-cases/re-export-symbol/foo.exp new file mode 100644 index 000000000..b9e50b82d --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/re-export-symbol/foo.exp @@ -0,0 +1,2 @@ +_foo +_bar diff --git a/src/dyld-apple/unit-tests/test-cases/re-export-symbol/foo2.exp b/src/dyld-apple/unit-tests/test-cases/re-export-symbol/foo2.exp new file mode 100644 index 000000000..d55b7486a --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/re-export-symbol/foo2.exp @@ -0,0 +1,2 @@ +_foo +_mybar diff --git a/src/dyld-apple/unit-tests/test-cases/re-export-symbol/main1.c b/src/dyld-apple/unit-tests/test-cases/re-export-symbol/main1.c new file mode 100644 index 000000000..cd2bab72c --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/re-export-symbol/main1.c @@ -0,0 +1,23 @@ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +extern int foo(); +extern int bar(); + +int (*pbar)() = &bar; + + +int main() +{ + if ( foo() != 10 ) + FAIL("re-export-symbol: foo() returned wrong value"); + if ( bar() != 10 ) + FAIL("re-export-symbol: bar() returned wrong value"); + if ( (*pbar)() != 10 ) + FAIL("re-export-symbol: (*pbar)() returned wrong value"); + PASS("re-export-symbol"); + return 0; +} diff --git a/src/dyld-apple/unit-tests/test-cases/re-export-symbol/main2.c b/src/dyld-apple/unit-tests/test-cases/re-export-symbol/main2.c new file mode 100644 index 000000000..7a11c9272 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/re-export-symbol/main2.c @@ -0,0 +1,26 @@ + +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + + +extern int foo(); +extern int mybar(); + +int (*pmybar)() = &mybar; + +int main() +{ + if ( foo() != 10 ) + FAIL("re-export-symbol: foo() returned wrong value"); + if ( mybar() != 10 ) + FAIL("re-export-symbol: mybar() returned wrong value"); + if ( (*pmybar)() != 10 ) + FAIL("re-export-symbol: (*pmybar)() returned wrong value"); + PASS("re-export-symbol"); + return 0; +} + diff --git a/src/dyld-apple/unit-tests/test-cases/read-only-import-shared-cache-coalesce/foo.cxx b/src/dyld-apple/unit-tests/test-cases/read-only-import-shared-cache-coalesce/foo.cxx new file mode 100644 index 000000000..9bf26a8c6 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/read-only-import-shared-cache-coalesce/foo.cxx @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2006-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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +#include +#include + +// +// This test case verifies that calling operator new[] in libstdc++.dylib +// will turn around and call operator new in this main exectuable +// + +static void* ptr; + +void* operator new(size_t s) throw (std::bad_alloc) +{ + ptr = malloc(s); + return ptr; +} + + +int check() +{ + char* stuff = new char[24]; + if ( (void*)stuff == ptr ) + PASS("operator-new"); + else + FAIL("operator-new"); + return 0; +} + +int x = check(); // runs when library is loaded + +// add this so WEAK_DEFINES is set, so dyld searchs this image +int __attribute__((weak)) junk = 2; diff --git a/src/dyld-apple/unit-tests/test-cases/read-only-import-shared-cache-coalesce/main.c b/src/dyld-apple/unit-tests/test-cases/read-only-import-shared-cache-coalesce/main.c new file mode 100644 index 000000000..4579fe46a --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/read-only-import-shared-cache-coalesce/main.c @@ -0,0 +1,12 @@ + +#include + +int main() +{ + // dynamically load libfoo.dylib which depends on libstdc++.dylib + // being re-bound to libfoo's operator new. + dlopen("libfoo.dylib", RTLD_LAZY); + return 0; +} + + diff --git a/src/dyld-apple/unit-tests/test-cases/read-only-import-shared-cache-dlopen-override/main.c b/src/dyld-apple/unit-tests/test-cases/read-only-import-shared-cache-dlopen-override/main.c new file mode 100644 index 000000000..b3c927d57 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/read-only-import-shared-cache-dlopen-override/main.c @@ -0,0 +1,14 @@ +#include + +#include "test.h" + +int main() +{ + // dynamically load libz.dylib which imports _malloc from libSystem + dlopen("/usr/lib/libz.dylib", RTLD_LAZY); + + + PASS("read-only-import-shared-cache-dlopen-override"); + return 0; +} + diff --git a/src/dyld-apple/unit-tests/test-cases/read-only-import-shared-cache-dlopen-override/mymalloc.c b/src/dyld-apple/unit-tests/test-cases/read-only-import-shared-cache-dlopen-override/mymalloc.c new file mode 100644 index 000000000..b0dd8191f --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/read-only-import-shared-cache-dlopen-override/mymalloc.c @@ -0,0 +1,9 @@ +#include +#include + +void* mymalloc(size_t s) +{ + return malloc(s); +} + +DYLD_INTERPOSE(mymalloc, malloc) diff --git a/src/dyld-apple/unit-tests/test-cases/read-only-stubs/bar.c b/src/dyld-apple/unit-tests/test-cases/read-only-stubs/bar.c new file mode 100644 index 000000000..d9a1c2025 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/read-only-stubs/bar.c @@ -0,0 +1,35 @@ +/* + * 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +int barData = 1; + +void bar() +{ +} + + diff --git a/src/dyld-apple/unit-tests/test-cases/read-only-stubs/foo.c b/src/dyld-apple/unit-tests/test-cases/read-only-stubs/foo.c new file mode 100644 index 000000000..68fb1fb96 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/read-only-stubs/foo.c @@ -0,0 +1,107 @@ +/* + * 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +extern void bar(); +extern int barData; + + +static vm_prot_t getPermission(void* addr) +{ + mach_vm_address_t address = (mach_vm_address_t)(uintptr_t)addr; + kern_return_t result; + mach_port_t object_name; + vm_region_basic_info_data_64_t info; + mach_msg_type_number_t count; + mach_vm_size_t size = 4096; + + count = VM_REGION_BASIC_INFO_COUNT_64; + result = mach_vm_region(mach_task_self(), + &address, + &size, + VM_REGION_BASIC_INFO_64, + (vm_region_info_t)&info, + &count, + &object_name); + if ( result == KERN_SUCCESS ) + return info.protection; + return 0; +} + + +static void* getStubAddr() +{ +#if __LP64__ + uint64_t size; +#else + uint32_t size; +#endif + uintptr_t slide = (uintptr_t)&_mh_dylib_header; // assume dylib is zero-base so slide == load address +#if __i386__ + return getsectdatafromheader(&_mh_dylib_header, "__IMPORT", "__jump_table", &size) + slide; +#elif __ppc__ + return getsectdatafromheader(&_mh_dylib_header, "TEXT", "__picsymbolstub1", &size) + slide; +#elif __ppc64__ + return getsectdatafromheader_64(&_mh_dylib_header, "__TEXT", "__picsymbolstub1", &size) + slide; +#elif __x86_64__ + return getsectdatafromheader_64(&_mh_dylib_header, "__TEXT", "__symbol_stub1", &size) + slide; +#elif __arm__ + void* p = getsectdata("__TEXT", "__picsymbolstub4", (unsigned long*)&size); + if ( p != NULL ) + return getsectdatafromheader(&_mh_dylib_header, "__TEXT", "__picsymbolstub4", &size) + slide; + return getsectdatafromheader(&_mh_dylib_header, "__TEXT", "__symbolstub1", &size) + slide; +#else + #error unknown arch +#endif +} + + +static void checkStubs(void* addr) +{ + vm_prot_t perm = getPermission(addr); + if ( (perm == 0) || ((perm & VM_PROT_WRITE) != 0) ) { + FAIL("read-only-stubs: bad permissions %d at address %p", perm, addr); + exit(0); + } +} + +int fooData = 1; + +void foo() +{ + void* stubAddr = getStubAddr(); + checkStubs(stubAddr); + barData = 0; + bar(); + checkStubs(stubAddr); +} + + diff --git a/src/dyld-apple/unit-tests/test-cases/read-only-stubs/main.c b/src/dyld-apple/unit-tests/test-cases/read-only-stubs/main.c new file mode 100644 index 000000000..b9420c7bf --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/read-only-stubs/main.c @@ -0,0 +1,122 @@ +/* + * 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include +#include +#include +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +#if __MAC_OS_X_VERSION_MIN_REQUIRED + +extern void foo(); +extern int fooData; + +static vm_prot_t getPermission(void* addr) +{ + mach_vm_address_t address = (mach_vm_address_t)(uintptr_t)addr; + kern_return_t result; + mach_port_t object_name; + vm_region_basic_info_data_64_t info; + mach_msg_type_number_t count; + mach_vm_size_t size = 4096; + + count = VM_REGION_BASIC_INFO_COUNT_64; + result = mach_vm_region(mach_task_self(), + &address, + &size, + VM_REGION_BASIC_INFO_64, + (vm_region_info_t)&info, + &count, + &object_name); + //fprintf(stderr, "result=%X, info.protection=%X\n", result, info.protection); + if ( result == KERN_SUCCESS ) + return info.protection; + return 0; +} + + +static void* getStubAddr() +{ + unsigned long size; +#if __i386__ + return getsectdata("__IMPORT", "__jump_table", &size); +#elif __ppc__ + void* p = getsectdata("__TEXT", "__picsymbolstub1", &size); + if ( p != NULL ) + return p; + return getsectdata("__TEXT", "__symbol_stub1", &size); +#elif __ppc64__ + return getsectdata("__TEXT", "__picsymbolstub1", &size); +#elif __x86_64__ + return getsectdata("__TEXT", "__symbol_stub1", &size); +#elif __arm__ + void* p = getsectdata("__TEXT", "__symbol_stub4", &size); + if ( p != NULL ) + return p; + return getsectdata("__TEXT", "__symbolstub1", &size); +#else + #error unknown arch +#endif +} + + +static void checkStubs(void* addr) +{ + vm_prot_t perm = getPermission(addr); + if ( (perm == 0) || ((perm & VM_PROT_WRITE) != 0) ) { + FAIL("read-only-stubs: bad permissions %d at address %p", perm, addr); + exit(0); + } +} + + +int main() +{ + void* stubAddr = getStubAddr(); +#if __i386__ + if ( stubAddr != NULL ) +#endif + { + checkStubs(stubAddr); + fooData = 1; + foo(); + checkStubs(stubAddr); + } + PASS("read-only-stubs"); + return EXIT_SUCCESS; +} + +#else + +int main() +{ + // iOS does not have modifiable stubs + PASS("read-only-stubs"); + return EXIT_SUCCESS; +} + +#endif + diff --git a/src/dyld-apple/unit-tests/test-cases/restrict-environ/main.c b/src/dyld-apple/unit-tests/test-cases/restrict-environ/main.c new file mode 100644 index 000000000..e68d0386d --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/restrict-environ/main.c @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2006-2009 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include // strcmp(), strncmp() +#include // dyld_process_is_restricted() + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +// +// binaries set to run as some other user id never see any DYLD_ environment variables +// + +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) +{ + // verify no DYLD_ variables + const char** p; + for(p = envp; *p != NULL; p++) { + //fprintf(stderr, "%s\n", *p); + if ( strncmp(*p, "DYLD_", 5) == 0 ) { + FAIL("restrict-environ: found %s", *p); + return EXIT_SUCCESS; + } + } + // verify same as apple parameter + ++p; + if ( apple != p ) { + FAIL("restrict-environ: apple parameter not at end of envp"); + return EXIT_SUCCESS; + } + + // verify apple parameter is not NULL and ends in main + if ( *apple == NULL ) { + FAIL("restrict-environ: apple parameter is empty"); + return EXIT_SUCCESS; + } + if ( strstr(*apple, "/main") == NULL ) { + FAIL("restrict-environ: apple parameter is not path to main"); + return EXIT_SUCCESS; + } + + // verify SPI says process is restricted + if ( !dyld_process_is_restricted() ) { + FAIL("restrict-environ: dyld_process_is_restrictet() returns false"); + return EXIT_SUCCESS; + } + + + PASS("restrict-environ"); + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/restrict-executable_path/foo.c b/src/dyld-apple/unit-tests/test-cases/restrict-executable_path/foo.c new file mode 100644 index 000000000..d2b44c453 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/restrict-executable_path/foo.c @@ -0,0 +1,5 @@ + + +int foo() { return 1; } + + diff --git a/src/dyld-apple/unit-tests/test-cases/restrict-executable_path/main.c b/src/dyld-apple/unit-tests/test-cases/restrict-executable_path/main.c new file mode 100644 index 000000000..de19bb65c --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/restrict-executable_path/main.c @@ -0,0 +1,14 @@ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include // strcmp(), strncmp() +#include // issetugid +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +extern int foo(); + +int main(int argc, const char* argv[]) +{ + foo(); + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/rpath-DYLD_FALLBACK_LIBRARY_PATH/foo.c b/src/dyld-apple/unit-tests/test-cases/rpath-DYLD_FALLBACK_LIBRARY_PATH/foo.c new file mode 100644 index 000000000..a8fe5b090 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/rpath-DYLD_FALLBACK_LIBRARY_PATH/foo.c @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2006 Apple Computer, 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@ + */ +int foo() +{ +#if BAD + return 0; +#else + return 1; +#endif +} diff --git a/src/dyld-apple/unit-tests/test-cases/rpath-DYLD_FALLBACK_LIBRARY_PATH/main.c b/src/dyld-apple/unit-tests/test-cases/rpath-DYLD_FALLBACK_LIBRARY_PATH/main.c new file mode 100644 index 000000000..72afbf73f --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/rpath-DYLD_FALLBACK_LIBRARY_PATH/main.c @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2006 Apple Computer, 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@ + */ +#include +#include +#include + +#include "test.h" + +extern int foo(); + +int main() +{ + if ( foo() ) + PASS("rpath-DYLD_FALLBACK_LIBRARY_PATH"); + else + FAIL("rpath-DYLD_FALLBACK_LIBRARY_PATH"); + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/rpath-DYLD_LIBRARY_PATH/foo.c b/src/dyld-apple/unit-tests/test-cases/rpath-DYLD_LIBRARY_PATH/foo.c new file mode 100644 index 000000000..a8fe5b090 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/rpath-DYLD_LIBRARY_PATH/foo.c @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2006 Apple Computer, 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@ + */ +int foo() +{ +#if BAD + return 0; +#else + return 1; +#endif +} diff --git a/src/dyld-apple/unit-tests/test-cases/rpath-DYLD_LIBRARY_PATH/main.c b/src/dyld-apple/unit-tests/test-cases/rpath-DYLD_LIBRARY_PATH/main.c new file mode 100644 index 000000000..01a144b43 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/rpath-DYLD_LIBRARY_PATH/main.c @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2006 Apple Computer, 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@ + */ +#include +#include +#include + +#include "test.h" + +extern int foo(); + +int main() +{ + if ( foo() ) + PASS("rpath-DYLD_LIBRARY_PATH"); + else + FAIL("rpath-DYLD_LIBRARY_PATH"); + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/rpath-DYLD_ROOT_PATH/foo.c b/src/dyld-apple/unit-tests/test-cases/rpath-DYLD_ROOT_PATH/foo.c new file mode 100644 index 000000000..c5563c574 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/rpath-DYLD_ROOT_PATH/foo.c @@ -0,0 +1,4 @@ +int foo() +{ + return 1; +} diff --git a/src/dyld-apple/unit-tests/test-cases/rpath-DYLD_ROOT_PATH/main.c b/src/dyld-apple/unit-tests/test-cases/rpath-DYLD_ROOT_PATH/main.c new file mode 100644 index 000000000..b379c3651 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/rpath-DYLD_ROOT_PATH/main.c @@ -0,0 +1,16 @@ + +#include +#include +#include + +#include "test.h" + +extern int foo(); + +int main() +{ + if ( foo() ) + return EXIT_SUCCESS; + else + return EXIT_FAILURE; +} diff --git a/src/dyld-apple/unit-tests/test-cases/rpath-LD_LIBRARY_PATH/foo.c b/src/dyld-apple/unit-tests/test-cases/rpath-LD_LIBRARY_PATH/foo.c new file mode 100644 index 000000000..79572f958 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/rpath-LD_LIBRARY_PATH/foo.c @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2006 Apple Computer, 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@ + */ +void foo() +{ +} diff --git a/src/dyld-apple/unit-tests/test-cases/rpath-LD_LIBRARY_PATH/main.c b/src/dyld-apple/unit-tests/test-cases/rpath-LD_LIBRARY_PATH/main.c new file mode 100644 index 000000000..784dee3b6 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/rpath-LD_LIBRARY_PATH/main.c @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2006 Apple Computer, 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@ + */ +#include +#include +#include + +#include "test.h" + +extern void foo(); + +int main() +{ + foo(); + PASS("rpath-LD_LIBRARY_PATH"); + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/rpath-basic/bar.c b/src/dyld-apple/unit-tests/test-cases/rpath-basic/bar.c new file mode 100644 index 000000000..b72a1a516 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/rpath-basic/bar.c @@ -0,0 +1,3 @@ +void bar() +{ +} diff --git a/src/dyld-apple/unit-tests/test-cases/rpath-basic/foo.c b/src/dyld-apple/unit-tests/test-cases/rpath-basic/foo.c new file mode 100644 index 000000000..3695dc91e --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/rpath-basic/foo.c @@ -0,0 +1,3 @@ +void foo() +{ +} diff --git a/src/dyld-apple/unit-tests/test-cases/rpath-basic/main.c b/src/dyld-apple/unit-tests/test-cases/rpath-basic/main.c new file mode 100644 index 000000000..bf6c7faa6 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/rpath-basic/main.c @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2006 Apple Computer, 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@ + */ +#include +#include +#include + +#include "test.h" + +extern void foo(); + +int main() +{ + foo(); + PASS("rpath-basic"); + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/rpath-dlopen-in-dylib/bar.c b/src/dyld-apple/unit-tests/test-cases/rpath-dlopen-in-dylib/bar.c new file mode 100644 index 000000000..17ab4f615 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/rpath-dlopen-in-dylib/bar.c @@ -0,0 +1,45 @@ +/* + * 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@ + */ + +#include +#include +#include + +#include "test.h" + +void bar() +{ + void* handle = dlopen("libfoo.dylib", RTLD_LAZY); + if ( handle == NULL ) { + FAIL("rpath-dlopen-indirect: %s", dlerror()); + exit(EXIT_SUCCESS); + } + + void* pFoo = dlsym(handle, "foo"); + if ( pFoo == NULL ) { + FAIL("rpath-dlopen-indirect: %s", dlerror()); + exit(EXIT_SUCCESS); + } +} + + diff --git a/src/dyld-apple/unit-tests/test-cases/rpath-dlopen-in-dylib/foo.c b/src/dyld-apple/unit-tests/test-cases/rpath-dlopen-in-dylib/foo.c new file mode 100644 index 000000000..3695dc91e --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/rpath-dlopen-in-dylib/foo.c @@ -0,0 +1,3 @@ +void foo() +{ +} diff --git a/src/dyld-apple/unit-tests/test-cases/rpath-dlopen-in-dylib/main.c b/src/dyld-apple/unit-tests/test-cases/rpath-dlopen-in-dylib/main.c new file mode 100644 index 000000000..1eb74298a --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/rpath-dlopen-in-dylib/main.c @@ -0,0 +1,37 @@ +/* + * 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@ + */ +#include +#include +#include + +#include "test.h" + +extern void bar(); + +int main() +{ + bar(); + + PASS("rpath-dlopen-in-dylib"); + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/rpath-dlopen-indirect/bar.c b/src/dyld-apple/unit-tests/test-cases/rpath-dlopen-indirect/bar.c new file mode 100644 index 000000000..160109b01 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/rpath-dlopen-indirect/bar.c @@ -0,0 +1,23 @@ + +#include +#include +#include + +#include "test.h" + +void bar() +{ + void* handle = dlopen("libfoo.dylib", RTLD_LAZY); + if ( handle == NULL ) { + FAIL("rpath-dlopen-indirect: %s", dlerror()); + exit(EXIT_SUCCESS); + } + + void* pFoo = dlsym(handle, "foo"); + if ( pFoo == NULL ) { + FAIL("rpath-dlopen-indirect: %s", dlerror()); + exit(EXIT_SUCCESS); + } +} + + diff --git a/src/dyld-apple/unit-tests/test-cases/rpath-dlopen-indirect/foo.c b/src/dyld-apple/unit-tests/test-cases/rpath-dlopen-indirect/foo.c new file mode 100644 index 000000000..79572f958 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/rpath-dlopen-indirect/foo.c @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2006 Apple Computer, 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@ + */ +void foo() +{ +} diff --git a/src/dyld-apple/unit-tests/test-cases/rpath-dlopen-indirect/main.c b/src/dyld-apple/unit-tests/test-cases/rpath-dlopen-indirect/main.c new file mode 100644 index 000000000..ba7efb603 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/rpath-dlopen-indirect/main.c @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2006 Apple Computer, 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@ + */ +#include +#include +#include + +#include "test.h" + +extern void bar(); + +int main() +{ + bar(); + + PASS("rpath-dlopen-indirect"); + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/rpath-dlopen-leak/foo.c b/src/dyld-apple/unit-tests/test-cases/rpath-dlopen-leak/foo.c new file mode 100644 index 000000000..79572f958 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/rpath-dlopen-leak/foo.c @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2006 Apple Computer, 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@ + */ +void foo() +{ +} diff --git a/src/dyld-apple/unit-tests/test-cases/rpath-dlopen-leak/main.c b/src/dyld-apple/unit-tests/test-cases/rpath-dlopen-leak/main.c new file mode 100644 index 000000000..993ea2501 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/rpath-dlopen-leak/main.c @@ -0,0 +1,50 @@ +/* + * 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@ + */ +#include +#include +#include +#include +#include + + +#include "test.h" + + +int main() +{ + for (int i=0; i < 100; ++i) { + void* handle = dlopen("libfoo.dylib", RTLD_LAZY); + if ( handle != NULL ) + dlclose(handle); + } + + // execute leaks command on myself + char cmd[512]; + int result = system(cmd); + if ( result == EXIT_SUCCESS ) + PASS("rpath-dlopen-leak"); + else + FAIL("rpath-dlopen-leak"); + + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/rpath-dlopen-rm-executable/foo.c b/src/dyld-apple/unit-tests/test-cases/rpath-dlopen-rm-executable/foo.c new file mode 100644 index 000000000..79572f958 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/rpath-dlopen-rm-executable/foo.c @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2006 Apple Computer, 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@ + */ +void foo() +{ +} diff --git a/src/dyld-apple/unit-tests/test-cases/rpath-dlopen-rm-executable/main.c b/src/dyld-apple/unit-tests/test-cases/rpath-dlopen-rm-executable/main.c new file mode 100644 index 000000000..9857c7156 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/rpath-dlopen-rm-executable/main.c @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2006 Apple Computer, 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@ + */ +#include +#include +#include +#include +#include + +#include "test.h" + + +int main() +{ + char buf[2048]; + uint32_t bufSize = sizeof(buf); + if ( _NSGetExecutablePath(buf, &bufSize) ) { + FAIL("rpath-dlopen-rm-exectuable: _NSGetExecutablePath()"); + return EXIT_SUCCESS; + } + + unlink(buf); + + void* handle = dlopen("libz.dylib", RTLD_LAZY); + if ( handle == NULL ) { + FAIL("rpath-dlopen-rm-exectuable: %s", dlerror()); + return EXIT_SUCCESS; + } + + PASS("rpath-dlopen-rm-exectuable"); + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/rpath-dlopen/foo.c b/src/dyld-apple/unit-tests/test-cases/rpath-dlopen/foo.c new file mode 100644 index 000000000..79572f958 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/rpath-dlopen/foo.c @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2006 Apple Computer, 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@ + */ +void foo() +{ +} diff --git a/src/dyld-apple/unit-tests/test-cases/rpath-dlopen/main.c b/src/dyld-apple/unit-tests/test-cases/rpath-dlopen/main.c new file mode 100644 index 000000000..d41390627 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/rpath-dlopen/main.c @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2006 Apple Computer, 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@ + */ +#include +#include +#include + +#include "test.h" + + +int main() +{ + void* handle = dlopen("libfoo.dylib", RTLD_LAZY); + if ( handle == NULL ) { + FAIL("rpath-dlopen: %s", dlerror()); + return EXIT_SUCCESS; + } + + void* pFoo = dlsym(handle, "foo"); + if ( pFoo == NULL ) { + FAIL("rpath-dlopen: %s", dlerror()); + return EXIT_SUCCESS; + } + + PASS("rpath-dlopen"); + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/rpath-executable_path/bar.c b/src/dyld-apple/unit-tests/test-cases/rpath-executable_path/bar.c new file mode 100644 index 000000000..31a758c5a --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/rpath-executable_path/bar.c @@ -0,0 +1,8 @@ +extern void foo(); + +void bar() +{ + foo(); +} + + diff --git a/src/dyld-apple/unit-tests/test-cases/rpath-executable_path/foo.c b/src/dyld-apple/unit-tests/test-cases/rpath-executable_path/foo.c new file mode 100644 index 000000000..16247579f --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/rpath-executable_path/foo.c @@ -0,0 +1,5 @@ + + +void foo() +{ +} diff --git a/src/dyld-apple/unit-tests/test-cases/rpath-executable_path/main.c b/src/dyld-apple/unit-tests/test-cases/rpath-executable_path/main.c new file mode 100644 index 000000000..4612793bf --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/rpath-executable_path/main.c @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2006 Apple Computer, 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@ + */ +#include +#include +#include + +#include "test.h" + +extern void bar(); + +int main() +{ + bar(); + PASS("rpath-executable_path"); + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/rpath-indirect-suid/bar.c b/src/dyld-apple/unit-tests/test-cases/rpath-indirect-suid/bar.c new file mode 100644 index 000000000..b1b8fd77b --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/rpath-indirect-suid/bar.c @@ -0,0 +1,6 @@ + +void bar() +{ +} + + diff --git a/src/dyld-apple/unit-tests/test-cases/rpath-indirect-suid/foo.c b/src/dyld-apple/unit-tests/test-cases/rpath-indirect-suid/foo.c new file mode 100644 index 000000000..3695dc91e --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/rpath-indirect-suid/foo.c @@ -0,0 +1,3 @@ +void foo() +{ +} diff --git a/src/dyld-apple/unit-tests/test-cases/rpath-indirect-suid/main.c b/src/dyld-apple/unit-tests/test-cases/rpath-indirect-suid/main.c new file mode 100644 index 000000000..bdea9f9b7 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/rpath-indirect-suid/main.c @@ -0,0 +1,18 @@ +#include +#include +#include + +#include "test.h" + +extern void foo(); + +int main() +{ + foo(); +#if DEFAULT_FAIL + FAIL("rpath-indirect-setuid"); +#else + PASS("rpath-indirect-setuid"); +#endif + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/rpath-install-name/bar.c b/src/dyld-apple/unit-tests/test-cases/rpath-install-name/bar.c new file mode 100644 index 000000000..a27d788c2 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/rpath-install-name/bar.c @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2009 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@ + */ + +extern void stuff(); + +void bar() +{ + stuff(); +} diff --git a/src/dyld-apple/unit-tests/test-cases/rpath-install-name/main.c b/src/dyld-apple/unit-tests/test-cases/rpath-install-name/main.c new file mode 100644 index 000000000..87e0ae4d7 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/rpath-install-name/main.c @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2009 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@ + */ +#include +#include +#include + +#include "test.h" + + +int main() +{ + void* h1 = dlopen("./libstuff_better.dylib", RTLD_LAZY); + if ( h1 == NULL ) { + FAIL("rpath-install-name: %s", dlerror()); + return EXIT_SUCCESS; + } + + + void* handle = dlopen("libbar.dylib", RTLD_LAZY); + if ( handle == NULL ) { + FAIL("rpath-install-name: %s", dlerror()); + return EXIT_SUCCESS; + } + + typedef void (*BarProc)(void); + BarProc pBar = (BarProc)dlsym(handle, "bar"); + if ( pBar == NULL ) { + FAIL("rpath-install-name: %s", dlerror()); + return EXIT_SUCCESS; + } + + (*pBar)(); + + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/rpath-install-name/stuff.c b/src/dyld-apple/unit-tests/test-cases/rpath-install-name/stuff.c new file mode 100644 index 000000000..6b60cb279 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/rpath-install-name/stuff.c @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2009 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@ + */ +#include +#include +#include + +#include "test.h" + + +void stuff() +{ +#if BETTER + PASS("rpath-install-name"); +#else + FAIL("rpath-install-name"); +#endif +} diff --git a/src/dyld-apple/unit-tests/test-cases/rpath-introspection/foo.c b/src/dyld-apple/unit-tests/test-cases/rpath-introspection/foo.c new file mode 100644 index 000000000..3695dc91e --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/rpath-introspection/foo.c @@ -0,0 +1,3 @@ +void foo() +{ +} diff --git a/src/dyld-apple/unit-tests/test-cases/rpath-introspection/main.c b/src/dyld-apple/unit-tests/test-cases/rpath-introspection/main.c new file mode 100644 index 000000000..6eba6dac1 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/rpath-introspection/main.c @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2012 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@ + */ +#include +#include +#include +#include +#include + +#include "test.h" + +extern void foo(); + +int main() +{ + foo(); + + // make sure foo's path does not have .. in it + int count = _dyld_image_count(); + bool found = false; + for(int i=0; i < count; ++i) { + const char* name = _dyld_get_image_name(i); + if ( strstr(name, "libfoo.dylib") == 0 ) { + found = true; + if ( strstr(name, "..") != NULL ) { + FAIL("rpath-introspection: _dyld_get_image_name(%d) returned %s", i, name); + return EXIT_SUCCESS; + } + } + } + if ( !found ) { + FAIL("rpath-introspection: _dyld_get_image_name() never returned libfoo.dylib"); + return EXIT_SUCCESS; + } + + // make sure dladdr path does not have .. in it + Dl_info info; + if ( dladdr(&foo, &info) == 0 ) { + FAIL("rpath-introspection: dladdr() failed"); + return EXIT_SUCCESS; + } + if ( strstr(info.dli_fname, "..") != NULL ) { + FAIL("rpath-introspection: dladdr() returned path with .. in it"); + return EXIT_SUCCESS; + } + + PASS("rpath-introspection"); + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/rpath-loader_path-dlopen/bar.c b/src/dyld-apple/unit-tests/test-cases/rpath-loader_path-dlopen/bar.c new file mode 100644 index 000000000..f9c07425c --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/rpath-loader_path-dlopen/bar.c @@ -0,0 +1,8 @@ +extern void baz(); + +void bar() +{ + baz(); +} + + diff --git a/src/dyld-apple/unit-tests/test-cases/rpath-loader_path-dlopen/baz.c b/src/dyld-apple/unit-tests/test-cases/rpath-loader_path-dlopen/baz.c new file mode 100644 index 000000000..0793d9790 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/rpath-loader_path-dlopen/baz.c @@ -0,0 +1,6 @@ + +void baz() +{ +} + + diff --git a/src/dyld-apple/unit-tests/test-cases/rpath-loader_path-dlopen/foo.c b/src/dyld-apple/unit-tests/test-cases/rpath-loader_path-dlopen/foo.c new file mode 100644 index 000000000..92d225798 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/rpath-loader_path-dlopen/foo.c @@ -0,0 +1,8 @@ +#include +#include +#include + +bool foo() +{ + return (dlopen("./libbar.dylib", RTLD_NOW) != NULL); +} diff --git a/src/dyld-apple/unit-tests/test-cases/rpath-loader_path-dlopen/main.c b/src/dyld-apple/unit-tests/test-cases/rpath-loader_path-dlopen/main.c new file mode 100644 index 000000000..69b4a598c --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/rpath-loader_path-dlopen/main.c @@ -0,0 +1,40 @@ +/* + * 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@ + */ +#include +#include +#include +#include + +#include "test.h" + +extern bool foo(); + +int main() +{ + if ( foo() ) + PASS("rpath-loader_path-dlopen"); + else + FAIL("rpath-loader_path-dlopen: %s", dlerror()); + + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/rpath-loader_path/foo.c b/src/dyld-apple/unit-tests/test-cases/rpath-loader_path/foo.c new file mode 100644 index 000000000..79572f958 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/rpath-loader_path/foo.c @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2006 Apple Computer, 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@ + */ +void foo() +{ +} diff --git a/src/dyld-apple/unit-tests/test-cases/rpath-loader_path/main.c b/src/dyld-apple/unit-tests/test-cases/rpath-loader_path/main.c new file mode 100644 index 000000000..bf6c7faa6 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/rpath-loader_path/main.c @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2006 Apple Computer, 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@ + */ +#include +#include +#include + +#include "test.h" + +extern void foo(); + +int main() +{ + foo(); + PASS("rpath-basic"); + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/rpath-nesting/bar.c b/src/dyld-apple/unit-tests/test-cases/rpath-nesting/bar.c new file mode 100644 index 000000000..d47ba495a --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/rpath-nesting/bar.c @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2006 Apple Computer, 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@ + */ + +void bar() +{ +} + + diff --git a/src/dyld-apple/unit-tests/test-cases/rpath-nesting/baz.c b/src/dyld-apple/unit-tests/test-cases/rpath-nesting/baz.c new file mode 100644 index 000000000..dc61b62e7 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/rpath-nesting/baz.c @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2006 Apple Computer, 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@ + */ + + +void baz() +{ +} + + diff --git a/src/dyld-apple/unit-tests/test-cases/rpath-nesting/foo.c b/src/dyld-apple/unit-tests/test-cases/rpath-nesting/foo.c new file mode 100644 index 000000000..7b46c28f7 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/rpath-nesting/foo.c @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2006 Apple Computer, 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@ + */ + +extern void bar(); +extern void baz(); + +void foo() +{ + bar(); + baz(); +} diff --git a/src/dyld-apple/unit-tests/test-cases/rpath-nesting/main.c b/src/dyld-apple/unit-tests/test-cases/rpath-nesting/main.c new file mode 100644 index 000000000..47d3ae23e --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/rpath-nesting/main.c @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2006 Apple Computer, 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@ + */ +#include +#include +#include + +#include "test.h" + +extern void foo(); + +int main() +{ + foo(); + + PASS("rpath-nesting"); + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/rpath-no-trailing-slash/foo.c b/src/dyld-apple/unit-tests/test-cases/rpath-no-trailing-slash/foo.c new file mode 100644 index 000000000..3695dc91e --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/rpath-no-trailing-slash/foo.c @@ -0,0 +1,3 @@ +void foo() +{ +} diff --git a/src/dyld-apple/unit-tests/test-cases/rpath-no-trailing-slash/main.c b/src/dyld-apple/unit-tests/test-cases/rpath-no-trailing-slash/main.c new file mode 100644 index 000000000..97bfaa29d --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/rpath-no-trailing-slash/main.c @@ -0,0 +1,14 @@ +#include +#include +#include + +#include "test.h" + +extern void foo(); + +int main() +{ + foo(); + PASS("rpath-no-trailing-slash"); + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/shared-cache-symlink/main.c b/src/dyld-apple/unit-tests/test-cases/shared-cache-symlink/main.c new file mode 100644 index 000000000..fc038c122 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/shared-cache-symlink/main.c @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +// libz.1.dylib should be in the shared cache +// libz.dylib is a symlink to libz.dylib +// we want to verify that dlopening the symlink name will use the one in the shared cache + + +int main() +{ + void* libzHandle = dlopen("/usr/lib/libz.dylib", RTLD_LAZY); + if ( libzHandle == NULL ) { + FAIL("shared-cache-symlink: dlopen(/usr/lib/libz.dylib, RTLD_LAZY) failed: %s", dlerror()); + return EXIT_SUCCESS; + } + + // get address of strcmp() + void* sym = dlsym(libzHandle, "inflate"); + if ( sym == NULL ) { + FAIL("shared-cache-symlink: dlsym(handle, \"inflate\") failed"); + return EXIT_SUCCESS; + } + + Dl_info info; + if ( dladdr(sym, &info) == 0 ) { + FAIL("shared-cache-symlink: dladdr(sym, xx) failed"); + return EXIT_SUCCESS; + } + + const struct mach_header* mh = (struct mach_header*)info.dli_fbase; + if ( mh->flags & 0x80000000 ) + PASS("shared-cache-symlink"); + else + FAIL("shared-cache-symlink: libz.dylib not loaded from shared cache"); + + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/shared-region-overlap/main.c b/src/dyld-apple/unit-tests/test-cases/shared-region-overlap/main.c new file mode 100644 index 000000000..83dfd8a4a --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/shared-region-overlap/main.c @@ -0,0 +1,12 @@ +#include // EXIT_SUCCESS + +#include "test.h" + + +int +main() +{ + return EXIT_SUCCESS; +} + + diff --git a/src/dyld-apple/unit-tests/test-cases/suid-environ/main.c b/src/dyld-apple/unit-tests/test-cases/suid-environ/main.c new file mode 100644 index 000000000..43a9749fa --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/suid-environ/main.c @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2006 Apple Computer, 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include // strcmp(), strncmp() + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +// +// binaries set to run as some other user id never see any DYLD_ environment variables +// + +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) +{ + // verify no DYLD_ variables + const char** p; + for(p = envp; *p != NULL; p++) { + //fprintf(stderr, "%s\n", *p); + if ( strncmp(*p, "DYLD_", 5) == 0 ) { + FAIL("suid-environ: found %s", *p); + return EXIT_SUCCESS; + } + } + // verify same as apple parameter + ++p; + if ( apple != p ) { + FAIL("suid-environ: apple parameter not at end of envp"); + return EXIT_SUCCESS; + } + + // verify apple parameter is not NULL and ends in main + if ( *apple == NULL ) { + FAIL("suid-environ: apple parameter is empty"); + return EXIT_SUCCESS; + } + if ( strstr(*apple, "/main") == NULL ) { + FAIL("suid-environ: apple parameter is not path to main"); + return EXIT_SUCCESS; + } + + PASS("suid-environ"); + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/suid-executable_path/foo.c b/src/dyld-apple/unit-tests/test-cases/suid-executable_path/foo.c new file mode 100644 index 000000000..d2b44c453 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/suid-executable_path/foo.c @@ -0,0 +1,5 @@ + + +int foo() { return 1; } + + diff --git a/src/dyld-apple/unit-tests/test-cases/suid-executable_path/main.c b/src/dyld-apple/unit-tests/test-cases/suid-executable_path/main.c new file mode 100644 index 000000000..aa63e85fd --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/suid-executable_path/main.c @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2006 Apple Computer, 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include // strcmp(), strncmp() +#include // issetugid +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +extern int foo(); + +int main(int argc, const char* argv[]) +{ + foo(); + if ( issetugid() ) + FAIL(argv[1]); + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/sym-link-load/base.c b/src/dyld-apple/unit-tests/test-cases/sym-link-load/base.c new file mode 100644 index 000000000..663274e1a --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/sym-link-load/base.c @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include +#include + +#include "base.h" +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +static int initCount = 0; + +void setState(int x) +{ + ++initCount; +} + + +void baseCheck() +{ + switch ( initCount ) { + case 0: + FAIL("sym-link-load no initializers run"); + break; + case 1: + PASS("sym-link-load"); + break; + case 2: + default: + FAIL("sym-link-load initializers double run (library double loaded)"); + break; + } +} + diff --git a/src/dyld-apple/unit-tests/test-cases/sym-link-load/base.h b/src/dyld-apple/unit-tests/test-cases/sym-link-load/base.h new file mode 100644 index 000000000..1dbf69785 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/sym-link-load/base.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ + + +extern void setState(int); +extern void baseCheck(); + diff --git a/src/dyld-apple/unit-tests/test-cases/sym-link-load/link.c b/src/dyld-apple/unit-tests/test-cases/sym-link-load/link.c new file mode 100644 index 000000000..42c87ba79 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/sym-link-load/link.c @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include +#include "base.h" + diff --git a/src/dyld-apple/unit-tests/test-cases/sym-link-load/main.c b/src/dyld-apple/unit-tests/test-cases/sym-link-load/main.c new file mode 100644 index 000000000..b210384ca --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/sym-link-load/main.c @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +#include "base.h" + +int main() +{ + baseCheck(); + return EXIT_SUCCESS; +} + + diff --git a/src/dyld-apple/unit-tests/test-cases/sym-link-load/test.c b/src/dyld-apple/unit-tests/test-cases/sym-link-load/test.c new file mode 100644 index 000000000..5b1e22198 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/sym-link-load/test.c @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include +#include "base.h" + + +#ifndef DO_NOTHING + +// should run second because first initiallzer in first .o file +static __attribute__((constructor)) void test_init() +{ + //fprintf(stderr, "test_init()\n"); + setState(1); +} + +#endif diff --git a/src/dyld-apple/unit-tests/test-cases/symbol-resolver-basic/foo.c b/src/dyld-apple/unit-tests/test-cases/symbol-resolver-basic/foo.c new file mode 100644 index 000000000..f7b6b4bf4 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/symbol-resolver-basic/foo.c @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2010 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@ + */ + +#include + + +static int foo_ten() +{ + return 10; +} + +static int foo_zero() +{ + return 0; +} + + +// This foo is a "resolver" function that return the actual address of "foo" +void* foo() +{ + __asm__(".symbol_resolver _foo"); // magic until we have compiler support + if ( getenv("TEN") != NULL ) + return &foo_ten; + else + return &foo_zero; +} + diff --git a/src/dyld-apple/unit-tests/test-cases/symbol-resolver-basic/foo2.c b/src/dyld-apple/unit-tests/test-cases/symbol-resolver-basic/foo2.c new file mode 100644 index 000000000..3c80d3ff0 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/symbol-resolver-basic/foo2.c @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2010 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@ + */ + +#include + +extern int foo(); + +// test that calls to resolver based function in same dylib work +int fooPlusOne() +{ + return foo() + 1; +} + diff --git a/src/dyld-apple/unit-tests/test-cases/symbol-resolver-basic/main.c b/src/dyld-apple/unit-tests/test-cases/symbol-resolver-basic/main.c new file mode 100644 index 000000000..670984ed4 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/symbol-resolver-basic/main.c @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2010 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +extern int foo(); +extern int fooPlusOne(); + + +int main() +{ + if ( getenv("TEN") != NULL ) { + if ( foo() != 10 ) + FAIL("symbol-resolver-basic: foo() != 10"); + else if ( fooPlusOne() != 11 ) + FAIL("symbol-resolver-basic: fooPlusOne() != 11"); + else + PASS("symbol-resolver-basic"); + } + else { + if ( foo() != 0 ) + FAIL("symbol-resolver-basic: foo() != 0"); + else if ( fooPlusOne() != 1 ) + FAIL("symbol-resolver-basic: fooPlusOne() != 1"); + else + PASS("symbol-resolver-basic"); + } + + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/symbol-resolver-interposed/foo.c b/src/dyld-apple/unit-tests/test-cases/symbol-resolver-interposed/foo.c new file mode 100644 index 000000000..8e9ff7eaf --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/symbol-resolver-interposed/foo.c @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2012 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@ + */ + +#include + +static int which_foo = 0; + +void set_foo(int x) +{ + which_foo = x; +} + +static int foo_ten() +{ + return 10; +} + +static int foo_zero() +{ + return 0; +} + + +// This foo is a "resolver" function that return the actual address of "foo" +void* foo() +{ + __asm__(".symbol_resolver _foo"); // magic until we have compiler support + if ( which_foo == 0 ) + return &foo_zero; + else + return &foo_ten; +} + diff --git a/src/dyld-apple/unit-tests/test-cases/symbol-resolver-interposed/foo.h b/src/dyld-apple/unit-tests/test-cases/symbol-resolver-interposed/foo.h new file mode 100644 index 000000000..2c1d57b79 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/symbol-resolver-interposed/foo.h @@ -0,0 +1,3 @@ + +extern int foo(); +extern void set_foo(int); diff --git a/src/dyld-apple/unit-tests/test-cases/symbol-resolver-interposed/main.c b/src/dyld-apple/unit-tests/test-cases/symbol-resolver-interposed/main.c new file mode 100644 index 000000000..32c1ccb02 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/symbol-resolver-interposed/main.c @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2012 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +#include "foo.h" + +int main(int argc, const char* argv[]) +{ + if ( getenv("DYLD_INSERT_LIBRARIES") == NULL ) { + if ( foo() != 0 ) { + FAIL("symbol-resolver-interposed: foo() != 0"); + return EXIT_SUCCESS; + } + } + else { + if ( foo() != 20 ) { + FAIL("symbol-resolver-interposed: foo() != 20"); + return EXIT_SUCCESS; + } + } + + PASS("symbol-resolver-interposed"); + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/symbol-resolver-interposed/myfoo.c b/src/dyld-apple/unit-tests/test-cases/symbol-resolver-interposed/myfoo.c new file mode 100644 index 000000000..62123312b --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/symbol-resolver-interposed/myfoo.c @@ -0,0 +1,11 @@ + +#include +#include "foo.h" + +int myfoo() +{ + foo(); + return 20; +} + +DYLD_INTERPOSE(myfoo, foo) diff --git a/src/dyld-apple/unit-tests/test-cases/symbol-resolver-lazy-prebound/bar.c b/src/dyld-apple/unit-tests/test-cases/symbol-resolver-lazy-prebound/bar.c new file mode 100644 index 000000000..870c431ee --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/symbol-resolver-lazy-prebound/bar.c @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2012 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@ + */ + +#include +#include +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +static bool hasBeenInited = false; + +__attribute__((constructor)) +void myInit() +{ + hasBeenInited = true; +} + + +int barGood() +{ + return 1; +} + +int barBad() +{ + return 0; +} + + +// This bar is a "resolver" function that return the actual address of "bar" +void* bar() +{ + __asm__(".symbol_resolver _bar"); // magic until we have compiler support + // Resolver function run before initializers + if ( hasBeenInited ) + return &barGood; + else + return &barBad; +} + diff --git a/src/dyld-apple/unit-tests/test-cases/symbol-resolver-lazy-prebound/foo.c b/src/dyld-apple/unit-tests/test-cases/symbol-resolver-lazy-prebound/foo.c new file mode 100644 index 000000000..bb9054caf --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/symbol-resolver-lazy-prebound/foo.c @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2010 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@ + */ + +#include + + +extern int bar(); + +int foo() +{ + return bar(); +} + diff --git a/src/dyld-apple/unit-tests/test-cases/symbol-resolver-lazy-prebound/main.c b/src/dyld-apple/unit-tests/test-cases/symbol-resolver-lazy-prebound/main.c new file mode 100644 index 000000000..212d02f4a --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/symbol-resolver-lazy-prebound/main.c @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2012 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +typedef int (*Func_t)(void); + +int main() +{ + // RTLD_NOW force lazy pointers to be bound early + void* handle = dlopen("libfoo.dylib", RTLD_NOW); + Func_t pFoo = (Func_t)dlsym(handle, "foo"); + int result = (*pFoo)(); + if ( result == 0 ) + FAIL("symbol-resolver-lazy-prebound: resolver ran before initializer"); + else + PASS("symbol-resolver-lazy-prebound"); + + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/symbol-resolver-pointer/foo.c b/src/dyld-apple/unit-tests/test-cases/symbol-resolver-pointer/foo.c new file mode 100644 index 000000000..71d67bcab --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/symbol-resolver-pointer/foo.c @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2010 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@ + */ + +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +int resolverCallCount = 0; +int realTestCallCount = 0; + + +void test$FOO(); +void test$FOO() { + //printf("test\n"); + ++realTestCallCount; +} + +void* test_chooser() __asm__("_test"); +void* test_chooser() { + __asm__(".symbol_resolver _test"); + //printf("resolver\n"); + ++resolverCallCount; + return test$FOO; +} + +void test(); +static void (*t)(void) = test; + +void check() { + t(); // call through initialized pointer + t = test; // re-assign pointer via non-lazy-poitner + t(); // call agin through pointer + test(); // call through stub + if ( resolverCallCount != 1 ) + FAIL("symbol-resolver-pointer: resolved called %d times", resolverCallCount); + else if ( realTestCallCount != 3 ) + FAIL("symbol-resolver-pointer: real test function called %d times", realTestCallCount); + else + PASS("symbol-resolver-pointer"); +} + + + + diff --git a/src/dyld-apple/unit-tests/test-cases/symbol-resolver-pointer/main.c b/src/dyld-apple/unit-tests/test-cases/symbol-resolver-pointer/main.c new file mode 100644 index 000000000..9e7a73bff --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/symbol-resolver-pointer/main.c @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2010 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +extern void check(); + + +int main() +{ + check(); + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/template/main.c b/src/dyld-apple/unit-tests/test-cases/template/main.c new file mode 100644 index 000000000..083736bb2 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/template/main.c @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +int +main(int argc, const char* argv[]) +{ + PASS("template"); + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/terminator-bounds-check/main.c b/src/dyld-apple/unit-tests/test-cases/terminator-bounds-check/main.c new file mode 100644 index 000000000..50177bba2 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/terminator-bounds-check/main.c @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2011 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +extern void* terminators[] __asm__("section$start$__DATA$__mod_term_func"); + + +__attribute__((destructor)) +void myterm() +{ + FAIL("terminator-bounds-check, terminator called"); + exit(0); +} + + + +int +main() +{ + // stomp on terminator routine to point into libSystem (_rand) + terminators[0] = &rand; + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/text-perm-alt-segment/foo.c b/src/dyld-apple/unit-tests/test-cases/text-perm-alt-segment/foo.c new file mode 100644 index 000000000..9f41b6b61 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/text-perm-alt-segment/foo.c @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2013 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@ + */ +#include // fprintf(), NULL + +int x = 5; + +__attribute__((constructor)) +void myInit() { + x = 10; +} + + +__attribute__((section("MYTEXT,myconst,regular,pure_instructions"))) +const void* p = &myInit; + + diff --git a/src/dyld-apple/unit-tests/test-cases/text-perm-alt-segment/main.c b/src/dyld-apple/unit-tests/test-cases/text-perm-alt-segment/main.c new file mode 100644 index 000000000..e6f9eedb5 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/text-perm-alt-segment/main.c @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2013 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +int main(int argc, const char* argv[]) +{ + PASS("text-reloc-alt-segment"); + return EXIT_SUCCESS; +} + + diff --git a/src/dyld-apple/unit-tests/test-cases/text-relocs-perms/foo.c b/src/dyld-apple/unit-tests/test-cases/text-relocs-perms/foo.c new file mode 100644 index 000000000..b644c3db8 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/text-relocs-perms/foo.c @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2007-2010 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include +#include +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +#if __MAC_OS_X_VERSION_MIN_REQUIRED + +static vm_prot_t getPermission(void* addr) +{ + mach_vm_address_t address = (mach_vm_address_t)(uintptr_t)addr; + kern_return_t result; + mach_port_t object_name; + vm_region_basic_info_data_64_t info; + mach_msg_type_number_t count; + mach_vm_size_t size = 4096; + + count = VM_REGION_BASIC_INFO_COUNT_64; + result = mach_vm_region(mach_task_self(), + &address, + &size, + VM_REGION_BASIC_INFO_64, + (vm_region_info_t)&info, + &count, + &object_name); + if ( result == KERN_SUCCESS ) + return info.protection; + return 0; +} + + +static void* getStubAddr() +{ +#if __LP64__ + uint64_t size; +#else + uint32_t size; +#endif + uintptr_t slide = (uintptr_t)&_mh_dylib_header; // assume dylib is zero-base so slide == load address +#if __i386__ + return getsectdatafromheader(&_mh_dylib_header, "__IMPORT", "__symbol_stub", &size) + slide; +#elif __ppc__ + return getsectdatafromheader(&_mh_dylib_header, "TEXT", "__picsymbolstub1", &size) + slide; +#elif __ppc64__ + return getsectdatafromheader_64(&_mh_dylib_header, "__TEXT", "__picsymbolstub1", &size) + slide; +#elif __x86_64__ + return getsectdatafromheader_64(&_mh_dylib_header, "__TEXT", "__symbol_stub1", &size) + slide; +#elif __arm__ + void* p = getsectdata("__TEXT", "__picsymbolstub4", (unsigned long*)&size); + if ( p != NULL ) + return getsectdatafromheader(&_mh_dylib_header, "__TEXT", "__picsymbolstub4", &size) + slide; + return getsectdatafromheader(&_mh_dylib_header, "__TEXT", "__symbolstub1", &size) + slide; +#else + #error unknown arch +#endif +} + + +static void checkStubs(void* addr) +{ + vm_prot_t perm = getPermission(addr); + //fprintf(stderr, "perm=0x%02X\n", perm); + if ( (perm == 0) || ((perm & VM_PROT_EXECUTE) == 0) ) { + FAIL("text-reloc-perms: missing exec permission 0x%0X at address %p", perm, addr); + exit(0); + } +} + + +void foo() +{ + void* stubAddr = getStubAddr(); + checkStubs(stubAddr); +} + +#else + +void foo() +{ + // iOS does not have text relocs +} + +#endif + diff --git a/src/dyld-apple/unit-tests/test-cases/text-relocs-perms/main.c b/src/dyld-apple/unit-tests/test-cases/text-relocs-perms/main.c new file mode 100644 index 000000000..fc07f175b --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/text-relocs-perms/main.c @@ -0,0 +1,122 @@ +/* + * 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include +#include +#include +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +#if __MAC_OS_X_VERSION_MIN_REQUIRED + +extern void foo(); + +static vm_prot_t getPermission(void* addr) +{ + mach_vm_address_t address = (mach_vm_address_t)(uintptr_t)addr; + kern_return_t result; + mach_port_t object_name; + vm_region_basic_info_data_64_t info; + mach_msg_type_number_t count; + mach_vm_size_t size = 4096; + + count = VM_REGION_BASIC_INFO_COUNT_64; + result = mach_vm_region(mach_task_self(), + &address, + &size, + VM_REGION_BASIC_INFO_64, + (vm_region_info_t)&info, + &count, + &object_name); + //fprintf(stderr, "result=%X, info.protection=%X\n", result, info.protection); + if ( result == KERN_SUCCESS ) + return info.protection; + return 0; +} + + +static void* getStubAddr() +{ + unsigned long size; +#if __i386__ + return getsectdata("__IMPORT", "__jump_table", &size); +#elif __ppc__ + void* p = getsectdata("__TEXT", "__picsymbolstub1", &size); + if ( p != NULL ) + return p; + return getsectdata("__TEXT", "__symbol_stub1", &size); +#elif __ppc64__ + return getsectdata("__TEXT", "__picsymbolstub1", &size); +#elif __x86_64__ + return getsectdata("__TEXT", "__symbol_stub1", &size); +#elif __arm__ + void* p = getsectdata("__TEXT", "__symbol_stub4", &size); + if ( p != NULL ) + return p; + return getsectdata("__TEXT", "__symbolstub1", &size); +#else + #error unknown arch +#endif +} + + +static void checkStubs(void* addr) +{ + vm_prot_t perm = getPermission(addr); + if ( (perm == 0) || ((perm & VM_PROT_WRITE) != 0) ) { + FAIL("read-only-stubs: bad permissions %d at address %p", perm, addr); + exit(0); + } +} + + +int main() +{ + foo(); + + void* stubAddr = getStubAddr(); +#if __i386__ + if ( stubAddr != NULL ) +#endif + { + checkStubs(stubAddr); + checkStubs(stubAddr); + } + PASS("text-relocs-perms"); + return EXIT_SUCCESS; +} + +#else + +int main() +{ + // iOS does not have text relocs + PASS("text-relocs-perm"); + return EXIT_SUCCESS; +} + +#endif + diff --git a/src/dyld-apple/unit-tests/test-cases/text-relocs/bar.c b/src/dyld-apple/unit-tests/test-cases/text-relocs/bar.c new file mode 100644 index 000000000..a87566e94 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/text-relocs/bar.c @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2005-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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +int y = 0; + +static int x = 0; + +int getx() { return x; } +void setx(int a) { x = a; } + +void bar() +{ + printf("hello\n"); +} diff --git a/src/dyld-apple/unit-tests/test-cases/text-relocs/bind.c b/src/dyld-apple/unit-tests/test-cases/text-relocs/bind.c new file mode 100644 index 000000000..fa0dd8109 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/text-relocs/bind.c @@ -0,0 +1,7 @@ + +extern int y; + +int test() +{ + return y; +} diff --git a/src/dyld-apple/unit-tests/test-cases/text-relocs/main.c b/src/dyld-apple/unit-tests/test-cases/text-relocs/main.c new file mode 100644 index 000000000..453b17bca --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/text-relocs/main.c @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2005-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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +extern int getx(); +extern void setx(int a); + +int main(int argc, const char* argv[]) +{ + setx(20); + if ( getx() != 20 ) + FAIL("text-reloc"); + else { + setx(99); + if ( getx() == 99 ) + PASS("text-reloc"); + else + FAIL("text-reloc"); + } + return EXIT_SUCCESS; +} + + diff --git a/src/dyld-apple/unit-tests/test-cases/text-relocs/space.s b/src/dyld-apple/unit-tests/test-cases/text-relocs/space.s new file mode 100644 index 000000000..0593e1e8d --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/text-relocs/space.s @@ -0,0 +1,3 @@ + .text + .align 2 +_junk: .space 1024*1024 diff --git a/src/dyld-apple/unit-tests/test-cases/threaded-flat-lookup/client.c b/src/dyld-apple/unit-tests/test-cases/threaded-flat-lookup/client.c new file mode 100644 index 000000000..72ba21892 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/threaded-flat-lookup/client.c @@ -0,0 +1,11 @@ + +extern void foo(); +extern void bar(); +extern void baz(); + +void doit() +{ + foo(); + bar(); + baz(); +} diff --git a/src/dyld-apple/unit-tests/test-cases/threaded-flat-lookup/foo.c b/src/dyld-apple/unit-tests/test-cases/threaded-flat-lookup/foo.c new file mode 100644 index 000000000..3f2cbaf44 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/threaded-flat-lookup/foo.c @@ -0,0 +1,4 @@ + +void foo() {} +void bar() {} +void baz() {} diff --git a/src/dyld-apple/unit-tests/test-cases/threaded-flat-lookup/main.c b/src/dyld-apple/unit-tests/test-cases/threaded-flat-lookup/main.c new file mode 100644 index 000000000..09ee55f0c --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/threaded-flat-lookup/main.c @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2010 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +void* h1 = NULL; +void* h2 = NULL; + +static void* work(void* ignore) +{ + for (int i=0; i < 10000; ++i) { + h1 = dlopen("libfoo1.dylib", 0); + if ( h1 == NULL ) { + FAIL("dlopen failed: %s", dlerror()); + exit(0); + } + dlclose(h2); + h2 = dlopen("libfoo2.dylib", 0); + if ( h2 == NULL ) { + FAIL("dlopen failed: %s", dlerror()); + exit(0); + } + dlclose(h1); + } + + //fprintf(stderr, "done with foos\n"); + return NULL; +} + + +int main() +{ + h2 = dlopen("libfoo2.dylib", 0); + if ( h2 == NULL ) { + FAIL("dlopen failed: %s", dlerror()); + exit(0); + } + + // other thread dlopens and closes libfoo.dylib 500 times + pthread_t other; + if ( pthread_create(&other, NULL, work, NULL) != 0 ) { + FAIL("pthread_create failed"); + exit(0); + } + + // this thread looks up a symbol 10,000 times + for (int i=0; i < 15000; ++i) { + void* handle = dlopen("libclient.dylib", 0); + if ( handle == NULL ) { + FAIL("dlopen failed: %s", dlerror()); + exit(0); + } + typedef void (*proc_t)(); + proc_t proc = dlsym(handle, "doit"); + if ( proc == NULL ) { + FAIL("dlsym failed: %s", dlerror()); + exit(0); + } + (*proc)(); + dlclose(handle); + } + //fprintf(stderr, "done with libclient\n"); + + void* result; + pthread_join(other, &result); + + PASS("threaded-flat-lookup"); + return 0; +} diff --git a/src/dyld-apple/unit-tests/test-cases/threaded-lazy-bind/foo.c b/src/dyld-apple/unit-tests/test-cases/threaded-lazy-bind/foo.c new file mode 100644 index 000000000..d647bd5ee --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/threaded-lazy-bind/foo.c @@ -0,0 +1,1001 @@ + +int do_000() { return 0; } +int do_001() { return 1; } +int do_002() { return 2; } +int do_003() { return 3; } +int do_004() { return 4; } +int do_005() { return 5; } +int do_006() { return 6; } +int do_007() { return 7; } +int do_008() { return 8; } +int do_009() { return 9; } +int do_010() { return 10; } +int do_011() { return 11; } +int do_012() { return 12; } +int do_013() { return 13; } +int do_014() { return 14; } +int do_015() { return 15; } +int do_016() { return 16; } +int do_017() { return 17; } +int do_018() { return 18; } +int do_019() { return 19; } +int do_020() { return 20; } +int do_021() { return 21; } +int do_022() { return 22; } +int do_023() { return 23; } +int do_024() { return 24; } +int do_025() { return 25; } +int do_026() { return 26; } +int do_027() { return 27; } +int do_028() { return 28; } +int do_029() { return 29; } +int do_030() { return 30; } +int do_031() { return 31; } +int do_032() { return 32; } +int do_033() { return 33; } +int do_034() { return 34; } +int do_035() { return 35; } +int do_036() { return 36; } +int do_037() { return 37; } +int do_038() { return 38; } +int do_039() { return 39; } +int do_040() { return 40; } +int do_041() { return 41; } +int do_042() { return 42; } +int do_043() { return 43; } +int do_044() { return 44; } +int do_045() { return 45; } +int do_046() { return 46; } +int do_047() { return 47; } +int do_048() { return 48; } +int do_049() { return 49; } +int do_050() { return 50; } +int do_051() { return 51; } +int do_052() { return 52; } +int do_053() { return 53; } +int do_054() { return 54; } +int do_055() { return 55; } +int do_056() { return 56; } +int do_057() { return 57; } +int do_058() { return 58; } +int do_059() { return 59; } +int do_060() { return 60; } +int do_061() { return 61; } +int do_062() { return 62; } +int do_063() { return 63; } +int do_064() { return 64; } +int do_065() { return 65; } +int do_066() { return 66; } +int do_067() { return 67; } +int do_068() { return 68; } +int do_069() { return 69; } +int do_070() { return 70; } +int do_071() { return 71; } +int do_072() { return 72; } +int do_073() { return 73; } +int do_074() { return 74; } +int do_075() { return 75; } +int do_076() { return 76; } +int do_077() { return 77; } +int do_078() { return 78; } +int do_079() { return 79; } +int do_080() { return 80; } +int do_081() { return 81; } +int do_082() { return 82; } +int do_083() { return 83; } +int do_084() { return 84; } +int do_085() { return 85; } +int do_086() { return 86; } +int do_087() { return 87; } +int do_088() { return 88; } +int do_089() { return 89; } +int do_090() { return 90; } +int do_091() { return 91; } +int do_092() { return 92; } +int do_093() { return 93; } +int do_094() { return 94; } +int do_095() { return 95; } +int do_096() { return 96; } +int do_097() { return 97; } +int do_098() { return 98; } +int do_099() { return 99; } +int do_100() { return 100; } +int do_101() { return 101; } +int do_102() { return 102; } +int do_103() { return 103; } +int do_104() { return 104; } +int do_105() { return 105; } +int do_106() { return 106; } +int do_107() { return 107; } +int do_108() { return 108; } +int do_109() { return 109; } +int do_110() { return 110; } +int do_111() { return 111; } +int do_112() { return 112; } +int do_113() { return 113; } +int do_114() { return 114; } +int do_115() { return 115; } +int do_116() { return 116; } +int do_117() { return 117; } +int do_118() { return 118; } +int do_119() { return 119; } +int do_120() { return 120; } +int do_121() { return 121; } +int do_122() { return 122; } +int do_123() { return 123; } +int do_124() { return 124; } +int do_125() { return 125; } +int do_126() { return 126; } +int do_127() { return 127; } +int do_128() { return 128; } +int do_129() { return 129; } +int do_130() { return 130; } +int do_131() { return 131; } +int do_132() { return 132; } +int do_133() { return 133; } +int do_134() { return 134; } +int do_135() { return 135; } +int do_136() { return 136; } +int do_137() { return 137; } +int do_138() { return 138; } +int do_139() { return 139; } +int do_140() { return 140; } +int do_141() { return 141; } +int do_142() { return 142; } +int do_143() { return 143; } +int do_144() { return 144; } +int do_145() { return 145; } +int do_146() { return 146; } +int do_147() { return 147; } +int do_148() { return 148; } +int do_149() { return 149; } +int do_150() { return 150; } +int do_151() { return 151; } +int do_152() { return 152; } +int do_153() { return 153; } +int do_154() { return 154; } +int do_155() { return 155; } +int do_156() { return 156; } +int do_157() { return 157; } +int do_158() { return 158; } +int do_159() { return 159; } +int do_160() { return 160; } +int do_161() { return 161; } +int do_162() { return 162; } +int do_163() { return 163; } +int do_164() { return 164; } +int do_165() { return 165; } +int do_166() { return 166; } +int do_167() { return 167; } +int do_168() { return 168; } +int do_169() { return 169; } +int do_170() { return 170; } +int do_171() { return 171; } +int do_172() { return 172; } +int do_173() { return 173; } +int do_174() { return 174; } +int do_175() { return 175; } +int do_176() { return 176; } +int do_177() { return 177; } +int do_178() { return 178; } +int do_179() { return 179; } +int do_180() { return 180; } +int do_181() { return 181; } +int do_182() { return 182; } +int do_183() { return 183; } +int do_184() { return 184; } +int do_185() { return 185; } +int do_186() { return 186; } +int do_187() { return 187; } +int do_188() { return 188; } +int do_189() { return 189; } +int do_190() { return 190; } +int do_191() { return 191; } +int do_192() { return 192; } +int do_193() { return 193; } +int do_194() { return 194; } +int do_195() { return 195; } +int do_196() { return 196; } +int do_197() { return 197; } +int do_198() { return 198; } +int do_199() { return 199; } +int do_200() { return 200; } +int do_201() { return 201; } +int do_202() { return 202; } +int do_203() { return 203; } +int do_204() { return 204; } +int do_205() { return 205; } +int do_206() { return 206; } +int do_207() { return 207; } +int do_208() { return 208; } +int do_209() { return 209; } +int do_210() { return 210; } +int do_211() { return 211; } +int do_212() { return 212; } +int do_213() { return 213; } +int do_214() { return 214; } +int do_215() { return 215; } +int do_216() { return 216; } +int do_217() { return 217; } +int do_218() { return 218; } +int do_219() { return 219; } +int do_220() { return 220; } +int do_221() { return 221; } +int do_222() { return 222; } +int do_223() { return 223; } +int do_224() { return 224; } +int do_225() { return 225; } +int do_226() { return 226; } +int do_227() { return 227; } +int do_228() { return 228; } +int do_229() { return 229; } +int do_230() { return 230; } +int do_231() { return 231; } +int do_232() { return 232; } +int do_233() { return 233; } +int do_234() { return 234; } +int do_235() { return 235; } +int do_236() { return 236; } +int do_237() { return 237; } +int do_238() { return 238; } +int do_239() { return 239; } +int do_240() { return 240; } +int do_241() { return 241; } +int do_242() { return 242; } +int do_243() { return 243; } +int do_244() { return 244; } +int do_245() { return 245; } +int do_246() { return 246; } +int do_247() { return 247; } +int do_248() { return 248; } +int do_249() { return 249; } +int do_250() { return 250; } +int do_251() { return 251; } +int do_252() { return 252; } +int do_253() { return 253; } +int do_254() { return 254; } +int do_255() { return 255; } +int do_256() { return 256; } +int do_257() { return 257; } +int do_258() { return 258; } +int do_259() { return 259; } +int do_260() { return 260; } +int do_261() { return 261; } +int do_262() { return 262; } +int do_263() { return 263; } +int do_264() { return 264; } +int do_265() { return 265; } +int do_266() { return 266; } +int do_267() { return 267; } +int do_268() { return 268; } +int do_269() { return 269; } +int do_270() { return 270; } +int do_271() { return 271; } +int do_272() { return 272; } +int do_273() { return 273; } +int do_274() { return 274; } +int do_275() { return 275; } +int do_276() { return 276; } +int do_277() { return 277; } +int do_278() { return 278; } +int do_279() { return 279; } +int do_280() { return 280; } +int do_281() { return 281; } +int do_282() { return 282; } +int do_283() { return 283; } +int do_284() { return 284; } +int do_285() { return 285; } +int do_286() { return 286; } +int do_287() { return 287; } +int do_288() { return 288; } +int do_289() { return 289; } +int do_290() { return 290; } +int do_291() { return 291; } +int do_292() { return 292; } +int do_293() { return 293; } +int do_294() { return 294; } +int do_295() { return 295; } +int do_296() { return 296; } +int do_297() { return 297; } +int do_298() { return 298; } +int do_299() { return 299; } +int do_300() { return 300; } +int do_301() { return 301; } +int do_302() { return 302; } +int do_303() { return 303; } +int do_304() { return 304; } +int do_305() { return 305; } +int do_306() { return 306; } +int do_307() { return 307; } +int do_308() { return 308; } +int do_309() { return 309; } +int do_310() { return 310; } +int do_311() { return 311; } +int do_312() { return 312; } +int do_313() { return 313; } +int do_314() { return 314; } +int do_315() { return 315; } +int do_316() { return 316; } +int do_317() { return 317; } +int do_318() { return 318; } +int do_319() { return 319; } +int do_320() { return 320; } +int do_321() { return 321; } +int do_322() { return 322; } +int do_323() { return 323; } +int do_324() { return 324; } +int do_325() { return 325; } +int do_326() { return 326; } +int do_327() { return 327; } +int do_328() { return 328; } +int do_329() { return 329; } +int do_330() { return 330; } +int do_331() { return 331; } +int do_332() { return 332; } +int do_333() { return 333; } +int do_334() { return 334; } +int do_335() { return 335; } +int do_336() { return 336; } +int do_337() { return 337; } +int do_338() { return 338; } +int do_339() { return 339; } +int do_340() { return 340; } +int do_341() { return 341; } +int do_342() { return 342; } +int do_343() { return 343; } +int do_344() { return 344; } +int do_345() { return 345; } +int do_346() { return 346; } +int do_347() { return 347; } +int do_348() { return 348; } +int do_349() { return 349; } +int do_350() { return 350; } +int do_351() { return 351; } +int do_352() { return 352; } +int do_353() { return 353; } +int do_354() { return 354; } +int do_355() { return 355; } +int do_356() { return 356; } +int do_357() { return 357; } +int do_358() { return 358; } +int do_359() { return 359; } +int do_360() { return 360; } +int do_361() { return 361; } +int do_362() { return 362; } +int do_363() { return 363; } +int do_364() { return 364; } +int do_365() { return 365; } +int do_366() { return 366; } +int do_367() { return 367; } +int do_368() { return 368; } +int do_369() { return 369; } +int do_370() { return 370; } +int do_371() { return 371; } +int do_372() { return 372; } +int do_373() { return 373; } +int do_374() { return 374; } +int do_375() { return 375; } +int do_376() { return 376; } +int do_377() { return 377; } +int do_378() { return 378; } +int do_379() { return 379; } +int do_380() { return 380; } +int do_381() { return 381; } +int do_382() { return 382; } +int do_383() { return 383; } +int do_384() { return 384; } +int do_385() { return 385; } +int do_386() { return 386; } +int do_387() { return 387; } +int do_388() { return 388; } +int do_389() { return 389; } +int do_390() { return 390; } +int do_391() { return 391; } +int do_392() { return 392; } +int do_393() { return 393; } +int do_394() { return 394; } +int do_395() { return 395; } +int do_396() { return 396; } +int do_397() { return 397; } +int do_398() { return 398; } +int do_399() { return 399; } +int do_400() { return 400; } +int do_401() { return 401; } +int do_402() { return 402; } +int do_403() { return 403; } +int do_404() { return 404; } +int do_405() { return 405; } +int do_406() { return 406; } +int do_407() { return 407; } +int do_408() { return 408; } +int do_409() { return 409; } +int do_410() { return 410; } +int do_411() { return 411; } +int do_412() { return 412; } +int do_413() { return 413; } +int do_414() { return 414; } +int do_415() { return 415; } +int do_416() { return 416; } +int do_417() { return 417; } +int do_418() { return 418; } +int do_419() { return 419; } +int do_420() { return 420; } +int do_421() { return 421; } +int do_422() { return 422; } +int do_423() { return 423; } +int do_424() { return 424; } +int do_425() { return 425; } +int do_426() { return 426; } +int do_427() { return 427; } +int do_428() { return 428; } +int do_429() { return 429; } +int do_430() { return 430; } +int do_431() { return 431; } +int do_432() { return 432; } +int do_433() { return 433; } +int do_434() { return 434; } +int do_435() { return 435; } +int do_436() { return 436; } +int do_437() { return 437; } +int do_438() { return 438; } +int do_439() { return 439; } +int do_440() { return 440; } +int do_441() { return 441; } +int do_442() { return 442; } +int do_443() { return 443; } +int do_444() { return 444; } +int do_445() { return 445; } +int do_446() { return 446; } +int do_447() { return 447; } +int do_448() { return 448; } +int do_449() { return 449; } +int do_450() { return 450; } +int do_451() { return 451; } +int do_452() { return 452; } +int do_453() { return 453; } +int do_454() { return 454; } +int do_455() { return 455; } +int do_456() { return 456; } +int do_457() { return 457; } +int do_458() { return 458; } +int do_459() { return 459; } +int do_460() { return 460; } +int do_461() { return 461; } +int do_462() { return 462; } +int do_463() { return 463; } +int do_464() { return 464; } +int do_465() { return 465; } +int do_466() { return 466; } +int do_467() { return 467; } +int do_468() { return 468; } +int do_469() { return 469; } +int do_470() { return 470; } +int do_471() { return 471; } +int do_472() { return 472; } +int do_473() { return 473; } +int do_474() { return 474; } +int do_475() { return 475; } +int do_476() { return 476; } +int do_477() { return 477; } +int do_478() { return 478; } +int do_479() { return 479; } +int do_480() { return 480; } +int do_481() { return 481; } +int do_482() { return 482; } +int do_483() { return 483; } +int do_484() { return 484; } +int do_485() { return 485; } +int do_486() { return 486; } +int do_487() { return 487; } +int do_488() { return 488; } +int do_489() { return 489; } +int do_490() { return 490; } +int do_491() { return 491; } +int do_492() { return 492; } +int do_493() { return 493; } +int do_494() { return 494; } +int do_495() { return 495; } +int do_496() { return 496; } +int do_497() { return 497; } +int do_498() { return 498; } +int do_499() { return 499; } +int do_500() { return 500; } +int do_501() { return 501; } +int do_502() { return 502; } +int do_503() { return 503; } +int do_504() { return 504; } +int do_505() { return 505; } +int do_506() { return 506; } +int do_507() { return 507; } +int do_508() { return 508; } +int do_509() { return 509; } +int do_510() { return 510; } +int do_511() { return 511; } +int do_512() { return 512; } +int do_513() { return 513; } +int do_514() { return 514; } +int do_515() { return 515; } +int do_516() { return 516; } +int do_517() { return 517; } +int do_518() { return 518; } +int do_519() { return 519; } +int do_520() { return 520; } +int do_521() { return 521; } +int do_522() { return 522; } +int do_523() { return 523; } +int do_524() { return 524; } +int do_525() { return 525; } +int do_526() { return 526; } +int do_527() { return 527; } +int do_528() { return 528; } +int do_529() { return 529; } +int do_530() { return 530; } +int do_531() { return 531; } +int do_532() { return 532; } +int do_533() { return 533; } +int do_534() { return 534; } +int do_535() { return 535; } +int do_536() { return 536; } +int do_537() { return 537; } +int do_538() { return 538; } +int do_539() { return 539; } +int do_540() { return 540; } +int do_541() { return 541; } +int do_542() { return 542; } +int do_543() { return 543; } +int do_544() { return 544; } +int do_545() { return 545; } +int do_546() { return 546; } +int do_547() { return 547; } +int do_548() { return 548; } +int do_549() { return 549; } +int do_550() { return 550; } +int do_551() { return 551; } +int do_552() { return 552; } +int do_553() { return 553; } +int do_554() { return 554; } +int do_555() { return 555; } +int do_556() { return 556; } +int do_557() { return 557; } +int do_558() { return 558; } +int do_559() { return 559; } +int do_560() { return 560; } +int do_561() { return 561; } +int do_562() { return 562; } +int do_563() { return 563; } +int do_564() { return 564; } +int do_565() { return 565; } +int do_566() { return 566; } +int do_567() { return 567; } +int do_568() { return 568; } +int do_569() { return 569; } +int do_570() { return 570; } +int do_571() { return 571; } +int do_572() { return 572; } +int do_573() { return 573; } +int do_574() { return 574; } +int do_575() { return 575; } +int do_576() { return 576; } +int do_577() { return 577; } +int do_578() { return 578; } +int do_579() { return 579; } +int do_580() { return 580; } +int do_581() { return 581; } +int do_582() { return 582; } +int do_583() { return 583; } +int do_584() { return 584; } +int do_585() { return 585; } +int do_586() { return 586; } +int do_587() { return 587; } +int do_588() { return 588; } +int do_589() { return 589; } +int do_590() { return 590; } +int do_591() { return 591; } +int do_592() { return 592; } +int do_593() { return 593; } +int do_594() { return 594; } +int do_595() { return 595; } +int do_596() { return 596; } +int do_597() { return 597; } +int do_598() { return 598; } +int do_599() { return 599; } +int do_600() { return 600; } +int do_601() { return 601; } +int do_602() { return 602; } +int do_603() { return 603; } +int do_604() { return 604; } +int do_605() { return 605; } +int do_606() { return 606; } +int do_607() { return 607; } +int do_608() { return 608; } +int do_609() { return 609; } +int do_610() { return 610; } +int do_611() { return 611; } +int do_612() { return 612; } +int do_613() { return 613; } +int do_614() { return 614; } +int do_615() { return 615; } +int do_616() { return 616; } +int do_617() { return 617; } +int do_618() { return 618; } +int do_619() { return 619; } +int do_620() { return 620; } +int do_621() { return 621; } +int do_622() { return 622; } +int do_623() { return 623; } +int do_624() { return 624; } +int do_625() { return 625; } +int do_626() { return 626; } +int do_627() { return 627; } +int do_628() { return 628; } +int do_629() { return 629; } +int do_630() { return 630; } +int do_631() { return 631; } +int do_632() { return 632; } +int do_633() { return 633; } +int do_634() { return 634; } +int do_635() { return 635; } +int do_636() { return 636; } +int do_637() { return 637; } +int do_638() { return 638; } +int do_639() { return 639; } +int do_640() { return 640; } +int do_641() { return 641; } +int do_642() { return 642; } +int do_643() { return 643; } +int do_644() { return 644; } +int do_645() { return 645; } +int do_646() { return 646; } +int do_647() { return 647; } +int do_648() { return 648; } +int do_649() { return 649; } +int do_650() { return 650; } +int do_651() { return 651; } +int do_652() { return 652; } +int do_653() { return 653; } +int do_654() { return 654; } +int do_655() { return 655; } +int do_656() { return 656; } +int do_657() { return 657; } +int do_658() { return 658; } +int do_659() { return 659; } +int do_660() { return 660; } +int do_661() { return 661; } +int do_662() { return 662; } +int do_663() { return 663; } +int do_664() { return 664; } +int do_665() { return 665; } +int do_666() { return 666; } +int do_667() { return 667; } +int do_668() { return 668; } +int do_669() { return 669; } +int do_670() { return 670; } +int do_671() { return 671; } +int do_672() { return 672; } +int do_673() { return 673; } +int do_674() { return 674; } +int do_675() { return 675; } +int do_676() { return 676; } +int do_677() { return 677; } +int do_678() { return 678; } +int do_679() { return 679; } +int do_680() { return 680; } +int do_681() { return 681; } +int do_682() { return 682; } +int do_683() { return 683; } +int do_684() { return 684; } +int do_685() { return 685; } +int do_686() { return 686; } +int do_687() { return 687; } +int do_688() { return 688; } +int do_689() { return 689; } +int do_690() { return 690; } +int do_691() { return 691; } +int do_692() { return 692; } +int do_693() { return 693; } +int do_694() { return 694; } +int do_695() { return 695; } +int do_696() { return 696; } +int do_697() { return 697; } +int do_698() { return 698; } +int do_699() { return 699; } +int do_700() { return 700; } +int do_701() { return 701; } +int do_702() { return 702; } +int do_703() { return 703; } +int do_704() { return 704; } +int do_705() { return 705; } +int do_706() { return 706; } +int do_707() { return 707; } +int do_708() { return 708; } +int do_709() { return 709; } +int do_710() { return 710; } +int do_711() { return 711; } +int do_712() { return 712; } +int do_713() { return 713; } +int do_714() { return 714; } +int do_715() { return 715; } +int do_716() { return 716; } +int do_717() { return 717; } +int do_718() { return 718; } +int do_719() { return 719; } +int do_720() { return 720; } +int do_721() { return 721; } +int do_722() { return 722; } +int do_723() { return 723; } +int do_724() { return 724; } +int do_725() { return 725; } +int do_726() { return 726; } +int do_727() { return 727; } +int do_728() { return 728; } +int do_729() { return 729; } +int do_730() { return 730; } +int do_731() { return 731; } +int do_732() { return 732; } +int do_733() { return 733; } +int do_734() { return 734; } +int do_735() { return 735; } +int do_736() { return 736; } +int do_737() { return 737; } +int do_738() { return 738; } +int do_739() { return 739; } +int do_740() { return 740; } +int do_741() { return 741; } +int do_742() { return 742; } +int do_743() { return 743; } +int do_744() { return 744; } +int do_745() { return 745; } +int do_746() { return 746; } +int do_747() { return 747; } +int do_748() { return 748; } +int do_749() { return 749; } +int do_750() { return 750; } +int do_751() { return 751; } +int do_752() { return 752; } +int do_753() { return 753; } +int do_754() { return 754; } +int do_755() { return 755; } +int do_756() { return 756; } +int do_757() { return 757; } +int do_758() { return 758; } +int do_759() { return 759; } +int do_760() { return 760; } +int do_761() { return 761; } +int do_762() { return 762; } +int do_763() { return 763; } +int do_764() { return 764; } +int do_765() { return 765; } +int do_766() { return 766; } +int do_767() { return 767; } +int do_768() { return 768; } +int do_769() { return 769; } +int do_770() { return 770; } +int do_771() { return 771; } +int do_772() { return 772; } +int do_773() { return 773; } +int do_774() { return 774; } +int do_775() { return 775; } +int do_776() { return 776; } +int do_777() { return 777; } +int do_778() { return 778; } +int do_779() { return 779; } +int do_780() { return 780; } +int do_781() { return 781; } +int do_782() { return 782; } +int do_783() { return 783; } +int do_784() { return 784; } +int do_785() { return 785; } +int do_786() { return 786; } +int do_787() { return 787; } +int do_788() { return 788; } +int do_789() { return 789; } +int do_790() { return 790; } +int do_791() { return 791; } +int do_792() { return 792; } +int do_793() { return 793; } +int do_794() { return 794; } +int do_795() { return 795; } +int do_796() { return 796; } +int do_797() { return 797; } +int do_798() { return 798; } +int do_799() { return 799; } +int do_800() { return 800; } +int do_801() { return 801; } +int do_802() { return 802; } +int do_803() { return 803; } +int do_804() { return 804; } +int do_805() { return 805; } +int do_806() { return 806; } +int do_807() { return 807; } +int do_808() { return 808; } +int do_809() { return 809; } +int do_810() { return 810; } +int do_811() { return 811; } +int do_812() { return 812; } +int do_813() { return 813; } +int do_814() { return 814; } +int do_815() { return 815; } +int do_816() { return 816; } +int do_817() { return 817; } +int do_818() { return 818; } +int do_819() { return 819; } +int do_820() { return 820; } +int do_821() { return 821; } +int do_822() { return 822; } +int do_823() { return 823; } +int do_824() { return 824; } +int do_825() { return 825; } +int do_826() { return 826; } +int do_827() { return 827; } +int do_828() { return 828; } +int do_829() { return 829; } +int do_830() { return 830; } +int do_831() { return 831; } +int do_832() { return 832; } +int do_833() { return 833; } +int do_834() { return 834; } +int do_835() { return 835; } +int do_836() { return 836; } +int do_837() { return 837; } +int do_838() { return 838; } +int do_839() { return 839; } +int do_840() { return 840; } +int do_841() { return 841; } +int do_842() { return 842; } +int do_843() { return 843; } +int do_844() { return 844; } +int do_845() { return 845; } +int do_846() { return 846; } +int do_847() { return 847; } +int do_848() { return 848; } +int do_849() { return 849; } +int do_850() { return 850; } +int do_851() { return 851; } +int do_852() { return 852; } +int do_853() { return 853; } +int do_854() { return 854; } +int do_855() { return 855; } +int do_856() { return 856; } +int do_857() { return 857; } +int do_858() { return 858; } +int do_859() { return 859; } +int do_860() { return 860; } +int do_861() { return 861; } +int do_862() { return 862; } +int do_863() { return 863; } +int do_864() { return 864; } +int do_865() { return 865; } +int do_866() { return 866; } +int do_867() { return 867; } +int do_868() { return 868; } +int do_869() { return 869; } +int do_870() { return 870; } +int do_871() { return 871; } +int do_872() { return 872; } +int do_873() { return 873; } +int do_874() { return 874; } +int do_875() { return 875; } +int do_876() { return 876; } +int do_877() { return 877; } +int do_878() { return 878; } +int do_879() { return 879; } +int do_880() { return 880; } +int do_881() { return 881; } +int do_882() { return 882; } +int do_883() { return 883; } +int do_884() { return 884; } +int do_885() { return 885; } +int do_886() { return 886; } +int do_887() { return 887; } +int do_888() { return 888; } +int do_889() { return 889; } +int do_890() { return 890; } +int do_891() { return 891; } +int do_892() { return 892; } +int do_893() { return 893; } +int do_894() { return 894; } +int do_895() { return 895; } +int do_896() { return 896; } +int do_897() { return 897; } +int do_898() { return 898; } +int do_899() { return 899; } +int do_900() { return 900; } +int do_901() { return 901; } +int do_902() { return 902; } +int do_903() { return 903; } +int do_904() { return 904; } +int do_905() { return 905; } +int do_906() { return 906; } +int do_907() { return 907; } +int do_908() { return 908; } +int do_909() { return 909; } +int do_910() { return 910; } +int do_911() { return 911; } +int do_912() { return 912; } +int do_913() { return 913; } +int do_914() { return 914; } +int do_915() { return 915; } +int do_916() { return 916; } +int do_917() { return 917; } +int do_918() { return 918; } +int do_919() { return 919; } +int do_920() { return 920; } +int do_921() { return 921; } +int do_922() { return 922; } +int do_923() { return 923; } +int do_924() { return 924; } +int do_925() { return 925; } +int do_926() { return 926; } +int do_927() { return 927; } +int do_928() { return 928; } +int do_929() { return 929; } +int do_930() { return 930; } +int do_931() { return 931; } +int do_932() { return 932; } +int do_933() { return 933; } +int do_934() { return 934; } +int do_935() { return 935; } +int do_936() { return 936; } +int do_937() { return 937; } +int do_938() { return 938; } +int do_939() { return 939; } +int do_940() { return 940; } +int do_941() { return 941; } +int do_942() { return 942; } +int do_943() { return 943; } +int do_944() { return 944; } +int do_945() { return 945; } +int do_946() { return 946; } +int do_947() { return 947; } +int do_948() { return 948; } +int do_949() { return 949; } +int do_950() { return 950; } +int do_951() { return 951; } +int do_952() { return 952; } +int do_953() { return 953; } +int do_954() { return 954; } +int do_955() { return 955; } +int do_956() { return 956; } +int do_957() { return 957; } +int do_958() { return 958; } +int do_959() { return 959; } +int do_960() { return 960; } +int do_961() { return 961; } +int do_962() { return 962; } +int do_963() { return 963; } +int do_964() { return 964; } +int do_965() { return 965; } +int do_966() { return 966; } +int do_967() { return 967; } +int do_968() { return 968; } +int do_969() { return 969; } +int do_970() { return 970; } +int do_971() { return 971; } +int do_972() { return 972; } +int do_973() { return 973; } +int do_974() { return 974; } +int do_975() { return 975; } +int do_976() { return 976; } +int do_977() { return 977; } +int do_978() { return 978; } +int do_979() { return 979; } +int do_980() { return 980; } +int do_981() { return 981; } +int do_982() { return 982; } +int do_983() { return 983; } +int do_984() { return 984; } +int do_985() { return 985; } +int do_986() { return 986; } +int do_987() { return 987; } +int do_988() { return 988; } +int do_989() { return 989; } +int do_990() { return 990; } +int do_991() { return 991; } +int do_992() { return 992; } +int do_993() { return 993; } +int do_994() { return 994; } +int do_995() { return 995; } +int do_996() { return 996; } +int do_997() { return 997; } +int do_998() { return 998; } +int do_999() { return 999; } diff --git a/src/dyld-apple/unit-tests/test-cases/threaded-lazy-bind/gen.c b/src/dyld-apple/unit-tests/test-cases/threaded-lazy-bind/gen.c new file mode 100644 index 000000000..03bd744d8 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/threaded-lazy-bind/gen.c @@ -0,0 +1,19 @@ + +#include + +int main() +{ + int i; + for (i = 0; i < 1000; ++i) { + printf("int do_%03d() { return %d; }\n", i, i); + } + + for (i = 0; i < 1000; ++i) { + printf("extern int do_%03d();\n", i); + } + for (i = 0; i < 1000; ++i) { + printf("if ( do_%03d() != %d ) { FAIL(\"iteration %d\"); exit(0); }\n", i, i, i); + } + + return 0; +} \ No newline at end of file diff --git a/src/dyld-apple/unit-tests/test-cases/threaded-lazy-bind/main.c b/src/dyld-apple/unit-tests/test-cases/threaded-lazy-bind/main.c new file mode 100644 index 000000000..ebb0e0de4 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/threaded-lazy-bind/main.c @@ -0,0 +1,2060 @@ +/* + * Copyright (c) 2009 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +/// +/// Test that lazy binding is thread safe +/// + +extern int do_000(); +extern int do_001(); +extern int do_002(); +extern int do_003(); +extern int do_004(); +extern int do_005(); +extern int do_006(); +extern int do_007(); +extern int do_008(); +extern int do_009(); +extern int do_010(); +extern int do_011(); +extern int do_012(); +extern int do_013(); +extern int do_014(); +extern int do_015(); +extern int do_016(); +extern int do_017(); +extern int do_018(); +extern int do_019(); +extern int do_020(); +extern int do_021(); +extern int do_022(); +extern int do_023(); +extern int do_024(); +extern int do_025(); +extern int do_026(); +extern int do_027(); +extern int do_028(); +extern int do_029(); +extern int do_030(); +extern int do_031(); +extern int do_032(); +extern int do_033(); +extern int do_034(); +extern int do_035(); +extern int do_036(); +extern int do_037(); +extern int do_038(); +extern int do_039(); +extern int do_040(); +extern int do_041(); +extern int do_042(); +extern int do_043(); +extern int do_044(); +extern int do_045(); +extern int do_046(); +extern int do_047(); +extern int do_048(); +extern int do_049(); +extern int do_050(); +extern int do_051(); +extern int do_052(); +extern int do_053(); +extern int do_054(); +extern int do_055(); +extern int do_056(); +extern int do_057(); +extern int do_058(); +extern int do_059(); +extern int do_060(); +extern int do_061(); +extern int do_062(); +extern int do_063(); +extern int do_064(); +extern int do_065(); +extern int do_066(); +extern int do_067(); +extern int do_068(); +extern int do_069(); +extern int do_070(); +extern int do_071(); +extern int do_072(); +extern int do_073(); +extern int do_074(); +extern int do_075(); +extern int do_076(); +extern int do_077(); +extern int do_078(); +extern int do_079(); +extern int do_080(); +extern int do_081(); +extern int do_082(); +extern int do_083(); +extern int do_084(); +extern int do_085(); +extern int do_086(); +extern int do_087(); +extern int do_088(); +extern int do_089(); +extern int do_090(); +extern int do_091(); +extern int do_092(); +extern int do_093(); +extern int do_094(); +extern int do_095(); +extern int do_096(); +extern int do_097(); +extern int do_098(); +extern int do_099(); +extern int do_100(); +extern int do_101(); +extern int do_102(); +extern int do_103(); +extern int do_104(); +extern int do_105(); +extern int do_106(); +extern int do_107(); +extern int do_108(); +extern int do_109(); +extern int do_110(); +extern int do_111(); +extern int do_112(); +extern int do_113(); +extern int do_114(); +extern int do_115(); +extern int do_116(); +extern int do_117(); +extern int do_118(); +extern int do_119(); +extern int do_120(); +extern int do_121(); +extern int do_122(); +extern int do_123(); +extern int do_124(); +extern int do_125(); +extern int do_126(); +extern int do_127(); +extern int do_128(); +extern int do_129(); +extern int do_130(); +extern int do_131(); +extern int do_132(); +extern int do_133(); +extern int do_134(); +extern int do_135(); +extern int do_136(); +extern int do_137(); +extern int do_138(); +extern int do_139(); +extern int do_140(); +extern int do_141(); +extern int do_142(); +extern int do_143(); +extern int do_144(); +extern int do_145(); +extern int do_146(); +extern int do_147(); +extern int do_148(); +extern int do_149(); +extern int do_150(); +extern int do_151(); +extern int do_152(); +extern int do_153(); +extern int do_154(); +extern int do_155(); +extern int do_156(); +extern int do_157(); +extern int do_158(); +extern int do_159(); +extern int do_160(); +extern int do_161(); +extern int do_162(); +extern int do_163(); +extern int do_164(); +extern int do_165(); +extern int do_166(); +extern int do_167(); +extern int do_168(); +extern int do_169(); +extern int do_170(); +extern int do_171(); +extern int do_172(); +extern int do_173(); +extern int do_174(); +extern int do_175(); +extern int do_176(); +extern int do_177(); +extern int do_178(); +extern int do_179(); +extern int do_180(); +extern int do_181(); +extern int do_182(); +extern int do_183(); +extern int do_184(); +extern int do_185(); +extern int do_186(); +extern int do_187(); +extern int do_188(); +extern int do_189(); +extern int do_190(); +extern int do_191(); +extern int do_192(); +extern int do_193(); +extern int do_194(); +extern int do_195(); +extern int do_196(); +extern int do_197(); +extern int do_198(); +extern int do_199(); +extern int do_200(); +extern int do_201(); +extern int do_202(); +extern int do_203(); +extern int do_204(); +extern int do_205(); +extern int do_206(); +extern int do_207(); +extern int do_208(); +extern int do_209(); +extern int do_210(); +extern int do_211(); +extern int do_212(); +extern int do_213(); +extern int do_214(); +extern int do_215(); +extern int do_216(); +extern int do_217(); +extern int do_218(); +extern int do_219(); +extern int do_220(); +extern int do_221(); +extern int do_222(); +extern int do_223(); +extern int do_224(); +extern int do_225(); +extern int do_226(); +extern int do_227(); +extern int do_228(); +extern int do_229(); +extern int do_230(); +extern int do_231(); +extern int do_232(); +extern int do_233(); +extern int do_234(); +extern int do_235(); +extern int do_236(); +extern int do_237(); +extern int do_238(); +extern int do_239(); +extern int do_240(); +extern int do_241(); +extern int do_242(); +extern int do_243(); +extern int do_244(); +extern int do_245(); +extern int do_246(); +extern int do_247(); +extern int do_248(); +extern int do_249(); +extern int do_250(); +extern int do_251(); +extern int do_252(); +extern int do_253(); +extern int do_254(); +extern int do_255(); +extern int do_256(); +extern int do_257(); +extern int do_258(); +extern int do_259(); +extern int do_260(); +extern int do_261(); +extern int do_262(); +extern int do_263(); +extern int do_264(); +extern int do_265(); +extern int do_266(); +extern int do_267(); +extern int do_268(); +extern int do_269(); +extern int do_270(); +extern int do_271(); +extern int do_272(); +extern int do_273(); +extern int do_274(); +extern int do_275(); +extern int do_276(); +extern int do_277(); +extern int do_278(); +extern int do_279(); +extern int do_280(); +extern int do_281(); +extern int do_282(); +extern int do_283(); +extern int do_284(); +extern int do_285(); +extern int do_286(); +extern int do_287(); +extern int do_288(); +extern int do_289(); +extern int do_290(); +extern int do_291(); +extern int do_292(); +extern int do_293(); +extern int do_294(); +extern int do_295(); +extern int do_296(); +extern int do_297(); +extern int do_298(); +extern int do_299(); +extern int do_300(); +extern int do_301(); +extern int do_302(); +extern int do_303(); +extern int do_304(); +extern int do_305(); +extern int do_306(); +extern int do_307(); +extern int do_308(); +extern int do_309(); +extern int do_310(); +extern int do_311(); +extern int do_312(); +extern int do_313(); +extern int do_314(); +extern int do_315(); +extern int do_316(); +extern int do_317(); +extern int do_318(); +extern int do_319(); +extern int do_320(); +extern int do_321(); +extern int do_322(); +extern int do_323(); +extern int do_324(); +extern int do_325(); +extern int do_326(); +extern int do_327(); +extern int do_328(); +extern int do_329(); +extern int do_330(); +extern int do_331(); +extern int do_332(); +extern int do_333(); +extern int do_334(); +extern int do_335(); +extern int do_336(); +extern int do_337(); +extern int do_338(); +extern int do_339(); +extern int do_340(); +extern int do_341(); +extern int do_342(); +extern int do_343(); +extern int do_344(); +extern int do_345(); +extern int do_346(); +extern int do_347(); +extern int do_348(); +extern int do_349(); +extern int do_350(); +extern int do_351(); +extern int do_352(); +extern int do_353(); +extern int do_354(); +extern int do_355(); +extern int do_356(); +extern int do_357(); +extern int do_358(); +extern int do_359(); +extern int do_360(); +extern int do_361(); +extern int do_362(); +extern int do_363(); +extern int do_364(); +extern int do_365(); +extern int do_366(); +extern int do_367(); +extern int do_368(); +extern int do_369(); +extern int do_370(); +extern int do_371(); +extern int do_372(); +extern int do_373(); +extern int do_374(); +extern int do_375(); +extern int do_376(); +extern int do_377(); +extern int do_378(); +extern int do_379(); +extern int do_380(); +extern int do_381(); +extern int do_382(); +extern int do_383(); +extern int do_384(); +extern int do_385(); +extern int do_386(); +extern int do_387(); +extern int do_388(); +extern int do_389(); +extern int do_390(); +extern int do_391(); +extern int do_392(); +extern int do_393(); +extern int do_394(); +extern int do_395(); +extern int do_396(); +extern int do_397(); +extern int do_398(); +extern int do_399(); +extern int do_400(); +extern int do_401(); +extern int do_402(); +extern int do_403(); +extern int do_404(); +extern int do_405(); +extern int do_406(); +extern int do_407(); +extern int do_408(); +extern int do_409(); +extern int do_410(); +extern int do_411(); +extern int do_412(); +extern int do_413(); +extern int do_414(); +extern int do_415(); +extern int do_416(); +extern int do_417(); +extern int do_418(); +extern int do_419(); +extern int do_420(); +extern int do_421(); +extern int do_422(); +extern int do_423(); +extern int do_424(); +extern int do_425(); +extern int do_426(); +extern int do_427(); +extern int do_428(); +extern int do_429(); +extern int do_430(); +extern int do_431(); +extern int do_432(); +extern int do_433(); +extern int do_434(); +extern int do_435(); +extern int do_436(); +extern int do_437(); +extern int do_438(); +extern int do_439(); +extern int do_440(); +extern int do_441(); +extern int do_442(); +extern int do_443(); +extern int do_444(); +extern int do_445(); +extern int do_446(); +extern int do_447(); +extern int do_448(); +extern int do_449(); +extern int do_450(); +extern int do_451(); +extern int do_452(); +extern int do_453(); +extern int do_454(); +extern int do_455(); +extern int do_456(); +extern int do_457(); +extern int do_458(); +extern int do_459(); +extern int do_460(); +extern int do_461(); +extern int do_462(); +extern int do_463(); +extern int do_464(); +extern int do_465(); +extern int do_466(); +extern int do_467(); +extern int do_468(); +extern int do_469(); +extern int do_470(); +extern int do_471(); +extern int do_472(); +extern int do_473(); +extern int do_474(); +extern int do_475(); +extern int do_476(); +extern int do_477(); +extern int do_478(); +extern int do_479(); +extern int do_480(); +extern int do_481(); +extern int do_482(); +extern int do_483(); +extern int do_484(); +extern int do_485(); +extern int do_486(); +extern int do_487(); +extern int do_488(); +extern int do_489(); +extern int do_490(); +extern int do_491(); +extern int do_492(); +extern int do_493(); +extern int do_494(); +extern int do_495(); +extern int do_496(); +extern int do_497(); +extern int do_498(); +extern int do_499(); +extern int do_500(); +extern int do_501(); +extern int do_502(); +extern int do_503(); +extern int do_504(); +extern int do_505(); +extern int do_506(); +extern int do_507(); +extern int do_508(); +extern int do_509(); +extern int do_510(); +extern int do_511(); +extern int do_512(); +extern int do_513(); +extern int do_514(); +extern int do_515(); +extern int do_516(); +extern int do_517(); +extern int do_518(); +extern int do_519(); +extern int do_520(); +extern int do_521(); +extern int do_522(); +extern int do_523(); +extern int do_524(); +extern int do_525(); +extern int do_526(); +extern int do_527(); +extern int do_528(); +extern int do_529(); +extern int do_530(); +extern int do_531(); +extern int do_532(); +extern int do_533(); +extern int do_534(); +extern int do_535(); +extern int do_536(); +extern int do_537(); +extern int do_538(); +extern int do_539(); +extern int do_540(); +extern int do_541(); +extern int do_542(); +extern int do_543(); +extern int do_544(); +extern int do_545(); +extern int do_546(); +extern int do_547(); +extern int do_548(); +extern int do_549(); +extern int do_550(); +extern int do_551(); +extern int do_552(); +extern int do_553(); +extern int do_554(); +extern int do_555(); +extern int do_556(); +extern int do_557(); +extern int do_558(); +extern int do_559(); +extern int do_560(); +extern int do_561(); +extern int do_562(); +extern int do_563(); +extern int do_564(); +extern int do_565(); +extern int do_566(); +extern int do_567(); +extern int do_568(); +extern int do_569(); +extern int do_570(); +extern int do_571(); +extern int do_572(); +extern int do_573(); +extern int do_574(); +extern int do_575(); +extern int do_576(); +extern int do_577(); +extern int do_578(); +extern int do_579(); +extern int do_580(); +extern int do_581(); +extern int do_582(); +extern int do_583(); +extern int do_584(); +extern int do_585(); +extern int do_586(); +extern int do_587(); +extern int do_588(); +extern int do_589(); +extern int do_590(); +extern int do_591(); +extern int do_592(); +extern int do_593(); +extern int do_594(); +extern int do_595(); +extern int do_596(); +extern int do_597(); +extern int do_598(); +extern int do_599(); +extern int do_600(); +extern int do_601(); +extern int do_602(); +extern int do_603(); +extern int do_604(); +extern int do_605(); +extern int do_606(); +extern int do_607(); +extern int do_608(); +extern int do_609(); +extern int do_610(); +extern int do_611(); +extern int do_612(); +extern int do_613(); +extern int do_614(); +extern int do_615(); +extern int do_616(); +extern int do_617(); +extern int do_618(); +extern int do_619(); +extern int do_620(); +extern int do_621(); +extern int do_622(); +extern int do_623(); +extern int do_624(); +extern int do_625(); +extern int do_626(); +extern int do_627(); +extern int do_628(); +extern int do_629(); +extern int do_630(); +extern int do_631(); +extern int do_632(); +extern int do_633(); +extern int do_634(); +extern int do_635(); +extern int do_636(); +extern int do_637(); +extern int do_638(); +extern int do_639(); +extern int do_640(); +extern int do_641(); +extern int do_642(); +extern int do_643(); +extern int do_644(); +extern int do_645(); +extern int do_646(); +extern int do_647(); +extern int do_648(); +extern int do_649(); +extern int do_650(); +extern int do_651(); +extern int do_652(); +extern int do_653(); +extern int do_654(); +extern int do_655(); +extern int do_656(); +extern int do_657(); +extern int do_658(); +extern int do_659(); +extern int do_660(); +extern int do_661(); +extern int do_662(); +extern int do_663(); +extern int do_664(); +extern int do_665(); +extern int do_666(); +extern int do_667(); +extern int do_668(); +extern int do_669(); +extern int do_670(); +extern int do_671(); +extern int do_672(); +extern int do_673(); +extern int do_674(); +extern int do_675(); +extern int do_676(); +extern int do_677(); +extern int do_678(); +extern int do_679(); +extern int do_680(); +extern int do_681(); +extern int do_682(); +extern int do_683(); +extern int do_684(); +extern int do_685(); +extern int do_686(); +extern int do_687(); +extern int do_688(); +extern int do_689(); +extern int do_690(); +extern int do_691(); +extern int do_692(); +extern int do_693(); +extern int do_694(); +extern int do_695(); +extern int do_696(); +extern int do_697(); +extern int do_698(); +extern int do_699(); +extern int do_700(); +extern int do_701(); +extern int do_702(); +extern int do_703(); +extern int do_704(); +extern int do_705(); +extern int do_706(); +extern int do_707(); +extern int do_708(); +extern int do_709(); +extern int do_710(); +extern int do_711(); +extern int do_712(); +extern int do_713(); +extern int do_714(); +extern int do_715(); +extern int do_716(); +extern int do_717(); +extern int do_718(); +extern int do_719(); +extern int do_720(); +extern int do_721(); +extern int do_722(); +extern int do_723(); +extern int do_724(); +extern int do_725(); +extern int do_726(); +extern int do_727(); +extern int do_728(); +extern int do_729(); +extern int do_730(); +extern int do_731(); +extern int do_732(); +extern int do_733(); +extern int do_734(); +extern int do_735(); +extern int do_736(); +extern int do_737(); +extern int do_738(); +extern int do_739(); +extern int do_740(); +extern int do_741(); +extern int do_742(); +extern int do_743(); +extern int do_744(); +extern int do_745(); +extern int do_746(); +extern int do_747(); +extern int do_748(); +extern int do_749(); +extern int do_750(); +extern int do_751(); +extern int do_752(); +extern int do_753(); +extern int do_754(); +extern int do_755(); +extern int do_756(); +extern int do_757(); +extern int do_758(); +extern int do_759(); +extern int do_760(); +extern int do_761(); +extern int do_762(); +extern int do_763(); +extern int do_764(); +extern int do_765(); +extern int do_766(); +extern int do_767(); +extern int do_768(); +extern int do_769(); +extern int do_770(); +extern int do_771(); +extern int do_772(); +extern int do_773(); +extern int do_774(); +extern int do_775(); +extern int do_776(); +extern int do_777(); +extern int do_778(); +extern int do_779(); +extern int do_780(); +extern int do_781(); +extern int do_782(); +extern int do_783(); +extern int do_784(); +extern int do_785(); +extern int do_786(); +extern int do_787(); +extern int do_788(); +extern int do_789(); +extern int do_790(); +extern int do_791(); +extern int do_792(); +extern int do_793(); +extern int do_794(); +extern int do_795(); +extern int do_796(); +extern int do_797(); +extern int do_798(); +extern int do_799(); +extern int do_800(); +extern int do_801(); +extern int do_802(); +extern int do_803(); +extern int do_804(); +extern int do_805(); +extern int do_806(); +extern int do_807(); +extern int do_808(); +extern int do_809(); +extern int do_810(); +extern int do_811(); +extern int do_812(); +extern int do_813(); +extern int do_814(); +extern int do_815(); +extern int do_816(); +extern int do_817(); +extern int do_818(); +extern int do_819(); +extern int do_820(); +extern int do_821(); +extern int do_822(); +extern int do_823(); +extern int do_824(); +extern int do_825(); +extern int do_826(); +extern int do_827(); +extern int do_828(); +extern int do_829(); +extern int do_830(); +extern int do_831(); +extern int do_832(); +extern int do_833(); +extern int do_834(); +extern int do_835(); +extern int do_836(); +extern int do_837(); +extern int do_838(); +extern int do_839(); +extern int do_840(); +extern int do_841(); +extern int do_842(); +extern int do_843(); +extern int do_844(); +extern int do_845(); +extern int do_846(); +extern int do_847(); +extern int do_848(); +extern int do_849(); +extern int do_850(); +extern int do_851(); +extern int do_852(); +extern int do_853(); +extern int do_854(); +extern int do_855(); +extern int do_856(); +extern int do_857(); +extern int do_858(); +extern int do_859(); +extern int do_860(); +extern int do_861(); +extern int do_862(); +extern int do_863(); +extern int do_864(); +extern int do_865(); +extern int do_866(); +extern int do_867(); +extern int do_868(); +extern int do_869(); +extern int do_870(); +extern int do_871(); +extern int do_872(); +extern int do_873(); +extern int do_874(); +extern int do_875(); +extern int do_876(); +extern int do_877(); +extern int do_878(); +extern int do_879(); +extern int do_880(); +extern int do_881(); +extern int do_882(); +extern int do_883(); +extern int do_884(); +extern int do_885(); +extern int do_886(); +extern int do_887(); +extern int do_888(); +extern int do_889(); +extern int do_890(); +extern int do_891(); +extern int do_892(); +extern int do_893(); +extern int do_894(); +extern int do_895(); +extern int do_896(); +extern int do_897(); +extern int do_898(); +extern int do_899(); +extern int do_900(); +extern int do_901(); +extern int do_902(); +extern int do_903(); +extern int do_904(); +extern int do_905(); +extern int do_906(); +extern int do_907(); +extern int do_908(); +extern int do_909(); +extern int do_910(); +extern int do_911(); +extern int do_912(); +extern int do_913(); +extern int do_914(); +extern int do_915(); +extern int do_916(); +extern int do_917(); +extern int do_918(); +extern int do_919(); +extern int do_920(); +extern int do_921(); +extern int do_922(); +extern int do_923(); +extern int do_924(); +extern int do_925(); +extern int do_926(); +extern int do_927(); +extern int do_928(); +extern int do_929(); +extern int do_930(); +extern int do_931(); +extern int do_932(); +extern int do_933(); +extern int do_934(); +extern int do_935(); +extern int do_936(); +extern int do_937(); +extern int do_938(); +extern int do_939(); +extern int do_940(); +extern int do_941(); +extern int do_942(); +extern int do_943(); +extern int do_944(); +extern int do_945(); +extern int do_946(); +extern int do_947(); +extern int do_948(); +extern int do_949(); +extern int do_950(); +extern int do_951(); +extern int do_952(); +extern int do_953(); +extern int do_954(); +extern int do_955(); +extern int do_956(); +extern int do_957(); +extern int do_958(); +extern int do_959(); +extern int do_960(); +extern int do_961(); +extern int do_962(); +extern int do_963(); +extern int do_964(); +extern int do_965(); +extern int do_966(); +extern int do_967(); +extern int do_968(); +extern int do_969(); +extern int do_970(); +extern int do_971(); +extern int do_972(); +extern int do_973(); +extern int do_974(); +extern int do_975(); +extern int do_976(); +extern int do_977(); +extern int do_978(); +extern int do_979(); +extern int do_980(); +extern int do_981(); +extern int do_982(); +extern int do_983(); +extern int do_984(); +extern int do_985(); +extern int do_986(); +extern int do_987(); +extern int do_988(); +extern int do_989(); +extern int do_990(); +extern int do_991(); +extern int do_992(); +extern int do_993(); +extern int do_994(); +extern int do_995(); +extern int do_996(); +extern int do_997(); +extern int do_998(); +extern int do_999(); + + +static void* work(void* ignore) +{ + if ( do_000() != 0 ) { FAIL("iteration 0"); exit(0); } + if ( do_001() != 1 ) { FAIL("iteration 1"); exit(0); } + if ( do_002() != 2 ) { FAIL("iteration 2"); exit(0); } + if ( do_003() != 3 ) { FAIL("iteration 3"); exit(0); } + if ( do_004() != 4 ) { FAIL("iteration 4"); exit(0); } + if ( do_005() != 5 ) { FAIL("iteration 5"); exit(0); } + if ( do_006() != 6 ) { FAIL("iteration 6"); exit(0); } + if ( do_007() != 7 ) { FAIL("iteration 7"); exit(0); } + if ( do_008() != 8 ) { FAIL("iteration 8"); exit(0); } + if ( do_009() != 9 ) { FAIL("iteration 9"); exit(0); } + if ( do_010() != 10 ) { FAIL("iteration 10"); exit(0); } + if ( do_011() != 11 ) { FAIL("iteration 11"); exit(0); } + if ( do_012() != 12 ) { FAIL("iteration 12"); exit(0); } + if ( do_013() != 13 ) { FAIL("iteration 13"); exit(0); } + if ( do_014() != 14 ) { FAIL("iteration 14"); exit(0); } + if ( do_015() != 15 ) { FAIL("iteration 15"); exit(0); } + if ( do_016() != 16 ) { FAIL("iteration 16"); exit(0); } + if ( do_017() != 17 ) { FAIL("iteration 17"); exit(0); } + if ( do_018() != 18 ) { FAIL("iteration 18"); exit(0); } + if ( do_019() != 19 ) { FAIL("iteration 19"); exit(0); } + if ( do_020() != 20 ) { FAIL("iteration 20"); exit(0); } + if ( do_021() != 21 ) { FAIL("iteration 21"); exit(0); } + if ( do_022() != 22 ) { FAIL("iteration 22"); exit(0); } + if ( do_023() != 23 ) { FAIL("iteration 23"); exit(0); } + if ( do_024() != 24 ) { FAIL("iteration 24"); exit(0); } + if ( do_025() != 25 ) { FAIL("iteration 25"); exit(0); } + if ( do_026() != 26 ) { FAIL("iteration 26"); exit(0); } + if ( do_027() != 27 ) { FAIL("iteration 27"); exit(0); } + if ( do_028() != 28 ) { FAIL("iteration 28"); exit(0); } + if ( do_029() != 29 ) { FAIL("iteration 29"); exit(0); } + if ( do_030() != 30 ) { FAIL("iteration 30"); exit(0); } + if ( do_031() != 31 ) { FAIL("iteration 31"); exit(0); } + if ( do_032() != 32 ) { FAIL("iteration 32"); exit(0); } + if ( do_033() != 33 ) { FAIL("iteration 33"); exit(0); } + if ( do_034() != 34 ) { FAIL("iteration 34"); exit(0); } + if ( do_035() != 35 ) { FAIL("iteration 35"); exit(0); } + if ( do_036() != 36 ) { FAIL("iteration 36"); exit(0); } + if ( do_037() != 37 ) { FAIL("iteration 37"); exit(0); } + if ( do_038() != 38 ) { FAIL("iteration 38"); exit(0); } + if ( do_039() != 39 ) { FAIL("iteration 39"); exit(0); } + if ( do_040() != 40 ) { FAIL("iteration 40"); exit(0); } + if ( do_041() != 41 ) { FAIL("iteration 41"); exit(0); } + if ( do_042() != 42 ) { FAIL("iteration 42"); exit(0); } + if ( do_043() != 43 ) { FAIL("iteration 43"); exit(0); } + if ( do_044() != 44 ) { FAIL("iteration 44"); exit(0); } + if ( do_045() != 45 ) { FAIL("iteration 45"); exit(0); } + if ( do_046() != 46 ) { FAIL("iteration 46"); exit(0); } + if ( do_047() != 47 ) { FAIL("iteration 47"); exit(0); } + if ( do_048() != 48 ) { FAIL("iteration 48"); exit(0); } + if ( do_049() != 49 ) { FAIL("iteration 49"); exit(0); } + if ( do_050() != 50 ) { FAIL("iteration 50"); exit(0); } + if ( do_051() != 51 ) { FAIL("iteration 51"); exit(0); } + if ( do_052() != 52 ) { FAIL("iteration 52"); exit(0); } + if ( do_053() != 53 ) { FAIL("iteration 53"); exit(0); } + if ( do_054() != 54 ) { FAIL("iteration 54"); exit(0); } + if ( do_055() != 55 ) { FAIL("iteration 55"); exit(0); } + if ( do_056() != 56 ) { FAIL("iteration 56"); exit(0); } + if ( do_057() != 57 ) { FAIL("iteration 57"); exit(0); } + if ( do_058() != 58 ) { FAIL("iteration 58"); exit(0); } + if ( do_059() != 59 ) { FAIL("iteration 59"); exit(0); } + if ( do_060() != 60 ) { FAIL("iteration 60"); exit(0); } + if ( do_061() != 61 ) { FAIL("iteration 61"); exit(0); } + if ( do_062() != 62 ) { FAIL("iteration 62"); exit(0); } + if ( do_063() != 63 ) { FAIL("iteration 63"); exit(0); } + if ( do_064() != 64 ) { FAIL("iteration 64"); exit(0); } + if ( do_065() != 65 ) { FAIL("iteration 65"); exit(0); } + if ( do_066() != 66 ) { FAIL("iteration 66"); exit(0); } + if ( do_067() != 67 ) { FAIL("iteration 67"); exit(0); } + if ( do_068() != 68 ) { FAIL("iteration 68"); exit(0); } + if ( do_069() != 69 ) { FAIL("iteration 69"); exit(0); } + if ( do_070() != 70 ) { FAIL("iteration 70"); exit(0); } + if ( do_071() != 71 ) { FAIL("iteration 71"); exit(0); } + if ( do_072() != 72 ) { FAIL("iteration 72"); exit(0); } + if ( do_073() != 73 ) { FAIL("iteration 73"); exit(0); } + if ( do_074() != 74 ) { FAIL("iteration 74"); exit(0); } + if ( do_075() != 75 ) { FAIL("iteration 75"); exit(0); } + if ( do_076() != 76 ) { FAIL("iteration 76"); exit(0); } + if ( do_077() != 77 ) { FAIL("iteration 77"); exit(0); } + if ( do_078() != 78 ) { FAIL("iteration 78"); exit(0); } + if ( do_079() != 79 ) { FAIL("iteration 79"); exit(0); } + if ( do_080() != 80 ) { FAIL("iteration 80"); exit(0); } + if ( do_081() != 81 ) { FAIL("iteration 81"); exit(0); } + if ( do_082() != 82 ) { FAIL("iteration 82"); exit(0); } + if ( do_083() != 83 ) { FAIL("iteration 83"); exit(0); } + if ( do_084() != 84 ) { FAIL("iteration 84"); exit(0); } + if ( do_085() != 85 ) { FAIL("iteration 85"); exit(0); } + if ( do_086() != 86 ) { FAIL("iteration 86"); exit(0); } + if ( do_087() != 87 ) { FAIL("iteration 87"); exit(0); } + if ( do_088() != 88 ) { FAIL("iteration 88"); exit(0); } + if ( do_089() != 89 ) { FAIL("iteration 89"); exit(0); } + if ( do_090() != 90 ) { FAIL("iteration 90"); exit(0); } + if ( do_091() != 91 ) { FAIL("iteration 91"); exit(0); } + if ( do_092() != 92 ) { FAIL("iteration 92"); exit(0); } + if ( do_093() != 93 ) { FAIL("iteration 93"); exit(0); } + if ( do_094() != 94 ) { FAIL("iteration 94"); exit(0); } + if ( do_095() != 95 ) { FAIL("iteration 95"); exit(0); } + if ( do_096() != 96 ) { FAIL("iteration 96"); exit(0); } + if ( do_097() != 97 ) { FAIL("iteration 97"); exit(0); } + if ( do_098() != 98 ) { FAIL("iteration 98"); exit(0); } + if ( do_099() != 99 ) { FAIL("iteration 99"); exit(0); } + if ( do_100() != 100 ) { FAIL("iteration 100"); exit(0); } + if ( do_101() != 101 ) { FAIL("iteration 101"); exit(0); } + if ( do_102() != 102 ) { FAIL("iteration 102"); exit(0); } + if ( do_103() != 103 ) { FAIL("iteration 103"); exit(0); } + if ( do_104() != 104 ) { FAIL("iteration 104"); exit(0); } + if ( do_105() != 105 ) { FAIL("iteration 105"); exit(0); } + if ( do_106() != 106 ) { FAIL("iteration 106"); exit(0); } + if ( do_107() != 107 ) { FAIL("iteration 107"); exit(0); } + if ( do_108() != 108 ) { FAIL("iteration 108"); exit(0); } + if ( do_109() != 109 ) { FAIL("iteration 109"); exit(0); } + if ( do_110() != 110 ) { FAIL("iteration 110"); exit(0); } + if ( do_111() != 111 ) { FAIL("iteration 111"); exit(0); } + if ( do_112() != 112 ) { FAIL("iteration 112"); exit(0); } + if ( do_113() != 113 ) { FAIL("iteration 113"); exit(0); } + if ( do_114() != 114 ) { FAIL("iteration 114"); exit(0); } + if ( do_115() != 115 ) { FAIL("iteration 115"); exit(0); } + if ( do_116() != 116 ) { FAIL("iteration 116"); exit(0); } + if ( do_117() != 117 ) { FAIL("iteration 117"); exit(0); } + if ( do_118() != 118 ) { FAIL("iteration 118"); exit(0); } + if ( do_119() != 119 ) { FAIL("iteration 119"); exit(0); } + if ( do_120() != 120 ) { FAIL("iteration 120"); exit(0); } + if ( do_121() != 121 ) { FAIL("iteration 121"); exit(0); } + if ( do_122() != 122 ) { FAIL("iteration 122"); exit(0); } + if ( do_123() != 123 ) { FAIL("iteration 123"); exit(0); } + if ( do_124() != 124 ) { FAIL("iteration 124"); exit(0); } + if ( do_125() != 125 ) { FAIL("iteration 125"); exit(0); } + if ( do_126() != 126 ) { FAIL("iteration 126"); exit(0); } + if ( do_127() != 127 ) { FAIL("iteration 127"); exit(0); } + if ( do_128() != 128 ) { FAIL("iteration 128"); exit(0); } + if ( do_129() != 129 ) { FAIL("iteration 129"); exit(0); } + if ( do_130() != 130 ) { FAIL("iteration 130"); exit(0); } + if ( do_131() != 131 ) { FAIL("iteration 131"); exit(0); } + if ( do_132() != 132 ) { FAIL("iteration 132"); exit(0); } + if ( do_133() != 133 ) { FAIL("iteration 133"); exit(0); } + if ( do_134() != 134 ) { FAIL("iteration 134"); exit(0); } + if ( do_135() != 135 ) { FAIL("iteration 135"); exit(0); } + if ( do_136() != 136 ) { FAIL("iteration 136"); exit(0); } + if ( do_137() != 137 ) { FAIL("iteration 137"); exit(0); } + if ( do_138() != 138 ) { FAIL("iteration 138"); exit(0); } + if ( do_139() != 139 ) { FAIL("iteration 139"); exit(0); } + if ( do_140() != 140 ) { FAIL("iteration 140"); exit(0); } + if ( do_141() != 141 ) { FAIL("iteration 141"); exit(0); } + if ( do_142() != 142 ) { FAIL("iteration 142"); exit(0); } + if ( do_143() != 143 ) { FAIL("iteration 143"); exit(0); } + if ( do_144() != 144 ) { FAIL("iteration 144"); exit(0); } + if ( do_145() != 145 ) { FAIL("iteration 145"); exit(0); } + if ( do_146() != 146 ) { FAIL("iteration 146"); exit(0); } + if ( do_147() != 147 ) { FAIL("iteration 147"); exit(0); } + if ( do_148() != 148 ) { FAIL("iteration 148"); exit(0); } + if ( do_149() != 149 ) { FAIL("iteration 149"); exit(0); } + if ( do_150() != 150 ) { FAIL("iteration 150"); exit(0); } + if ( do_151() != 151 ) { FAIL("iteration 151"); exit(0); } + if ( do_152() != 152 ) { FAIL("iteration 152"); exit(0); } + if ( do_153() != 153 ) { FAIL("iteration 153"); exit(0); } + if ( do_154() != 154 ) { FAIL("iteration 154"); exit(0); } + if ( do_155() != 155 ) { FAIL("iteration 155"); exit(0); } + if ( do_156() != 156 ) { FAIL("iteration 156"); exit(0); } + if ( do_157() != 157 ) { FAIL("iteration 157"); exit(0); } + if ( do_158() != 158 ) { FAIL("iteration 158"); exit(0); } + if ( do_159() != 159 ) { FAIL("iteration 159"); exit(0); } + if ( do_160() != 160 ) { FAIL("iteration 160"); exit(0); } + if ( do_161() != 161 ) { FAIL("iteration 161"); exit(0); } + if ( do_162() != 162 ) { FAIL("iteration 162"); exit(0); } + if ( do_163() != 163 ) { FAIL("iteration 163"); exit(0); } + if ( do_164() != 164 ) { FAIL("iteration 164"); exit(0); } + if ( do_165() != 165 ) { FAIL("iteration 165"); exit(0); } + if ( do_166() != 166 ) { FAIL("iteration 166"); exit(0); } + if ( do_167() != 167 ) { FAIL("iteration 167"); exit(0); } + if ( do_168() != 168 ) { FAIL("iteration 168"); exit(0); } + if ( do_169() != 169 ) { FAIL("iteration 169"); exit(0); } + if ( do_170() != 170 ) { FAIL("iteration 170"); exit(0); } + if ( do_171() != 171 ) { FAIL("iteration 171"); exit(0); } + if ( do_172() != 172 ) { FAIL("iteration 172"); exit(0); } + if ( do_173() != 173 ) { FAIL("iteration 173"); exit(0); } + if ( do_174() != 174 ) { FAIL("iteration 174"); exit(0); } + if ( do_175() != 175 ) { FAIL("iteration 175"); exit(0); } + if ( do_176() != 176 ) { FAIL("iteration 176"); exit(0); } + if ( do_177() != 177 ) { FAIL("iteration 177"); exit(0); } + if ( do_178() != 178 ) { FAIL("iteration 178"); exit(0); } + if ( do_179() != 179 ) { FAIL("iteration 179"); exit(0); } + if ( do_180() != 180 ) { FAIL("iteration 180"); exit(0); } + if ( do_181() != 181 ) { FAIL("iteration 181"); exit(0); } + if ( do_182() != 182 ) { FAIL("iteration 182"); exit(0); } + if ( do_183() != 183 ) { FAIL("iteration 183"); exit(0); } + if ( do_184() != 184 ) { FAIL("iteration 184"); exit(0); } + if ( do_185() != 185 ) { FAIL("iteration 185"); exit(0); } + if ( do_186() != 186 ) { FAIL("iteration 186"); exit(0); } + if ( do_187() != 187 ) { FAIL("iteration 187"); exit(0); } + if ( do_188() != 188 ) { FAIL("iteration 188"); exit(0); } + if ( do_189() != 189 ) { FAIL("iteration 189"); exit(0); } + if ( do_190() != 190 ) { FAIL("iteration 190"); exit(0); } + if ( do_191() != 191 ) { FAIL("iteration 191"); exit(0); } + if ( do_192() != 192 ) { FAIL("iteration 192"); exit(0); } + if ( do_193() != 193 ) { FAIL("iteration 193"); exit(0); } + if ( do_194() != 194 ) { FAIL("iteration 194"); exit(0); } + if ( do_195() != 195 ) { FAIL("iteration 195"); exit(0); } + if ( do_196() != 196 ) { FAIL("iteration 196"); exit(0); } + if ( do_197() != 197 ) { FAIL("iteration 197"); exit(0); } + if ( do_198() != 198 ) { FAIL("iteration 198"); exit(0); } + if ( do_199() != 199 ) { FAIL("iteration 199"); exit(0); } + if ( do_200() != 200 ) { FAIL("iteration 200"); exit(0); } + if ( do_201() != 201 ) { FAIL("iteration 201"); exit(0); } + if ( do_202() != 202 ) { FAIL("iteration 202"); exit(0); } + if ( do_203() != 203 ) { FAIL("iteration 203"); exit(0); } + if ( do_204() != 204 ) { FAIL("iteration 204"); exit(0); } + if ( do_205() != 205 ) { FAIL("iteration 205"); exit(0); } + if ( do_206() != 206 ) { FAIL("iteration 206"); exit(0); } + if ( do_207() != 207 ) { FAIL("iteration 207"); exit(0); } + if ( do_208() != 208 ) { FAIL("iteration 208"); exit(0); } + if ( do_209() != 209 ) { FAIL("iteration 209"); exit(0); } + if ( do_210() != 210 ) { FAIL("iteration 210"); exit(0); } + if ( do_211() != 211 ) { FAIL("iteration 211"); exit(0); } + if ( do_212() != 212 ) { FAIL("iteration 212"); exit(0); } + if ( do_213() != 213 ) { FAIL("iteration 213"); exit(0); } + if ( do_214() != 214 ) { FAIL("iteration 214"); exit(0); } + if ( do_215() != 215 ) { FAIL("iteration 215"); exit(0); } + if ( do_216() != 216 ) { FAIL("iteration 216"); exit(0); } + if ( do_217() != 217 ) { FAIL("iteration 217"); exit(0); } + if ( do_218() != 218 ) { FAIL("iteration 218"); exit(0); } + if ( do_219() != 219 ) { FAIL("iteration 219"); exit(0); } + if ( do_220() != 220 ) { FAIL("iteration 220"); exit(0); } + if ( do_221() != 221 ) { FAIL("iteration 221"); exit(0); } + if ( do_222() != 222 ) { FAIL("iteration 222"); exit(0); } + if ( do_223() != 223 ) { FAIL("iteration 223"); exit(0); } + if ( do_224() != 224 ) { FAIL("iteration 224"); exit(0); } + if ( do_225() != 225 ) { FAIL("iteration 225"); exit(0); } + if ( do_226() != 226 ) { FAIL("iteration 226"); exit(0); } + if ( do_227() != 227 ) { FAIL("iteration 227"); exit(0); } + if ( do_228() != 228 ) { FAIL("iteration 228"); exit(0); } + if ( do_229() != 229 ) { FAIL("iteration 229"); exit(0); } + if ( do_230() != 230 ) { FAIL("iteration 230"); exit(0); } + if ( do_231() != 231 ) { FAIL("iteration 231"); exit(0); } + if ( do_232() != 232 ) { FAIL("iteration 232"); exit(0); } + if ( do_233() != 233 ) { FAIL("iteration 233"); exit(0); } + if ( do_234() != 234 ) { FAIL("iteration 234"); exit(0); } + if ( do_235() != 235 ) { FAIL("iteration 235"); exit(0); } + if ( do_236() != 236 ) { FAIL("iteration 236"); exit(0); } + if ( do_237() != 237 ) { FAIL("iteration 237"); exit(0); } + if ( do_238() != 238 ) { FAIL("iteration 238"); exit(0); } + if ( do_239() != 239 ) { FAIL("iteration 239"); exit(0); } + if ( do_240() != 240 ) { FAIL("iteration 240"); exit(0); } + if ( do_241() != 241 ) { FAIL("iteration 241"); exit(0); } + if ( do_242() != 242 ) { FAIL("iteration 242"); exit(0); } + if ( do_243() != 243 ) { FAIL("iteration 243"); exit(0); } + if ( do_244() != 244 ) { FAIL("iteration 244"); exit(0); } + if ( do_245() != 245 ) { FAIL("iteration 245"); exit(0); } + if ( do_246() != 246 ) { FAIL("iteration 246"); exit(0); } + if ( do_247() != 247 ) { FAIL("iteration 247"); exit(0); } + if ( do_248() != 248 ) { FAIL("iteration 248"); exit(0); } + if ( do_249() != 249 ) { FAIL("iteration 249"); exit(0); } + if ( do_250() != 250 ) { FAIL("iteration 250"); exit(0); } + if ( do_251() != 251 ) { FAIL("iteration 251"); exit(0); } + if ( do_252() != 252 ) { FAIL("iteration 252"); exit(0); } + if ( do_253() != 253 ) { FAIL("iteration 253"); exit(0); } + if ( do_254() != 254 ) { FAIL("iteration 254"); exit(0); } + if ( do_255() != 255 ) { FAIL("iteration 255"); exit(0); } + if ( do_256() != 256 ) { FAIL("iteration 256"); exit(0); } + if ( do_257() != 257 ) { FAIL("iteration 257"); exit(0); } + if ( do_258() != 258 ) { FAIL("iteration 258"); exit(0); } + if ( do_259() != 259 ) { FAIL("iteration 259"); exit(0); } + if ( do_260() != 260 ) { FAIL("iteration 260"); exit(0); } + if ( do_261() != 261 ) { FAIL("iteration 261"); exit(0); } + if ( do_262() != 262 ) { FAIL("iteration 262"); exit(0); } + if ( do_263() != 263 ) { FAIL("iteration 263"); exit(0); } + if ( do_264() != 264 ) { FAIL("iteration 264"); exit(0); } + if ( do_265() != 265 ) { FAIL("iteration 265"); exit(0); } + if ( do_266() != 266 ) { FAIL("iteration 266"); exit(0); } + if ( do_267() != 267 ) { FAIL("iteration 267"); exit(0); } + if ( do_268() != 268 ) { FAIL("iteration 268"); exit(0); } + if ( do_269() != 269 ) { FAIL("iteration 269"); exit(0); } + if ( do_270() != 270 ) { FAIL("iteration 270"); exit(0); } + if ( do_271() != 271 ) { FAIL("iteration 271"); exit(0); } + if ( do_272() != 272 ) { FAIL("iteration 272"); exit(0); } + if ( do_273() != 273 ) { FAIL("iteration 273"); exit(0); } + if ( do_274() != 274 ) { FAIL("iteration 274"); exit(0); } + if ( do_275() != 275 ) { FAIL("iteration 275"); exit(0); } + if ( do_276() != 276 ) { FAIL("iteration 276"); exit(0); } + if ( do_277() != 277 ) { FAIL("iteration 277"); exit(0); } + if ( do_278() != 278 ) { FAIL("iteration 278"); exit(0); } + if ( do_279() != 279 ) { FAIL("iteration 279"); exit(0); } + if ( do_280() != 280 ) { FAIL("iteration 280"); exit(0); } + if ( do_281() != 281 ) { FAIL("iteration 281"); exit(0); } + if ( do_282() != 282 ) { FAIL("iteration 282"); exit(0); } + if ( do_283() != 283 ) { FAIL("iteration 283"); exit(0); } + if ( do_284() != 284 ) { FAIL("iteration 284"); exit(0); } + if ( do_285() != 285 ) { FAIL("iteration 285"); exit(0); } + if ( do_286() != 286 ) { FAIL("iteration 286"); exit(0); } + if ( do_287() != 287 ) { FAIL("iteration 287"); exit(0); } + if ( do_288() != 288 ) { FAIL("iteration 288"); exit(0); } + if ( do_289() != 289 ) { FAIL("iteration 289"); exit(0); } + if ( do_290() != 290 ) { FAIL("iteration 290"); exit(0); } + if ( do_291() != 291 ) { FAIL("iteration 291"); exit(0); } + if ( do_292() != 292 ) { FAIL("iteration 292"); exit(0); } + if ( do_293() != 293 ) { FAIL("iteration 293"); exit(0); } + if ( do_294() != 294 ) { FAIL("iteration 294"); exit(0); } + if ( do_295() != 295 ) { FAIL("iteration 295"); exit(0); } + if ( do_296() != 296 ) { FAIL("iteration 296"); exit(0); } + if ( do_297() != 297 ) { FAIL("iteration 297"); exit(0); } + if ( do_298() != 298 ) { FAIL("iteration 298"); exit(0); } + if ( do_299() != 299 ) { FAIL("iteration 299"); exit(0); } + if ( do_300() != 300 ) { FAIL("iteration 300"); exit(0); } + if ( do_301() != 301 ) { FAIL("iteration 301"); exit(0); } + if ( do_302() != 302 ) { FAIL("iteration 302"); exit(0); } + if ( do_303() != 303 ) { FAIL("iteration 303"); exit(0); } + if ( do_304() != 304 ) { FAIL("iteration 304"); exit(0); } + if ( do_305() != 305 ) { FAIL("iteration 305"); exit(0); } + if ( do_306() != 306 ) { FAIL("iteration 306"); exit(0); } + if ( do_307() != 307 ) { FAIL("iteration 307"); exit(0); } + if ( do_308() != 308 ) { FAIL("iteration 308"); exit(0); } + if ( do_309() != 309 ) { FAIL("iteration 309"); exit(0); } + if ( do_310() != 310 ) { FAIL("iteration 310"); exit(0); } + if ( do_311() != 311 ) { FAIL("iteration 311"); exit(0); } + if ( do_312() != 312 ) { FAIL("iteration 312"); exit(0); } + if ( do_313() != 313 ) { FAIL("iteration 313"); exit(0); } + if ( do_314() != 314 ) { FAIL("iteration 314"); exit(0); } + if ( do_315() != 315 ) { FAIL("iteration 315"); exit(0); } + if ( do_316() != 316 ) { FAIL("iteration 316"); exit(0); } + if ( do_317() != 317 ) { FAIL("iteration 317"); exit(0); } + if ( do_318() != 318 ) { FAIL("iteration 318"); exit(0); } + if ( do_319() != 319 ) { FAIL("iteration 319"); exit(0); } + if ( do_320() != 320 ) { FAIL("iteration 320"); exit(0); } + if ( do_321() != 321 ) { FAIL("iteration 321"); exit(0); } + if ( do_322() != 322 ) { FAIL("iteration 322"); exit(0); } + if ( do_323() != 323 ) { FAIL("iteration 323"); exit(0); } + if ( do_324() != 324 ) { FAIL("iteration 324"); exit(0); } + if ( do_325() != 325 ) { FAIL("iteration 325"); exit(0); } + if ( do_326() != 326 ) { FAIL("iteration 326"); exit(0); } + if ( do_327() != 327 ) { FAIL("iteration 327"); exit(0); } + if ( do_328() != 328 ) { FAIL("iteration 328"); exit(0); } + if ( do_329() != 329 ) { FAIL("iteration 329"); exit(0); } + if ( do_330() != 330 ) { FAIL("iteration 330"); exit(0); } + if ( do_331() != 331 ) { FAIL("iteration 331"); exit(0); } + if ( do_332() != 332 ) { FAIL("iteration 332"); exit(0); } + if ( do_333() != 333 ) { FAIL("iteration 333"); exit(0); } + if ( do_334() != 334 ) { FAIL("iteration 334"); exit(0); } + if ( do_335() != 335 ) { FAIL("iteration 335"); exit(0); } + if ( do_336() != 336 ) { FAIL("iteration 336"); exit(0); } + if ( do_337() != 337 ) { FAIL("iteration 337"); exit(0); } + if ( do_338() != 338 ) { FAIL("iteration 338"); exit(0); } + if ( do_339() != 339 ) { FAIL("iteration 339"); exit(0); } + if ( do_340() != 340 ) { FAIL("iteration 340"); exit(0); } + if ( do_341() != 341 ) { FAIL("iteration 341"); exit(0); } + if ( do_342() != 342 ) { FAIL("iteration 342"); exit(0); } + if ( do_343() != 343 ) { FAIL("iteration 343"); exit(0); } + if ( do_344() != 344 ) { FAIL("iteration 344"); exit(0); } + if ( do_345() != 345 ) { FAIL("iteration 345"); exit(0); } + if ( do_346() != 346 ) { FAIL("iteration 346"); exit(0); } + if ( do_347() != 347 ) { FAIL("iteration 347"); exit(0); } + if ( do_348() != 348 ) { FAIL("iteration 348"); exit(0); } + if ( do_349() != 349 ) { FAIL("iteration 349"); exit(0); } + if ( do_350() != 350 ) { FAIL("iteration 350"); exit(0); } + if ( do_351() != 351 ) { FAIL("iteration 351"); exit(0); } + if ( do_352() != 352 ) { FAIL("iteration 352"); exit(0); } + if ( do_353() != 353 ) { FAIL("iteration 353"); exit(0); } + if ( do_354() != 354 ) { FAIL("iteration 354"); exit(0); } + if ( do_355() != 355 ) { FAIL("iteration 355"); exit(0); } + if ( do_356() != 356 ) { FAIL("iteration 356"); exit(0); } + if ( do_357() != 357 ) { FAIL("iteration 357"); exit(0); } + if ( do_358() != 358 ) { FAIL("iteration 358"); exit(0); } + if ( do_359() != 359 ) { FAIL("iteration 359"); exit(0); } + if ( do_360() != 360 ) { FAIL("iteration 360"); exit(0); } + if ( do_361() != 361 ) { FAIL("iteration 361"); exit(0); } + if ( do_362() != 362 ) { FAIL("iteration 362"); exit(0); } + if ( do_363() != 363 ) { FAIL("iteration 363"); exit(0); } + if ( do_364() != 364 ) { FAIL("iteration 364"); exit(0); } + if ( do_365() != 365 ) { FAIL("iteration 365"); exit(0); } + if ( do_366() != 366 ) { FAIL("iteration 366"); exit(0); } + if ( do_367() != 367 ) { FAIL("iteration 367"); exit(0); } + if ( do_368() != 368 ) { FAIL("iteration 368"); exit(0); } + if ( do_369() != 369 ) { FAIL("iteration 369"); exit(0); } + if ( do_370() != 370 ) { FAIL("iteration 370"); exit(0); } + if ( do_371() != 371 ) { FAIL("iteration 371"); exit(0); } + if ( do_372() != 372 ) { FAIL("iteration 372"); exit(0); } + if ( do_373() != 373 ) { FAIL("iteration 373"); exit(0); } + if ( do_374() != 374 ) { FAIL("iteration 374"); exit(0); } + if ( do_375() != 375 ) { FAIL("iteration 375"); exit(0); } + if ( do_376() != 376 ) { FAIL("iteration 376"); exit(0); } + if ( do_377() != 377 ) { FAIL("iteration 377"); exit(0); } + if ( do_378() != 378 ) { FAIL("iteration 378"); exit(0); } + if ( do_379() != 379 ) { FAIL("iteration 379"); exit(0); } + if ( do_380() != 380 ) { FAIL("iteration 380"); exit(0); } + if ( do_381() != 381 ) { FAIL("iteration 381"); exit(0); } + if ( do_382() != 382 ) { FAIL("iteration 382"); exit(0); } + if ( do_383() != 383 ) { FAIL("iteration 383"); exit(0); } + if ( do_384() != 384 ) { FAIL("iteration 384"); exit(0); } + if ( do_385() != 385 ) { FAIL("iteration 385"); exit(0); } + if ( do_386() != 386 ) { FAIL("iteration 386"); exit(0); } + if ( do_387() != 387 ) { FAIL("iteration 387"); exit(0); } + if ( do_388() != 388 ) { FAIL("iteration 388"); exit(0); } + if ( do_389() != 389 ) { FAIL("iteration 389"); exit(0); } + if ( do_390() != 390 ) { FAIL("iteration 390"); exit(0); } + if ( do_391() != 391 ) { FAIL("iteration 391"); exit(0); } + if ( do_392() != 392 ) { FAIL("iteration 392"); exit(0); } + if ( do_393() != 393 ) { FAIL("iteration 393"); exit(0); } + if ( do_394() != 394 ) { FAIL("iteration 394"); exit(0); } + if ( do_395() != 395 ) { FAIL("iteration 395"); exit(0); } + if ( do_396() != 396 ) { FAIL("iteration 396"); exit(0); } + if ( do_397() != 397 ) { FAIL("iteration 397"); exit(0); } + if ( do_398() != 398 ) { FAIL("iteration 398"); exit(0); } + if ( do_399() != 399 ) { FAIL("iteration 399"); exit(0); } + if ( do_400() != 400 ) { FAIL("iteration 400"); exit(0); } + if ( do_401() != 401 ) { FAIL("iteration 401"); exit(0); } + if ( do_402() != 402 ) { FAIL("iteration 402"); exit(0); } + if ( do_403() != 403 ) { FAIL("iteration 403"); exit(0); } + if ( do_404() != 404 ) { FAIL("iteration 404"); exit(0); } + if ( do_405() != 405 ) { FAIL("iteration 405"); exit(0); } + if ( do_406() != 406 ) { FAIL("iteration 406"); exit(0); } + if ( do_407() != 407 ) { FAIL("iteration 407"); exit(0); } + if ( do_408() != 408 ) { FAIL("iteration 408"); exit(0); } + if ( do_409() != 409 ) { FAIL("iteration 409"); exit(0); } + if ( do_410() != 410 ) { FAIL("iteration 410"); exit(0); } + if ( do_411() != 411 ) { FAIL("iteration 411"); exit(0); } + if ( do_412() != 412 ) { FAIL("iteration 412"); exit(0); } + if ( do_413() != 413 ) { FAIL("iteration 413"); exit(0); } + if ( do_414() != 414 ) { FAIL("iteration 414"); exit(0); } + if ( do_415() != 415 ) { FAIL("iteration 415"); exit(0); } + if ( do_416() != 416 ) { FAIL("iteration 416"); exit(0); } + if ( do_417() != 417 ) { FAIL("iteration 417"); exit(0); } + if ( do_418() != 418 ) { FAIL("iteration 418"); exit(0); } + if ( do_419() != 419 ) { FAIL("iteration 419"); exit(0); } + if ( do_420() != 420 ) { FAIL("iteration 420"); exit(0); } + if ( do_421() != 421 ) { FAIL("iteration 421"); exit(0); } + if ( do_422() != 422 ) { FAIL("iteration 422"); exit(0); } + if ( do_423() != 423 ) { FAIL("iteration 423"); exit(0); } + if ( do_424() != 424 ) { FAIL("iteration 424"); exit(0); } + if ( do_425() != 425 ) { FAIL("iteration 425"); exit(0); } + if ( do_426() != 426 ) { FAIL("iteration 426"); exit(0); } + if ( do_427() != 427 ) { FAIL("iteration 427"); exit(0); } + if ( do_428() != 428 ) { FAIL("iteration 428"); exit(0); } + if ( do_429() != 429 ) { FAIL("iteration 429"); exit(0); } + if ( do_430() != 430 ) { FAIL("iteration 430"); exit(0); } + if ( do_431() != 431 ) { FAIL("iteration 431"); exit(0); } + if ( do_432() != 432 ) { FAIL("iteration 432"); exit(0); } + if ( do_433() != 433 ) { FAIL("iteration 433"); exit(0); } + if ( do_434() != 434 ) { FAIL("iteration 434"); exit(0); } + if ( do_435() != 435 ) { FAIL("iteration 435"); exit(0); } + if ( do_436() != 436 ) { FAIL("iteration 436"); exit(0); } + if ( do_437() != 437 ) { FAIL("iteration 437"); exit(0); } + if ( do_438() != 438 ) { FAIL("iteration 438"); exit(0); } + if ( do_439() != 439 ) { FAIL("iteration 439"); exit(0); } + if ( do_440() != 440 ) { FAIL("iteration 440"); exit(0); } + if ( do_441() != 441 ) { FAIL("iteration 441"); exit(0); } + if ( do_442() != 442 ) { FAIL("iteration 442"); exit(0); } + if ( do_443() != 443 ) { FAIL("iteration 443"); exit(0); } + if ( do_444() != 444 ) { FAIL("iteration 444"); exit(0); } + if ( do_445() != 445 ) { FAIL("iteration 445"); exit(0); } + if ( do_446() != 446 ) { FAIL("iteration 446"); exit(0); } + if ( do_447() != 447 ) { FAIL("iteration 447"); exit(0); } + if ( do_448() != 448 ) { FAIL("iteration 448"); exit(0); } + if ( do_449() != 449 ) { FAIL("iteration 449"); exit(0); } + if ( do_450() != 450 ) { FAIL("iteration 450"); exit(0); } + if ( do_451() != 451 ) { FAIL("iteration 451"); exit(0); } + if ( do_452() != 452 ) { FAIL("iteration 452"); exit(0); } + if ( do_453() != 453 ) { FAIL("iteration 453"); exit(0); } + if ( do_454() != 454 ) { FAIL("iteration 454"); exit(0); } + if ( do_455() != 455 ) { FAIL("iteration 455"); exit(0); } + if ( do_456() != 456 ) { FAIL("iteration 456"); exit(0); } + if ( do_457() != 457 ) { FAIL("iteration 457"); exit(0); } + if ( do_458() != 458 ) { FAIL("iteration 458"); exit(0); } + if ( do_459() != 459 ) { FAIL("iteration 459"); exit(0); } + if ( do_460() != 460 ) { FAIL("iteration 460"); exit(0); } + if ( do_461() != 461 ) { FAIL("iteration 461"); exit(0); } + if ( do_462() != 462 ) { FAIL("iteration 462"); exit(0); } + if ( do_463() != 463 ) { FAIL("iteration 463"); exit(0); } + if ( do_464() != 464 ) { FAIL("iteration 464"); exit(0); } + if ( do_465() != 465 ) { FAIL("iteration 465"); exit(0); } + if ( do_466() != 466 ) { FAIL("iteration 466"); exit(0); } + if ( do_467() != 467 ) { FAIL("iteration 467"); exit(0); } + if ( do_468() != 468 ) { FAIL("iteration 468"); exit(0); } + if ( do_469() != 469 ) { FAIL("iteration 469"); exit(0); } + if ( do_470() != 470 ) { FAIL("iteration 470"); exit(0); } + if ( do_471() != 471 ) { FAIL("iteration 471"); exit(0); } + if ( do_472() != 472 ) { FAIL("iteration 472"); exit(0); } + if ( do_473() != 473 ) { FAIL("iteration 473"); exit(0); } + if ( do_474() != 474 ) { FAIL("iteration 474"); exit(0); } + if ( do_475() != 475 ) { FAIL("iteration 475"); exit(0); } + if ( do_476() != 476 ) { FAIL("iteration 476"); exit(0); } + if ( do_477() != 477 ) { FAIL("iteration 477"); exit(0); } + if ( do_478() != 478 ) { FAIL("iteration 478"); exit(0); } + if ( do_479() != 479 ) { FAIL("iteration 479"); exit(0); } + if ( do_480() != 480 ) { FAIL("iteration 480"); exit(0); } + if ( do_481() != 481 ) { FAIL("iteration 481"); exit(0); } + if ( do_482() != 482 ) { FAIL("iteration 482"); exit(0); } + if ( do_483() != 483 ) { FAIL("iteration 483"); exit(0); } + if ( do_484() != 484 ) { FAIL("iteration 484"); exit(0); } + if ( do_485() != 485 ) { FAIL("iteration 485"); exit(0); } + if ( do_486() != 486 ) { FAIL("iteration 486"); exit(0); } + if ( do_487() != 487 ) { FAIL("iteration 487"); exit(0); } + if ( do_488() != 488 ) { FAIL("iteration 488"); exit(0); } + if ( do_489() != 489 ) { FAIL("iteration 489"); exit(0); } + if ( do_490() != 490 ) { FAIL("iteration 490"); exit(0); } + if ( do_491() != 491 ) { FAIL("iteration 491"); exit(0); } + if ( do_492() != 492 ) { FAIL("iteration 492"); exit(0); } + if ( do_493() != 493 ) { FAIL("iteration 493"); exit(0); } + if ( do_494() != 494 ) { FAIL("iteration 494"); exit(0); } + if ( do_495() != 495 ) { FAIL("iteration 495"); exit(0); } + if ( do_496() != 496 ) { FAIL("iteration 496"); exit(0); } + if ( do_497() != 497 ) { FAIL("iteration 497"); exit(0); } + if ( do_498() != 498 ) { FAIL("iteration 498"); exit(0); } + if ( do_499() != 499 ) { FAIL("iteration 499"); exit(0); } + if ( do_500() != 500 ) { FAIL("iteration 500"); exit(0); } + if ( do_501() != 501 ) { FAIL("iteration 501"); exit(0); } + if ( do_502() != 502 ) { FAIL("iteration 502"); exit(0); } + if ( do_503() != 503 ) { FAIL("iteration 503"); exit(0); } + if ( do_504() != 504 ) { FAIL("iteration 504"); exit(0); } + if ( do_505() != 505 ) { FAIL("iteration 505"); exit(0); } + if ( do_506() != 506 ) { FAIL("iteration 506"); exit(0); } + if ( do_507() != 507 ) { FAIL("iteration 507"); exit(0); } + if ( do_508() != 508 ) { FAIL("iteration 508"); exit(0); } + if ( do_509() != 509 ) { FAIL("iteration 509"); exit(0); } + if ( do_510() != 510 ) { FAIL("iteration 510"); exit(0); } + if ( do_511() != 511 ) { FAIL("iteration 511"); exit(0); } + if ( do_512() != 512 ) { FAIL("iteration 512"); exit(0); } + if ( do_513() != 513 ) { FAIL("iteration 513"); exit(0); } + if ( do_514() != 514 ) { FAIL("iteration 514"); exit(0); } + if ( do_515() != 515 ) { FAIL("iteration 515"); exit(0); } + if ( do_516() != 516 ) { FAIL("iteration 516"); exit(0); } + if ( do_517() != 517 ) { FAIL("iteration 517"); exit(0); } + if ( do_518() != 518 ) { FAIL("iteration 518"); exit(0); } + if ( do_519() != 519 ) { FAIL("iteration 519"); exit(0); } + if ( do_520() != 520 ) { FAIL("iteration 520"); exit(0); } + if ( do_521() != 521 ) { FAIL("iteration 521"); exit(0); } + if ( do_522() != 522 ) { FAIL("iteration 522"); exit(0); } + if ( do_523() != 523 ) { FAIL("iteration 523"); exit(0); } + if ( do_524() != 524 ) { FAIL("iteration 524"); exit(0); } + if ( do_525() != 525 ) { FAIL("iteration 525"); exit(0); } + if ( do_526() != 526 ) { FAIL("iteration 526"); exit(0); } + if ( do_527() != 527 ) { FAIL("iteration 527"); exit(0); } + if ( do_528() != 528 ) { FAIL("iteration 528"); exit(0); } + if ( do_529() != 529 ) { FAIL("iteration 529"); exit(0); } + if ( do_530() != 530 ) { FAIL("iteration 530"); exit(0); } + if ( do_531() != 531 ) { FAIL("iteration 531"); exit(0); } + if ( do_532() != 532 ) { FAIL("iteration 532"); exit(0); } + if ( do_533() != 533 ) { FAIL("iteration 533"); exit(0); } + if ( do_534() != 534 ) { FAIL("iteration 534"); exit(0); } + if ( do_535() != 535 ) { FAIL("iteration 535"); exit(0); } + if ( do_536() != 536 ) { FAIL("iteration 536"); exit(0); } + if ( do_537() != 537 ) { FAIL("iteration 537"); exit(0); } + if ( do_538() != 538 ) { FAIL("iteration 538"); exit(0); } + if ( do_539() != 539 ) { FAIL("iteration 539"); exit(0); } + if ( do_540() != 540 ) { FAIL("iteration 540"); exit(0); } + if ( do_541() != 541 ) { FAIL("iteration 541"); exit(0); } + if ( do_542() != 542 ) { FAIL("iteration 542"); exit(0); } + if ( do_543() != 543 ) { FAIL("iteration 543"); exit(0); } + if ( do_544() != 544 ) { FAIL("iteration 544"); exit(0); } + if ( do_545() != 545 ) { FAIL("iteration 545"); exit(0); } + if ( do_546() != 546 ) { FAIL("iteration 546"); exit(0); } + if ( do_547() != 547 ) { FAIL("iteration 547"); exit(0); } + if ( do_548() != 548 ) { FAIL("iteration 548"); exit(0); } + if ( do_549() != 549 ) { FAIL("iteration 549"); exit(0); } + if ( do_550() != 550 ) { FAIL("iteration 550"); exit(0); } + if ( do_551() != 551 ) { FAIL("iteration 551"); exit(0); } + if ( do_552() != 552 ) { FAIL("iteration 552"); exit(0); } + if ( do_553() != 553 ) { FAIL("iteration 553"); exit(0); } + if ( do_554() != 554 ) { FAIL("iteration 554"); exit(0); } + if ( do_555() != 555 ) { FAIL("iteration 555"); exit(0); } + if ( do_556() != 556 ) { FAIL("iteration 556"); exit(0); } + if ( do_557() != 557 ) { FAIL("iteration 557"); exit(0); } + if ( do_558() != 558 ) { FAIL("iteration 558"); exit(0); } + if ( do_559() != 559 ) { FAIL("iteration 559"); exit(0); } + if ( do_560() != 560 ) { FAIL("iteration 560"); exit(0); } + if ( do_561() != 561 ) { FAIL("iteration 561"); exit(0); } + if ( do_562() != 562 ) { FAIL("iteration 562"); exit(0); } + if ( do_563() != 563 ) { FAIL("iteration 563"); exit(0); } + if ( do_564() != 564 ) { FAIL("iteration 564"); exit(0); } + if ( do_565() != 565 ) { FAIL("iteration 565"); exit(0); } + if ( do_566() != 566 ) { FAIL("iteration 566"); exit(0); } + if ( do_567() != 567 ) { FAIL("iteration 567"); exit(0); } + if ( do_568() != 568 ) { FAIL("iteration 568"); exit(0); } + if ( do_569() != 569 ) { FAIL("iteration 569"); exit(0); } + if ( do_570() != 570 ) { FAIL("iteration 570"); exit(0); } + if ( do_571() != 571 ) { FAIL("iteration 571"); exit(0); } + if ( do_572() != 572 ) { FAIL("iteration 572"); exit(0); } + if ( do_573() != 573 ) { FAIL("iteration 573"); exit(0); } + if ( do_574() != 574 ) { FAIL("iteration 574"); exit(0); } + if ( do_575() != 575 ) { FAIL("iteration 575"); exit(0); } + if ( do_576() != 576 ) { FAIL("iteration 576"); exit(0); } + if ( do_577() != 577 ) { FAIL("iteration 577"); exit(0); } + if ( do_578() != 578 ) { FAIL("iteration 578"); exit(0); } + if ( do_579() != 579 ) { FAIL("iteration 579"); exit(0); } + if ( do_580() != 580 ) { FAIL("iteration 580"); exit(0); } + if ( do_581() != 581 ) { FAIL("iteration 581"); exit(0); } + if ( do_582() != 582 ) { FAIL("iteration 582"); exit(0); } + if ( do_583() != 583 ) { FAIL("iteration 583"); exit(0); } + if ( do_584() != 584 ) { FAIL("iteration 584"); exit(0); } + if ( do_585() != 585 ) { FAIL("iteration 585"); exit(0); } + if ( do_586() != 586 ) { FAIL("iteration 586"); exit(0); } + if ( do_587() != 587 ) { FAIL("iteration 587"); exit(0); } + if ( do_588() != 588 ) { FAIL("iteration 588"); exit(0); } + if ( do_589() != 589 ) { FAIL("iteration 589"); exit(0); } + if ( do_590() != 590 ) { FAIL("iteration 590"); exit(0); } + if ( do_591() != 591 ) { FAIL("iteration 591"); exit(0); } + if ( do_592() != 592 ) { FAIL("iteration 592"); exit(0); } + if ( do_593() != 593 ) { FAIL("iteration 593"); exit(0); } + if ( do_594() != 594 ) { FAIL("iteration 594"); exit(0); } + if ( do_595() != 595 ) { FAIL("iteration 595"); exit(0); } + if ( do_596() != 596 ) { FAIL("iteration 596"); exit(0); } + if ( do_597() != 597 ) { FAIL("iteration 597"); exit(0); } + if ( do_598() != 598 ) { FAIL("iteration 598"); exit(0); } + if ( do_599() != 599 ) { FAIL("iteration 599"); exit(0); } + if ( do_600() != 600 ) { FAIL("iteration 600"); exit(0); } + if ( do_601() != 601 ) { FAIL("iteration 601"); exit(0); } + if ( do_602() != 602 ) { FAIL("iteration 602"); exit(0); } + if ( do_603() != 603 ) { FAIL("iteration 603"); exit(0); } + if ( do_604() != 604 ) { FAIL("iteration 604"); exit(0); } + if ( do_605() != 605 ) { FAIL("iteration 605"); exit(0); } + if ( do_606() != 606 ) { FAIL("iteration 606"); exit(0); } + if ( do_607() != 607 ) { FAIL("iteration 607"); exit(0); } + if ( do_608() != 608 ) { FAIL("iteration 608"); exit(0); } + if ( do_609() != 609 ) { FAIL("iteration 609"); exit(0); } + if ( do_610() != 610 ) { FAIL("iteration 610"); exit(0); } + if ( do_611() != 611 ) { FAIL("iteration 611"); exit(0); } + if ( do_612() != 612 ) { FAIL("iteration 612"); exit(0); } + if ( do_613() != 613 ) { FAIL("iteration 613"); exit(0); } + if ( do_614() != 614 ) { FAIL("iteration 614"); exit(0); } + if ( do_615() != 615 ) { FAIL("iteration 615"); exit(0); } + if ( do_616() != 616 ) { FAIL("iteration 616"); exit(0); } + if ( do_617() != 617 ) { FAIL("iteration 617"); exit(0); } + if ( do_618() != 618 ) { FAIL("iteration 618"); exit(0); } + if ( do_619() != 619 ) { FAIL("iteration 619"); exit(0); } + if ( do_620() != 620 ) { FAIL("iteration 620"); exit(0); } + if ( do_621() != 621 ) { FAIL("iteration 621"); exit(0); } + if ( do_622() != 622 ) { FAIL("iteration 622"); exit(0); } + if ( do_623() != 623 ) { FAIL("iteration 623"); exit(0); } + if ( do_624() != 624 ) { FAIL("iteration 624"); exit(0); } + if ( do_625() != 625 ) { FAIL("iteration 625"); exit(0); } + if ( do_626() != 626 ) { FAIL("iteration 626"); exit(0); } + if ( do_627() != 627 ) { FAIL("iteration 627"); exit(0); } + if ( do_628() != 628 ) { FAIL("iteration 628"); exit(0); } + if ( do_629() != 629 ) { FAIL("iteration 629"); exit(0); } + if ( do_630() != 630 ) { FAIL("iteration 630"); exit(0); } + if ( do_631() != 631 ) { FAIL("iteration 631"); exit(0); } + if ( do_632() != 632 ) { FAIL("iteration 632"); exit(0); } + if ( do_633() != 633 ) { FAIL("iteration 633"); exit(0); } + if ( do_634() != 634 ) { FAIL("iteration 634"); exit(0); } + if ( do_635() != 635 ) { FAIL("iteration 635"); exit(0); } + if ( do_636() != 636 ) { FAIL("iteration 636"); exit(0); } + if ( do_637() != 637 ) { FAIL("iteration 637"); exit(0); } + if ( do_638() != 638 ) { FAIL("iteration 638"); exit(0); } + if ( do_639() != 639 ) { FAIL("iteration 639"); exit(0); } + if ( do_640() != 640 ) { FAIL("iteration 640"); exit(0); } + if ( do_641() != 641 ) { FAIL("iteration 641"); exit(0); } + if ( do_642() != 642 ) { FAIL("iteration 642"); exit(0); } + if ( do_643() != 643 ) { FAIL("iteration 643"); exit(0); } + if ( do_644() != 644 ) { FAIL("iteration 644"); exit(0); } + if ( do_645() != 645 ) { FAIL("iteration 645"); exit(0); } + if ( do_646() != 646 ) { FAIL("iteration 646"); exit(0); } + if ( do_647() != 647 ) { FAIL("iteration 647"); exit(0); } + if ( do_648() != 648 ) { FAIL("iteration 648"); exit(0); } + if ( do_649() != 649 ) { FAIL("iteration 649"); exit(0); } + if ( do_650() != 650 ) { FAIL("iteration 650"); exit(0); } + if ( do_651() != 651 ) { FAIL("iteration 651"); exit(0); } + if ( do_652() != 652 ) { FAIL("iteration 652"); exit(0); } + if ( do_653() != 653 ) { FAIL("iteration 653"); exit(0); } + if ( do_654() != 654 ) { FAIL("iteration 654"); exit(0); } + if ( do_655() != 655 ) { FAIL("iteration 655"); exit(0); } + if ( do_656() != 656 ) { FAIL("iteration 656"); exit(0); } + if ( do_657() != 657 ) { FAIL("iteration 657"); exit(0); } + if ( do_658() != 658 ) { FAIL("iteration 658"); exit(0); } + if ( do_659() != 659 ) { FAIL("iteration 659"); exit(0); } + if ( do_660() != 660 ) { FAIL("iteration 660"); exit(0); } + if ( do_661() != 661 ) { FAIL("iteration 661"); exit(0); } + if ( do_662() != 662 ) { FAIL("iteration 662"); exit(0); } + if ( do_663() != 663 ) { FAIL("iteration 663"); exit(0); } + if ( do_664() != 664 ) { FAIL("iteration 664"); exit(0); } + if ( do_665() != 665 ) { FAIL("iteration 665"); exit(0); } + if ( do_666() != 666 ) { FAIL("iteration 666"); exit(0); } + if ( do_667() != 667 ) { FAIL("iteration 667"); exit(0); } + if ( do_668() != 668 ) { FAIL("iteration 668"); exit(0); } + if ( do_669() != 669 ) { FAIL("iteration 669"); exit(0); } + if ( do_670() != 670 ) { FAIL("iteration 670"); exit(0); } + if ( do_671() != 671 ) { FAIL("iteration 671"); exit(0); } + if ( do_672() != 672 ) { FAIL("iteration 672"); exit(0); } + if ( do_673() != 673 ) { FAIL("iteration 673"); exit(0); } + if ( do_674() != 674 ) { FAIL("iteration 674"); exit(0); } + if ( do_675() != 675 ) { FAIL("iteration 675"); exit(0); } + if ( do_676() != 676 ) { FAIL("iteration 676"); exit(0); } + if ( do_677() != 677 ) { FAIL("iteration 677"); exit(0); } + if ( do_678() != 678 ) { FAIL("iteration 678"); exit(0); } + if ( do_679() != 679 ) { FAIL("iteration 679"); exit(0); } + if ( do_680() != 680 ) { FAIL("iteration 680"); exit(0); } + if ( do_681() != 681 ) { FAIL("iteration 681"); exit(0); } + if ( do_682() != 682 ) { FAIL("iteration 682"); exit(0); } + if ( do_683() != 683 ) { FAIL("iteration 683"); exit(0); } + if ( do_684() != 684 ) { FAIL("iteration 684"); exit(0); } + if ( do_685() != 685 ) { FAIL("iteration 685"); exit(0); } + if ( do_686() != 686 ) { FAIL("iteration 686"); exit(0); } + if ( do_687() != 687 ) { FAIL("iteration 687"); exit(0); } + if ( do_688() != 688 ) { FAIL("iteration 688"); exit(0); } + if ( do_689() != 689 ) { FAIL("iteration 689"); exit(0); } + if ( do_690() != 690 ) { FAIL("iteration 690"); exit(0); } + if ( do_691() != 691 ) { FAIL("iteration 691"); exit(0); } + if ( do_692() != 692 ) { FAIL("iteration 692"); exit(0); } + if ( do_693() != 693 ) { FAIL("iteration 693"); exit(0); } + if ( do_694() != 694 ) { FAIL("iteration 694"); exit(0); } + if ( do_695() != 695 ) { FAIL("iteration 695"); exit(0); } + if ( do_696() != 696 ) { FAIL("iteration 696"); exit(0); } + if ( do_697() != 697 ) { FAIL("iteration 697"); exit(0); } + if ( do_698() != 698 ) { FAIL("iteration 698"); exit(0); } + if ( do_699() != 699 ) { FAIL("iteration 699"); exit(0); } + if ( do_700() != 700 ) { FAIL("iteration 700"); exit(0); } + if ( do_701() != 701 ) { FAIL("iteration 701"); exit(0); } + if ( do_702() != 702 ) { FAIL("iteration 702"); exit(0); } + if ( do_703() != 703 ) { FAIL("iteration 703"); exit(0); } + if ( do_704() != 704 ) { FAIL("iteration 704"); exit(0); } + if ( do_705() != 705 ) { FAIL("iteration 705"); exit(0); } + if ( do_706() != 706 ) { FAIL("iteration 706"); exit(0); } + if ( do_707() != 707 ) { FAIL("iteration 707"); exit(0); } + if ( do_708() != 708 ) { FAIL("iteration 708"); exit(0); } + if ( do_709() != 709 ) { FAIL("iteration 709"); exit(0); } + if ( do_710() != 710 ) { FAIL("iteration 710"); exit(0); } + if ( do_711() != 711 ) { FAIL("iteration 711"); exit(0); } + if ( do_712() != 712 ) { FAIL("iteration 712"); exit(0); } + if ( do_713() != 713 ) { FAIL("iteration 713"); exit(0); } + if ( do_714() != 714 ) { FAIL("iteration 714"); exit(0); } + if ( do_715() != 715 ) { FAIL("iteration 715"); exit(0); } + if ( do_716() != 716 ) { FAIL("iteration 716"); exit(0); } + if ( do_717() != 717 ) { FAIL("iteration 717"); exit(0); } + if ( do_718() != 718 ) { FAIL("iteration 718"); exit(0); } + if ( do_719() != 719 ) { FAIL("iteration 719"); exit(0); } + if ( do_720() != 720 ) { FAIL("iteration 720"); exit(0); } + if ( do_721() != 721 ) { FAIL("iteration 721"); exit(0); } + if ( do_722() != 722 ) { FAIL("iteration 722"); exit(0); } + if ( do_723() != 723 ) { FAIL("iteration 723"); exit(0); } + if ( do_724() != 724 ) { FAIL("iteration 724"); exit(0); } + if ( do_725() != 725 ) { FAIL("iteration 725"); exit(0); } + if ( do_726() != 726 ) { FAIL("iteration 726"); exit(0); } + if ( do_727() != 727 ) { FAIL("iteration 727"); exit(0); } + if ( do_728() != 728 ) { FAIL("iteration 728"); exit(0); } + if ( do_729() != 729 ) { FAIL("iteration 729"); exit(0); } + if ( do_730() != 730 ) { FAIL("iteration 730"); exit(0); } + if ( do_731() != 731 ) { FAIL("iteration 731"); exit(0); } + if ( do_732() != 732 ) { FAIL("iteration 732"); exit(0); } + if ( do_733() != 733 ) { FAIL("iteration 733"); exit(0); } + if ( do_734() != 734 ) { FAIL("iteration 734"); exit(0); } + if ( do_735() != 735 ) { FAIL("iteration 735"); exit(0); } + if ( do_736() != 736 ) { FAIL("iteration 736"); exit(0); } + if ( do_737() != 737 ) { FAIL("iteration 737"); exit(0); } + if ( do_738() != 738 ) { FAIL("iteration 738"); exit(0); } + if ( do_739() != 739 ) { FAIL("iteration 739"); exit(0); } + if ( do_740() != 740 ) { FAIL("iteration 740"); exit(0); } + if ( do_741() != 741 ) { FAIL("iteration 741"); exit(0); } + if ( do_742() != 742 ) { FAIL("iteration 742"); exit(0); } + if ( do_743() != 743 ) { FAIL("iteration 743"); exit(0); } + if ( do_744() != 744 ) { FAIL("iteration 744"); exit(0); } + if ( do_745() != 745 ) { FAIL("iteration 745"); exit(0); } + if ( do_746() != 746 ) { FAIL("iteration 746"); exit(0); } + if ( do_747() != 747 ) { FAIL("iteration 747"); exit(0); } + if ( do_748() != 748 ) { FAIL("iteration 748"); exit(0); } + if ( do_749() != 749 ) { FAIL("iteration 749"); exit(0); } + if ( do_750() != 750 ) { FAIL("iteration 750"); exit(0); } + if ( do_751() != 751 ) { FAIL("iteration 751"); exit(0); } + if ( do_752() != 752 ) { FAIL("iteration 752"); exit(0); } + if ( do_753() != 753 ) { FAIL("iteration 753"); exit(0); } + if ( do_754() != 754 ) { FAIL("iteration 754"); exit(0); } + if ( do_755() != 755 ) { FAIL("iteration 755"); exit(0); } + if ( do_756() != 756 ) { FAIL("iteration 756"); exit(0); } + if ( do_757() != 757 ) { FAIL("iteration 757"); exit(0); } + if ( do_758() != 758 ) { FAIL("iteration 758"); exit(0); } + if ( do_759() != 759 ) { FAIL("iteration 759"); exit(0); } + if ( do_760() != 760 ) { FAIL("iteration 760"); exit(0); } + if ( do_761() != 761 ) { FAIL("iteration 761"); exit(0); } + if ( do_762() != 762 ) { FAIL("iteration 762"); exit(0); } + if ( do_763() != 763 ) { FAIL("iteration 763"); exit(0); } + if ( do_764() != 764 ) { FAIL("iteration 764"); exit(0); } + if ( do_765() != 765 ) { FAIL("iteration 765"); exit(0); } + if ( do_766() != 766 ) { FAIL("iteration 766"); exit(0); } + if ( do_767() != 767 ) { FAIL("iteration 767"); exit(0); } + if ( do_768() != 768 ) { FAIL("iteration 768"); exit(0); } + if ( do_769() != 769 ) { FAIL("iteration 769"); exit(0); } + if ( do_770() != 770 ) { FAIL("iteration 770"); exit(0); } + if ( do_771() != 771 ) { FAIL("iteration 771"); exit(0); } + if ( do_772() != 772 ) { FAIL("iteration 772"); exit(0); } + if ( do_773() != 773 ) { FAIL("iteration 773"); exit(0); } + if ( do_774() != 774 ) { FAIL("iteration 774"); exit(0); } + if ( do_775() != 775 ) { FAIL("iteration 775"); exit(0); } + if ( do_776() != 776 ) { FAIL("iteration 776"); exit(0); } + if ( do_777() != 777 ) { FAIL("iteration 777"); exit(0); } + if ( do_778() != 778 ) { FAIL("iteration 778"); exit(0); } + if ( do_779() != 779 ) { FAIL("iteration 779"); exit(0); } + if ( do_780() != 780 ) { FAIL("iteration 780"); exit(0); } + if ( do_781() != 781 ) { FAIL("iteration 781"); exit(0); } + if ( do_782() != 782 ) { FAIL("iteration 782"); exit(0); } + if ( do_783() != 783 ) { FAIL("iteration 783"); exit(0); } + if ( do_784() != 784 ) { FAIL("iteration 784"); exit(0); } + if ( do_785() != 785 ) { FAIL("iteration 785"); exit(0); } + if ( do_786() != 786 ) { FAIL("iteration 786"); exit(0); } + if ( do_787() != 787 ) { FAIL("iteration 787"); exit(0); } + if ( do_788() != 788 ) { FAIL("iteration 788"); exit(0); } + if ( do_789() != 789 ) { FAIL("iteration 789"); exit(0); } + if ( do_790() != 790 ) { FAIL("iteration 790"); exit(0); } + if ( do_791() != 791 ) { FAIL("iteration 791"); exit(0); } + if ( do_792() != 792 ) { FAIL("iteration 792"); exit(0); } + if ( do_793() != 793 ) { FAIL("iteration 793"); exit(0); } + if ( do_794() != 794 ) { FAIL("iteration 794"); exit(0); } + if ( do_795() != 795 ) { FAIL("iteration 795"); exit(0); } + if ( do_796() != 796 ) { FAIL("iteration 796"); exit(0); } + if ( do_797() != 797 ) { FAIL("iteration 797"); exit(0); } + if ( do_798() != 798 ) { FAIL("iteration 798"); exit(0); } + if ( do_799() != 799 ) { FAIL("iteration 799"); exit(0); } + if ( do_800() != 800 ) { FAIL("iteration 800"); exit(0); } + if ( do_801() != 801 ) { FAIL("iteration 801"); exit(0); } + if ( do_802() != 802 ) { FAIL("iteration 802"); exit(0); } + if ( do_803() != 803 ) { FAIL("iteration 803"); exit(0); } + if ( do_804() != 804 ) { FAIL("iteration 804"); exit(0); } + if ( do_805() != 805 ) { FAIL("iteration 805"); exit(0); } + if ( do_806() != 806 ) { FAIL("iteration 806"); exit(0); } + if ( do_807() != 807 ) { FAIL("iteration 807"); exit(0); } + if ( do_808() != 808 ) { FAIL("iteration 808"); exit(0); } + if ( do_809() != 809 ) { FAIL("iteration 809"); exit(0); } + if ( do_810() != 810 ) { FAIL("iteration 810"); exit(0); } + if ( do_811() != 811 ) { FAIL("iteration 811"); exit(0); } + if ( do_812() != 812 ) { FAIL("iteration 812"); exit(0); } + if ( do_813() != 813 ) { FAIL("iteration 813"); exit(0); } + if ( do_814() != 814 ) { FAIL("iteration 814"); exit(0); } + if ( do_815() != 815 ) { FAIL("iteration 815"); exit(0); } + if ( do_816() != 816 ) { FAIL("iteration 816"); exit(0); } + if ( do_817() != 817 ) { FAIL("iteration 817"); exit(0); } + if ( do_818() != 818 ) { FAIL("iteration 818"); exit(0); } + if ( do_819() != 819 ) { FAIL("iteration 819"); exit(0); } + if ( do_820() != 820 ) { FAIL("iteration 820"); exit(0); } + if ( do_821() != 821 ) { FAIL("iteration 821"); exit(0); } + if ( do_822() != 822 ) { FAIL("iteration 822"); exit(0); } + if ( do_823() != 823 ) { FAIL("iteration 823"); exit(0); } + if ( do_824() != 824 ) { FAIL("iteration 824"); exit(0); } + if ( do_825() != 825 ) { FAIL("iteration 825"); exit(0); } + if ( do_826() != 826 ) { FAIL("iteration 826"); exit(0); } + if ( do_827() != 827 ) { FAIL("iteration 827"); exit(0); } + if ( do_828() != 828 ) { FAIL("iteration 828"); exit(0); } + if ( do_829() != 829 ) { FAIL("iteration 829"); exit(0); } + if ( do_830() != 830 ) { FAIL("iteration 830"); exit(0); } + if ( do_831() != 831 ) { FAIL("iteration 831"); exit(0); } + if ( do_832() != 832 ) { FAIL("iteration 832"); exit(0); } + if ( do_833() != 833 ) { FAIL("iteration 833"); exit(0); } + if ( do_834() != 834 ) { FAIL("iteration 834"); exit(0); } + if ( do_835() != 835 ) { FAIL("iteration 835"); exit(0); } + if ( do_836() != 836 ) { FAIL("iteration 836"); exit(0); } + if ( do_837() != 837 ) { FAIL("iteration 837"); exit(0); } + if ( do_838() != 838 ) { FAIL("iteration 838"); exit(0); } + if ( do_839() != 839 ) { FAIL("iteration 839"); exit(0); } + if ( do_840() != 840 ) { FAIL("iteration 840"); exit(0); } + if ( do_841() != 841 ) { FAIL("iteration 841"); exit(0); } + if ( do_842() != 842 ) { FAIL("iteration 842"); exit(0); } + if ( do_843() != 843 ) { FAIL("iteration 843"); exit(0); } + if ( do_844() != 844 ) { FAIL("iteration 844"); exit(0); } + if ( do_845() != 845 ) { FAIL("iteration 845"); exit(0); } + if ( do_846() != 846 ) { FAIL("iteration 846"); exit(0); } + if ( do_847() != 847 ) { FAIL("iteration 847"); exit(0); } + if ( do_848() != 848 ) { FAIL("iteration 848"); exit(0); } + if ( do_849() != 849 ) { FAIL("iteration 849"); exit(0); } + if ( do_850() != 850 ) { FAIL("iteration 850"); exit(0); } + if ( do_851() != 851 ) { FAIL("iteration 851"); exit(0); } + if ( do_852() != 852 ) { FAIL("iteration 852"); exit(0); } + if ( do_853() != 853 ) { FAIL("iteration 853"); exit(0); } + if ( do_854() != 854 ) { FAIL("iteration 854"); exit(0); } + if ( do_855() != 855 ) { FAIL("iteration 855"); exit(0); } + if ( do_856() != 856 ) { FAIL("iteration 856"); exit(0); } + if ( do_857() != 857 ) { FAIL("iteration 857"); exit(0); } + if ( do_858() != 858 ) { FAIL("iteration 858"); exit(0); } + if ( do_859() != 859 ) { FAIL("iteration 859"); exit(0); } + if ( do_860() != 860 ) { FAIL("iteration 860"); exit(0); } + if ( do_861() != 861 ) { FAIL("iteration 861"); exit(0); } + if ( do_862() != 862 ) { FAIL("iteration 862"); exit(0); } + if ( do_863() != 863 ) { FAIL("iteration 863"); exit(0); } + if ( do_864() != 864 ) { FAIL("iteration 864"); exit(0); } + if ( do_865() != 865 ) { FAIL("iteration 865"); exit(0); } + if ( do_866() != 866 ) { FAIL("iteration 866"); exit(0); } + if ( do_867() != 867 ) { FAIL("iteration 867"); exit(0); } + if ( do_868() != 868 ) { FAIL("iteration 868"); exit(0); } + if ( do_869() != 869 ) { FAIL("iteration 869"); exit(0); } + if ( do_870() != 870 ) { FAIL("iteration 870"); exit(0); } + if ( do_871() != 871 ) { FAIL("iteration 871"); exit(0); } + if ( do_872() != 872 ) { FAIL("iteration 872"); exit(0); } + if ( do_873() != 873 ) { FAIL("iteration 873"); exit(0); } + if ( do_874() != 874 ) { FAIL("iteration 874"); exit(0); } + if ( do_875() != 875 ) { FAIL("iteration 875"); exit(0); } + if ( do_876() != 876 ) { FAIL("iteration 876"); exit(0); } + if ( do_877() != 877 ) { FAIL("iteration 877"); exit(0); } + if ( do_878() != 878 ) { FAIL("iteration 878"); exit(0); } + if ( do_879() != 879 ) { FAIL("iteration 879"); exit(0); } + if ( do_880() != 880 ) { FAIL("iteration 880"); exit(0); } + if ( do_881() != 881 ) { FAIL("iteration 881"); exit(0); } + if ( do_882() != 882 ) { FAIL("iteration 882"); exit(0); } + if ( do_883() != 883 ) { FAIL("iteration 883"); exit(0); } + if ( do_884() != 884 ) { FAIL("iteration 884"); exit(0); } + if ( do_885() != 885 ) { FAIL("iteration 885"); exit(0); } + if ( do_886() != 886 ) { FAIL("iteration 886"); exit(0); } + if ( do_887() != 887 ) { FAIL("iteration 887"); exit(0); } + if ( do_888() != 888 ) { FAIL("iteration 888"); exit(0); } + if ( do_889() != 889 ) { FAIL("iteration 889"); exit(0); } + if ( do_890() != 890 ) { FAIL("iteration 890"); exit(0); } + if ( do_891() != 891 ) { FAIL("iteration 891"); exit(0); } + if ( do_892() != 892 ) { FAIL("iteration 892"); exit(0); } + if ( do_893() != 893 ) { FAIL("iteration 893"); exit(0); } + if ( do_894() != 894 ) { FAIL("iteration 894"); exit(0); } + if ( do_895() != 895 ) { FAIL("iteration 895"); exit(0); } + if ( do_896() != 896 ) { FAIL("iteration 896"); exit(0); } + if ( do_897() != 897 ) { FAIL("iteration 897"); exit(0); } + if ( do_898() != 898 ) { FAIL("iteration 898"); exit(0); } + if ( do_899() != 899 ) { FAIL("iteration 899"); exit(0); } + if ( do_900() != 900 ) { FAIL("iteration 900"); exit(0); } + if ( do_901() != 901 ) { FAIL("iteration 901"); exit(0); } + if ( do_902() != 902 ) { FAIL("iteration 902"); exit(0); } + if ( do_903() != 903 ) { FAIL("iteration 903"); exit(0); } + if ( do_904() != 904 ) { FAIL("iteration 904"); exit(0); } + if ( do_905() != 905 ) { FAIL("iteration 905"); exit(0); } + if ( do_906() != 906 ) { FAIL("iteration 906"); exit(0); } + if ( do_907() != 907 ) { FAIL("iteration 907"); exit(0); } + if ( do_908() != 908 ) { FAIL("iteration 908"); exit(0); } + if ( do_909() != 909 ) { FAIL("iteration 909"); exit(0); } + if ( do_910() != 910 ) { FAIL("iteration 910"); exit(0); } + if ( do_911() != 911 ) { FAIL("iteration 911"); exit(0); } + if ( do_912() != 912 ) { FAIL("iteration 912"); exit(0); } + if ( do_913() != 913 ) { FAIL("iteration 913"); exit(0); } + if ( do_914() != 914 ) { FAIL("iteration 914"); exit(0); } + if ( do_915() != 915 ) { FAIL("iteration 915"); exit(0); } + if ( do_916() != 916 ) { FAIL("iteration 916"); exit(0); } + if ( do_917() != 917 ) { FAIL("iteration 917"); exit(0); } + if ( do_918() != 918 ) { FAIL("iteration 918"); exit(0); } + if ( do_919() != 919 ) { FAIL("iteration 919"); exit(0); } + if ( do_920() != 920 ) { FAIL("iteration 920"); exit(0); } + if ( do_921() != 921 ) { FAIL("iteration 921"); exit(0); } + if ( do_922() != 922 ) { FAIL("iteration 922"); exit(0); } + if ( do_923() != 923 ) { FAIL("iteration 923"); exit(0); } + if ( do_924() != 924 ) { FAIL("iteration 924"); exit(0); } + if ( do_925() != 925 ) { FAIL("iteration 925"); exit(0); } + if ( do_926() != 926 ) { FAIL("iteration 926"); exit(0); } + if ( do_927() != 927 ) { FAIL("iteration 927"); exit(0); } + if ( do_928() != 928 ) { FAIL("iteration 928"); exit(0); } + if ( do_929() != 929 ) { FAIL("iteration 929"); exit(0); } + if ( do_930() != 930 ) { FAIL("iteration 930"); exit(0); } + if ( do_931() != 931 ) { FAIL("iteration 931"); exit(0); } + if ( do_932() != 932 ) { FAIL("iteration 932"); exit(0); } + if ( do_933() != 933 ) { FAIL("iteration 933"); exit(0); } + if ( do_934() != 934 ) { FAIL("iteration 934"); exit(0); } + if ( do_935() != 935 ) { FAIL("iteration 935"); exit(0); } + if ( do_936() != 936 ) { FAIL("iteration 936"); exit(0); } + if ( do_937() != 937 ) { FAIL("iteration 937"); exit(0); } + if ( do_938() != 938 ) { FAIL("iteration 938"); exit(0); } + if ( do_939() != 939 ) { FAIL("iteration 939"); exit(0); } + if ( do_940() != 940 ) { FAIL("iteration 940"); exit(0); } + if ( do_941() != 941 ) { FAIL("iteration 941"); exit(0); } + if ( do_942() != 942 ) { FAIL("iteration 942"); exit(0); } + if ( do_943() != 943 ) { FAIL("iteration 943"); exit(0); } + if ( do_944() != 944 ) { FAIL("iteration 944"); exit(0); } + if ( do_945() != 945 ) { FAIL("iteration 945"); exit(0); } + if ( do_946() != 946 ) { FAIL("iteration 946"); exit(0); } + if ( do_947() != 947 ) { FAIL("iteration 947"); exit(0); } + if ( do_948() != 948 ) { FAIL("iteration 948"); exit(0); } + if ( do_949() != 949 ) { FAIL("iteration 949"); exit(0); } + if ( do_950() != 950 ) { FAIL("iteration 950"); exit(0); } + if ( do_951() != 951 ) { FAIL("iteration 951"); exit(0); } + if ( do_952() != 952 ) { FAIL("iteration 952"); exit(0); } + if ( do_953() != 953 ) { FAIL("iteration 953"); exit(0); } + if ( do_954() != 954 ) { FAIL("iteration 954"); exit(0); } + if ( do_955() != 955 ) { FAIL("iteration 955"); exit(0); } + if ( do_956() != 956 ) { FAIL("iteration 956"); exit(0); } + if ( do_957() != 957 ) { FAIL("iteration 957"); exit(0); } + if ( do_958() != 958 ) { FAIL("iteration 958"); exit(0); } + if ( do_959() != 959 ) { FAIL("iteration 959"); exit(0); } + if ( do_960() != 960 ) { FAIL("iteration 960"); exit(0); } + if ( do_961() != 961 ) { FAIL("iteration 961"); exit(0); } + if ( do_962() != 962 ) { FAIL("iteration 962"); exit(0); } + if ( do_963() != 963 ) { FAIL("iteration 963"); exit(0); } + if ( do_964() != 964 ) { FAIL("iteration 964"); exit(0); } + if ( do_965() != 965 ) { FAIL("iteration 965"); exit(0); } + if ( do_966() != 966 ) { FAIL("iteration 966"); exit(0); } + if ( do_967() != 967 ) { FAIL("iteration 967"); exit(0); } + if ( do_968() != 968 ) { FAIL("iteration 968"); exit(0); } + if ( do_969() != 969 ) { FAIL("iteration 969"); exit(0); } + if ( do_970() != 970 ) { FAIL("iteration 970"); exit(0); } + if ( do_971() != 971 ) { FAIL("iteration 971"); exit(0); } + if ( do_972() != 972 ) { FAIL("iteration 972"); exit(0); } + if ( do_973() != 973 ) { FAIL("iteration 973"); exit(0); } + if ( do_974() != 974 ) { FAIL("iteration 974"); exit(0); } + if ( do_975() != 975 ) { FAIL("iteration 975"); exit(0); } + if ( do_976() != 976 ) { FAIL("iteration 976"); exit(0); } + if ( do_977() != 977 ) { FAIL("iteration 977"); exit(0); } + if ( do_978() != 978 ) { FAIL("iteration 978"); exit(0); } + if ( do_979() != 979 ) { FAIL("iteration 979"); exit(0); } + if ( do_980() != 980 ) { FAIL("iteration 980"); exit(0); } + if ( do_981() != 981 ) { FAIL("iteration 981"); exit(0); } + if ( do_982() != 982 ) { FAIL("iteration 982"); exit(0); } + if ( do_983() != 983 ) { FAIL("iteration 983"); exit(0); } + if ( do_984() != 984 ) { FAIL("iteration 984"); exit(0); } + if ( do_985() != 985 ) { FAIL("iteration 985"); exit(0); } + if ( do_986() != 986 ) { FAIL("iteration 986"); exit(0); } + if ( do_987() != 987 ) { FAIL("iteration 987"); exit(0); } + if ( do_988() != 988 ) { FAIL("iteration 988"); exit(0); } + if ( do_989() != 989 ) { FAIL("iteration 989"); exit(0); } + if ( do_990() != 990 ) { FAIL("iteration 990"); exit(0); } + if ( do_991() != 991 ) { FAIL("iteration 991"); exit(0); } + if ( do_992() != 992 ) { FAIL("iteration 992"); exit(0); } + if ( do_993() != 993 ) { FAIL("iteration 993"); exit(0); } + if ( do_994() != 994 ) { FAIL("iteration 994"); exit(0); } + if ( do_995() != 995 ) { FAIL("iteration 995"); exit(0); } + if ( do_996() != 996 ) { FAIL("iteration 996"); exit(0); } + if ( do_997() != 997 ) { FAIL("iteration 997"); exit(0); } + if ( do_998() != 998 ) { FAIL("iteration 998"); exit(0); } + if ( do_999() != 999 ) { FAIL("iteration 999"); exit(0); } + return NULL; +} + + +int main() +{ + pthread_t worker[10]; + for (int i=0; i < 10; ++i) { + if ( pthread_create(&worker[i], NULL, work, NULL) != 0 ) { + FAIL("pthread_create failed"); + exit(0); + } + } + + void* result; + for (int i=0; i < 10; ++i) { + pthread_join(worker[i], &result); + } + + PASS("thread-lazy-bind"); + return 0; +} diff --git a/src/dyld-apple/unit-tests/test-cases/tlv-basic/main.c b/src/dyld-apple/unit-tests/test-cases/tlv-basic/main.c new file mode 100644 index 000000000..1bbdc613c --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/tlv-basic/main.c @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2010 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +__attribute__((weak)) +void foobar() { +} + +__thread int a; +__thread int b = 5; +__thread static int c; +__thread static int d = 5; + + +static void* work(void* arg) +{ + uintptr_t offset = (uintptr_t)arg; + //fprintf(stderr, "self=%p, &a=%p\n", pthread_self(), get_a()); + void* result = malloc(10); + if ( a != 0 ) { + FAIL("tlv-basic: a non-zero on slow-path"); + exit(0); + } + if ( b != 5 ) { + FAIL("tlv-basic: b not five"); + exit(0); + } + + if ( c != 0 ) { + FAIL("tlv-basic: c non-zero"); + exit(0); + } + + if ( d != 5 ) { + FAIL("tlv-basic: gd not five"); + exit(0); + } + + for(int i=0; i < 10000; ++i) { + a = 3 + offset + i; + b = 7 + offset + i; + c = 11 + offset + i; + d = 13 + offset + i; + foobar(); + if ( a != 3 + offset + i ) { + FAIL("tlv-basic: a not three"); + exit(0); + } + if ( b != 7 + offset + i ) { + FAIL("tlv-basic: b not seven"); + exit(0); + } + if ( c != 11 + offset + i ) { + FAIL("tlv-basic: c not eleven"); + exit(0); + } + if ( d != 13 + offset + i ) { + FAIL("tlv-basic: d not thirteen"); + exit(0); + } + } + + return result; +} + +int main() +{ + pthread_t worker1; + if ( pthread_create(&worker1, NULL, work, (void*)1) != 0 ) { + FAIL("pthread_create failed"); + exit(0); + } + + pthread_t worker2; + if ( pthread_create(&worker2, NULL, work, (void*)10) != 0 ) { + FAIL("pthread_create failed"); + exit(0); + } + + pthread_t worker3; + if ( pthread_create(&worker3, NULL, work, (void*)100) != 0 ) { + FAIL("pthread_create failed"); + exit(0); + } + + void* result; + //fprintf(stderr, "waiting for worker 1\n"); + pthread_join(worker1, &result); + //fprintf(stderr, "waiting for worker 2\n"); + pthread_join(worker2, &result); + //fprintf(stderr, "waiting for worker 3\n"); + pthread_join(worker3, &result); + + work(NULL); + + PASS("tlv-basic"); + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/tlv-dylib/foo.c b/src/dyld-apple/unit-tests/test-cases/tlv-dylib/foo.c new file mode 100644 index 000000000..4a47edbbf --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/tlv-dylib/foo.c @@ -0,0 +1,8 @@ + + + +__thread int a; +__thread int b = 5; + + +int getB() { return b; } diff --git a/src/dyld-apple/unit-tests/test-cases/tlv-dylib/main.c b/src/dyld-apple/unit-tests/test-cases/tlv-dylib/main.c new file mode 100644 index 000000000..117a64633 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/tlv-dylib/main.c @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2010 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +extern __thread int a; +extern __thread int b; +__thread static int c; +__thread static int d = 5; + + +static void* work(void* arg) +{ + //fprintf(stderr, "self=%p, &a=%p\n", pthread_self(), get_a()); + if ( a != 0 ) { + FAIL("tlv-basic: get_a() non-zero"); + exit(0); + } + if ( b != 5 ) { + FAIL("tlv-basic: get_b() not five"); + exit(0); + } + if ( c != 0 ) { + FAIL("tlv-basic: get_c() non-zero"); + exit(0); + } + if ( d != 5 ) { + FAIL("tlv-basic: get_d() not five"); + exit(0); + } + return NULL; +} + +int main() +{ + pthread_t worker1; + if ( pthread_create(&worker1, NULL, work, NULL) != 0 ) { + FAIL("pthread_create failed"); + exit(0); + } + + pthread_t worker2; + if ( pthread_create(&worker2, NULL, work, NULL) != 0 ) { + FAIL("pthread_create failed"); + exit(0); + } + + void* result; + //fprintf(stderr, "waiting for worker 1\n"); + pthread_join(worker1, &result); + //fprintf(stderr, "waiting for worker 2\n"); + pthread_join(worker2, &result); + + work(NULL); + + PASS("tlv-basic"); + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/tlv-initializer/get.s b/src/dyld-apple/unit-tests/test-cases/tlv-initializer/get.s new file mode 100644 index 000000000..919667ab3 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/tlv-initializer/get.s @@ -0,0 +1,18 @@ + +#if __x86_64__ + + # _myinit sets up TLV content + .thread_init_func + .quad _myinit + +#endif + +#if __i386__ + + # _myinit sets up TLV content + .thread_init_func + .long _myinit + +#endif + +.subsections_via_symbols diff --git a/src/dyld-apple/unit-tests/test-cases/tlv-initializer/main.c b/src/dyld-apple/unit-tests/test-cases/tlv-initializer/main.c new file mode 100644 index 000000000..3f89123ec --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/tlv-initializer/main.c @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2010-2011 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +__thread int a = 0; // initially 0 +__thread int b = 5; // initially 5 + +// simulate C++ initializer on thread local variables +void myinit() +{ + a = 11; + b = 42; +} + +static void* work(void* arg) +{ + //fprintf(stderr, "self=%p, &a=%p\n", pthread_self(), get_a()); + if ( a != 11 ) { + FAIL("tlv-initializer: get_a() not initialized to 11"); + exit(0); + } + if ( b != 42 ) { + FAIL("tlv-initializer: get_b() not initialized to 42"); + exit(0); + } + return NULL; +} + +int main() +{ + pthread_t worker1; + if ( pthread_create(&worker1, NULL, work, NULL) != 0 ) { + FAIL("pthread_create failed"); + exit(0); + } + + pthread_t worker2; + if ( pthread_create(&worker2, NULL, work, NULL) != 0 ) { + FAIL("pthread_create failed"); + exit(0); + } + + void* result; + //fprintf(stderr, "waiting for worker 1\n"); + pthread_join(worker1, &result); + //fprintf(stderr, "waiting for worker 2\n"); + pthread_join(worker2, &result); + + work(NULL); + + PASS("tlv-initializer"); + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/tlv-terminators/init.s b/src/dyld-apple/unit-tests/test-cases/tlv-terminators/init.s new file mode 100644 index 000000000..9bb901c0d --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/tlv-terminators/init.s @@ -0,0 +1,10 @@ + + # _myinit sets up TLV content + .thread_init_func + #if __LP64__ + .quad _myinit + #else + .long _myinit + #endif + +.subsections_via_symbols diff --git a/src/dyld-apple/unit-tests/test-cases/tlv-terminators/main.c b/src/dyld-apple/unit-tests/test-cases/tlv-terminators/main.c new file mode 100644 index 000000000..94d1d2a3a --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/tlv-terminators/main.c @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2010 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +struct thread_var_info +{ + pthread_t th; + int* a_addr; + int* b_addr; + bool a_terminated; + bool b_terminated; +}; + +struct thread_var_info threadmain; +struct thread_var_info thread1; +struct thread_var_info thread2; + + +__thread int a; // statically, initially 0 +__thread int b = 5; // statically, initially 5 + +extern void _tlv_atexit(void (*termfunc)(void* objAddr), void* objAddr); + +void myinit() +{ + a = 11; // dynamically initialized to 11 + b = 42; // dynamically initialized to 42 +} + +void myterm(void* objAddr) +{ + pthread_t self = pthread_self(); + //fprintf(stderr, "myterm(%p), self=%p\n", objAddr, self); + if ( thread1.th == self && thread1.a_addr == objAddr ) + thread1.a_terminated = true; + else if ( thread1.th == self && thread1.b_addr == objAddr ) + thread1.b_terminated = true; + else if ( thread2.th == self && thread2.a_addr == objAddr ) + thread2.a_terminated = true; + else if ( thread2.th == self && thread2.b_addr == objAddr ) + thread2.b_terminated = true; + else if ( threadmain.th == self && threadmain.a_addr == objAddr ) + threadmain.a_terminated = true; + else if ( threadmain.th == self && threadmain.b_addr == objAddr ) + threadmain.b_terminated = true; +} + +static void* work(void* arg) +{ + if ( a != 11 ) { + FAIL("tlv-terminators: a not initialized to 11"); + exit(0); + } + if ( b != 42 ) { + FAIL("tlv-terminators: b not initialized to 42"); + exit(0); + } + struct thread_var_info* s = (struct thread_var_info*)arg; + s->th = pthread_self(); + s->a_addr = &a; + s->b_addr = &b; + s->a_terminated = false; + s->b_terminated = false; + //fprintf(stderr, "self=%p, arg=%p, &a=%p, &b=%p\n", s->th, arg, s->a_addr , s->b_addr); + + _tlv_atexit(myterm, &a); + _tlv_atexit(myterm, &b); + + return NULL; +} + +int main() +{ + pthread_t worker1; + if ( pthread_create(&worker1, NULL, work, &thread1) != 0 ) { + FAIL("pthread_create failed"); + exit(0); + } + + pthread_t worker2; + if ( pthread_create(&worker2, NULL, work, &thread2) != 0 ) { + FAIL("pthread_create failed"); + exit(0); + } + + void* result; + //fprintf(stderr, "waiting for worker 1\n"); + pthread_join(worker1, &result); + //fprintf(stderr, "waiting for worker 2\n"); + pthread_join(worker2, &result); + + work(&threadmain); + + //fprintf(stderr, "thread1: &a=%p, &b=%p\n", thread1.a_addr, thread1.b_addr); + //fprintf(stderr, "thread2: &a=%p, &b=%p\n", thread2.a_addr, thread2.b_addr); + //fprintf(stderr, "threadm: &a=%p, &b=%p\n", threadmain.a_addr, threadmain.b_addr); + + if ( ! thread1.a_terminated ) { + FAIL("tlv-terminators: terminator for a on thread 1 not run"); + exit(0); + } + if ( ! thread1.b_terminated ) { + FAIL("tlv-terminators: terminator for b on thread 1 not run"); + exit(0); + } + + if ( ! thread2.a_terminated ) { + FAIL("tlv-terminators: terminator for a on thread 2 not run"); + exit(0); + } + if ( ! thread2.b_terminated ) { + FAIL("tlv-terminators: terminator for b on thread 2 not run"); + exit(0); + } + + if ( threadmain.a_terminated ) { + FAIL("tlv-terminators: terminator for a on main thread run early"); + exit(0); + } + if ( threadmain.b_terminated ) { + FAIL("tlv-terminators: terminator for b on main thread run early"); + exit(0); + } + + + PASS("tlv-terminators"); + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/trie-symbol-overrun/foo.c b/src/dyld-apple/unit-tests/test-cases/trie-symbol-overrun/foo.c new file mode 100644 index 000000000..62d5f38dd --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/trie-symbol-overrun/foo.c @@ -0,0 +1,12 @@ + +void abc() +{ +} + +void abcdefghi() +{ +} + +void abcdee() +{ +} diff --git a/src/dyld-apple/unit-tests/test-cases/trie-symbol-overrun/main.c b/src/dyld-apple/unit-tests/test-cases/trie-symbol-overrun/main.c new file mode 100644 index 000000000..e3d07f1e1 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/trie-symbol-overrun/main.c @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2009 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include +#include +#include + + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + + +int main() +{ + // allocate two pages + vm_address_t addr = 0; + kern_return_t r = vm_allocate(mach_task_self(), &addr, 8192, VM_FLAGS_ANYWHERE); + if ( r != KERN_SUCCESS ) { + FAIL("vm_allocate returned %d", r); + exit(0); + } + // mark the second page unreadable + mprotect((char*)(addr+4096), 4096, 0); + // copy a string to the end of the first page + char* sym = (char*)(addr+4096-5); + strcpy(sym, "_abd"); + + // call a dyld API that uses the string + // if dyld reads past the end of the string, it will crash + // trie parser can read past end of input symbol name +#if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && (__MAC_OS_X_VERSION_MIN_REQUIRED <= __MAC_10_5) + _dyld_lookup_and_bind(sym, NULL, NULL); +#else + dlsym(RTLD_DEFAULT, sym); +#endif + PASS("trie-symbol-overrun"); + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/unloadable-library-residue/bar.c b/src/dyld-apple/unit-tests/test-cases/unloadable-library-residue/bar.c new file mode 100644 index 000000000..400e1bf65 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/unloadable-library-residue/bar.c @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ + + +int bar1 = 1; +#if HAS_BAR2 +int bar2 = 2; +#endif +int bar3 = 3; + diff --git a/src/dyld-apple/unit-tests/test-cases/unloadable-library-residue/foo.c b/src/dyld-apple/unit-tests/test-cases/unloadable-library-residue/foo.c new file mode 100644 index 000000000..8abc708be --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/unloadable-library-residue/foo.c @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ + +extern int bar1; +extern int bar2; +extern int bar3; + + + +int foo() +{ + return bar1 + bar2 + bar3; +} diff --git a/src/dyld-apple/unit-tests/test-cases/unloadable-library-residue/main.c b/src/dyld-apple/unit-tests/test-cases/unloadable-library-residue/main.c new file mode 100644 index 000000000..d1bab845b --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/unloadable-library-residue/main.c @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2005-2009 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@ + */ +#include +#include +#include +#include +#include + +#include "test.h" // PASS(), FAIL() + + +int main() +{ +#if __MAC_OS_X_VERSION_MIN_REQUIRED + // load libfoo which depends on libbar + const struct mach_header* mh = NSAddImage("libfoo.dylib", NSADDIMAGE_OPTION_RETURN_ON_ERROR); + if ( mh != NULL ) { + FAIL("library-cant-be-bound: NSAddImage should have failed"); + return 1; + } +#else + if ( dlopen("libfoo.dylib", RTLD_LAZY) != NULL ){ + FAIL("library-cant-be-bound: dlopen should have failed"); + return 1; + } +#endif + + uint32_t count = _dyld_image_count(); + for(uint32_t i=0; i < count; ++i) { + const char* name = _dyld_get_image_name(i); + //fprintf(stderr, "%s\n", name); + if (strcmp(name, "libfoo.dylib") == 0 ) { + FAIL("library-cant-be-bound: libfoo.dylib shows up in list of images"); + return 0; + } + } + + + PASS("library-cant-be-bound"); + return 0; +} \ No newline at end of file diff --git a/src/dyld-apple/unit-tests/test-cases/upward-dylib-init-order/b.c b/src/dyld-apple/unit-tests/test-cases/upward-dylib-init-order/b.c new file mode 100644 index 000000000..9a146f446 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/upward-dylib-init-order/b.c @@ -0,0 +1,10 @@ +#include +#include "common.h" + + + +static __attribute__((constructor)) void myInit() +{ + setB(); + //fprintf(stderr, "init b\n"); +} diff --git a/src/dyld-apple/unit-tests/test-cases/upward-dylib-init-order/c.c b/src/dyld-apple/unit-tests/test-cases/upward-dylib-init-order/c.c new file mode 100644 index 000000000..0d79e7cd7 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/upward-dylib-init-order/c.c @@ -0,0 +1,8 @@ +#include + + + +static __attribute__((constructor)) void myInit() +{ + //fprintf(stderr, "init c\n"); +} diff --git a/src/dyld-apple/unit-tests/test-cases/upward-dylib-init-order/common.c b/src/dyld-apple/unit-tests/test-cases/upward-dylib-init-order/common.c new file mode 100644 index 000000000..663e4a19b --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/upward-dylib-init-order/common.c @@ -0,0 +1,37 @@ +#include "common.h" +#include + +static bool b = false; +static bool u = false; +static bool u2 = false; +static bool isOk = true; + +void setB() +{ + if ( u || b || u2 ) + isOk = false; + b = true; +} + +void setU() +{ + if ( u || u2 ) + isOk = false; + u = true; +} + +void setU2() +{ + if ( u2 ) + isOk = false; + u2 = true; +} + +// return true iff +// setB() was called, then setU() +bool ok() +{ + //fprintf(stderr, "isOk=%d, u=%d, b=%d\n", isOk, u, b); + return isOk && u && b && u2; +} + diff --git a/src/dyld-apple/unit-tests/test-cases/upward-dylib-init-order/common.h b/src/dyld-apple/unit-tests/test-cases/upward-dylib-init-order/common.h new file mode 100644 index 000000000..05427dcbb --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/upward-dylib-init-order/common.h @@ -0,0 +1,8 @@ +#include + +extern void setB(); +extern void setU(); +extern void setU2(); + +extern bool ok(); + diff --git a/src/dyld-apple/unit-tests/test-cases/upward-dylib-init-order/main.c b/src/dyld-apple/unit-tests/test-cases/upward-dylib-init-order/main.c new file mode 100644 index 000000000..e7edc5517 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/upward-dylib-init-order/main.c @@ -0,0 +1,19 @@ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +#include "common.h" + +int main() +{ + if ( ok() ) + PASS("upward-dylib-init-order"); + else + FAIL("upward-dylib-init-order"); + + return EXIT_SUCCESS; +} + + diff --git a/src/dyld-apple/unit-tests/test-cases/upward-dylib-init-order/u.c b/src/dyld-apple/unit-tests/test-cases/upward-dylib-init-order/u.c new file mode 100644 index 000000000..81c404385 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/upward-dylib-init-order/u.c @@ -0,0 +1,10 @@ +#include +#include "common.h" + + +static __attribute__((constructor)) void myInit() +{ + setU(); + //fprintf(stderr, "init u\n"); +} + diff --git a/src/dyld-apple/unit-tests/test-cases/upward-dylib-init-order/u2.c b/src/dyld-apple/unit-tests/test-cases/upward-dylib-init-order/u2.c new file mode 100644 index 000000000..7add33f50 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/upward-dylib-init-order/u2.c @@ -0,0 +1,10 @@ +#include +#include "common.h" + + +static __attribute__((constructor)) void myInit() +{ + setU2(); + //fprintf(stderr, "init u2\n"); +} + diff --git a/src/dyld-apple/unit-tests/test-cases/upward-dylib/down.c b/src/dyld-apple/unit-tests/test-cases/upward-dylib/down.c new file mode 100644 index 000000000..f576ad6c6 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/upward-dylib/down.c @@ -0,0 +1,22 @@ +#include +#include "up.h" + +static int state = 0; + + +// should run second because down.dylib is lower than up.dylib +static __attribute__((constructor)) void myInit3() +{ + //fprintf(stderr, "myInit3()\n"); + state = 1; +} + +int getdown() +{ + return state; +} + +int getdownsup() +{ + return whatsup(); +} \ No newline at end of file diff --git a/src/dyld-apple/unit-tests/test-cases/upward-dylib/down.h b/src/dyld-apple/unit-tests/test-cases/upward-dylib/down.h new file mode 100644 index 000000000..e4eb56c61 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/upward-dylib/down.h @@ -0,0 +1,3 @@ +extern int getdown(); +extern int getdownsup(); + diff --git a/src/dyld-apple/unit-tests/test-cases/upward-dylib/main.c b/src/dyld-apple/unit-tests/test-cases/upward-dylib/main.c new file mode 100644 index 000000000..302478a78 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/upward-dylib/main.c @@ -0,0 +1,20 @@ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +#include "up.h" +#include "down.h" + +int main() +{ + if ( whatsup() ) + PASS("upward-dylib"); + else + FAIL("upward-dylib"); + + return EXIT_SUCCESS; +} + + diff --git a/src/dyld-apple/unit-tests/test-cases/upward-dylib/main2.c b/src/dyld-apple/unit-tests/test-cases/upward-dylib/main2.c new file mode 100644 index 000000000..25ee4eba9 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/upward-dylib/main2.c @@ -0,0 +1,20 @@ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +#include "up.h" +#include "down.h" + +int main() +{ + if ( getdownsup() ) + PASS("upward-dylib"); + else + FAIL("upward-dylib"); + + return EXIT_SUCCESS; +} + + diff --git a/src/dyld-apple/unit-tests/test-cases/upward-dylib/up.c b/src/dyld-apple/unit-tests/test-cases/upward-dylib/up.c new file mode 100644 index 000000000..2451d74e0 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/upward-dylib/up.c @@ -0,0 +1,21 @@ +#include +#include "down.h" + +static int state = 0; + +#ifndef STUB +// should run second because up.dylib is higher than down.dylib +static __attribute__((constructor)) void myInit1() +{ + //fprintf(stderr, "myInit1()\n"); + if ( getdown() ) { + state = 1; + } +} +#endif + +int whatsup() +{ + return state; +} + diff --git a/src/dyld-apple/unit-tests/test-cases/upward-dylib/up.h b/src/dyld-apple/unit-tests/test-cases/upward-dylib/up.h new file mode 100644 index 000000000..9ed804cf7 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/upward-dylib/up.h @@ -0,0 +1,2 @@ + +extern int whatsup(); diff --git a/src/dyld-apple/unit-tests/test-cases/weak-coalesce-c++/a.h b/src/dyld-apple/unit-tests/test-cases/weak-coalesce-c++/a.h new file mode 100644 index 000000000..574026dd1 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/weak-coalesce-c++/a.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +template +struct wrapper { + static X x; +}; + +template X wrapper::x; diff --git a/src/dyld-apple/unit-tests/test-cases/weak-coalesce-c++/a1.cc b/src/dyld-apple/unit-tests/test-cases/weak-coalesce-c++/a1.cc new file mode 100644 index 000000000..0395912ca --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/weak-coalesce-c++/a1.cc @@ -0,0 +1,4 @@ +#include "a.h" + +int get_x_1 (void) { return wrapper::x; } +void set_x_1 (int x_) { wrapper::x = x_; } diff --git a/src/dyld-apple/unit-tests/test-cases/weak-coalesce-c++/a2.cc b/src/dyld-apple/unit-tests/test-cases/weak-coalesce-c++/a2.cc new file mode 100644 index 000000000..b3a3feda4 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/weak-coalesce-c++/a2.cc @@ -0,0 +1,4 @@ +#include "a.h" + +int get_x_2 (void) { return wrapper::x; } +void set_x_2 (int x_) { wrapper::x = x_; } diff --git a/src/dyld-apple/unit-tests/test-cases/weak-coalesce-c++/main.cc b/src/dyld-apple/unit-tests/test-cases/weak-coalesce-c++/main.cc new file mode 100644 index 000000000..3915e94ed --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/weak-coalesce-c++/main.cc @@ -0,0 +1,20 @@ +#include +#include // exit(), EXIT_SUCCESS + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +extern void set_x_1(int); +extern void set_x_2(int); +extern int get_x_1(void); +extern int get_x_2(void); + +int main() +{ + set_x_1 (17); + set_x_2 (76); + + if (get_x_1() == 76 && get_x_2() == 76) + PASS("weak-coalesce=c++"); + else + FAIL("weak-coalesce=c++"); +} diff --git a/src/dyld-apple/unit-tests/test-cases/weak-coalesce-inserted/base.c b/src/dyld-apple/unit-tests/test-cases/weak-coalesce-inserted/base.c new file mode 100644 index 000000000..a2d200b81 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/weak-coalesce-inserted/base.c @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include +#include + +#include "base.h" +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +static bool wasProblem = false; + +static const char* coal1Where = NULL; +static int* coal1Addr = NULL; +static int checkInCountCoal1 = 0; + +void baseVerifyCoal1(const char* where, int* addr) +{ + //fprintf(stderr, "baseVerifyCoal1(%s, %p)\n", where, addr); + ++checkInCountCoal1; + if ( coal1Where == NULL ) { + coal1Where = where; + coal1Addr = addr; + } + else { + if ( addr != coal1Addr ) { + fprintf(stderr, "coal1 resolved to different locations. %p %s and %p %s\n", + coal1Addr, coal1Where, addr, where); + wasProblem = true; + } + } +} + + +static const char* coal2Where = NULL; +static int* coal2Addr = NULL; +static int checkInCountCoal2 = 0; + +void baseVerifyCoal2(const char* where, int* addr) +{ + //fprintf(stderr, "baseVerifyCoal2(%s, %p)\n", where, addr); + ++checkInCountCoal2; + if ( coal2Where == NULL ) { + coal2Where = where; + coal2Addr = addr; + } + else { + if ( addr != coal2Addr ) { + fprintf(stderr, "coal2 resolved to different locations. %p %s and %p %s\n", + coal2Addr, coal2Where, addr, where); + wasProblem = true; + } + } +} + + + +void baseCheck() +{ + if ( wasProblem || (checkInCountCoal1 != 3) || (checkInCountCoal2 != 2) ) + FAIL("weak-coal"); + else + PASS("weak-coal"); +} + diff --git a/src/dyld-apple/unit-tests/test-cases/weak-coalesce-inserted/base.h b/src/dyld-apple/unit-tests/test-cases/weak-coalesce-inserted/base.h new file mode 100644 index 000000000..482b83276 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/weak-coalesce-inserted/base.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ + + +extern void baseCheck(); + +extern int coal1; +extern int coal2; + +extern void baseVerifyCoal1(const char* where, int* addr); +extern void baseVerifyCoal2(const char* where, int* addr); diff --git a/src/dyld-apple/unit-tests/test-cases/weak-coalesce-inserted/foo1.c b/src/dyld-apple/unit-tests/test-cases/weak-coalesce-inserted/foo1.c new file mode 100644 index 000000000..0707e0de8 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/weak-coalesce-inserted/foo1.c @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include +#include "base.h" + + + +int __attribute__((weak)) coal1 = 1; +int __attribute__((weak)) coal2 = 1; + + +static __attribute__((constructor)) void myinit() +{ + //fprintf(stderr, "myinit() in foo1.c\n"); + baseVerifyCoal1("in foo1", &coal1); + baseVerifyCoal2("in foo1", &coal2); +} + + diff --git a/src/dyld-apple/unit-tests/test-cases/weak-coalesce-inserted/foo2.c b/src/dyld-apple/unit-tests/test-cases/weak-coalesce-inserted/foo2.c new file mode 100644 index 000000000..13e8b0710 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/weak-coalesce-inserted/foo2.c @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include +#include "base.h" + + +int __attribute__((weak)) coal1 = 2; +int __attribute__((weak)) coal2 = 2; + +static __attribute__((constructor)) void myinit() +{ + //fprintf(stderr, "myinit() in foo1.c\n"); + baseVerifyCoal1("in foo2", &coal1); + baseVerifyCoal2("in foo2", &coal2); +} diff --git a/src/dyld-apple/unit-tests/test-cases/weak-coalesce-inserted/main.c b/src/dyld-apple/unit-tests/test-cases/weak-coalesce-inserted/main.c new file mode 100644 index 000000000..bf38e29ad --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/weak-coalesce-inserted/main.c @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +#include "base.h" + +int __attribute__((weak)) coal1 = 3; + + +int main() +{ + baseVerifyCoal1("in main", &coal1); + + baseCheck(); + return EXIT_SUCCESS; +} + + diff --git a/src/dyld-apple/unit-tests/test-cases/weak-coalesce-stubs/bar.c b/src/dyld-apple/unit-tests/test-cases/weak-coalesce-stubs/bar.c new file mode 100644 index 000000000..344a82c94 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/weak-coalesce-stubs/bar.c @@ -0,0 +1,6 @@ +void bar() {} + +void foo() __attribute__((weak)); +void foo() +{ +} \ No newline at end of file diff --git a/src/dyld-apple/unit-tests/test-cases/weak-coalesce-stubs/foo.c b/src/dyld-apple/unit-tests/test-cases/weak-coalesce-stubs/foo.c new file mode 100644 index 000000000..348cf656e --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/weak-coalesce-stubs/foo.c @@ -0,0 +1,61 @@ + + +void foo() __attribute__((weak)); + +void foo() +{ +} + +void abcdefghijklmnopqrstuvwxzy() {} +void abcde3fghijklmnopqrstuvwxzy() {} +void abcdef4ghijklmnopqrstuvwxzy() {} +void abcdefgh5ijklmnopqrstuvwxzy() {} +void abcdefghij6klmnopqrstuvwxzy() {} +void abcdefghijk7lmnopqrstuvwxzy() {} +void abcdefghijklm8nopqrstuvwxzy() {} +void abcdefghijklmn9opqrstuvwxzy() {} +void a1bcdefghijklmnopqrstuvwxzy() {} +void a2bcdefghijklmnopqrstuvwxzy() {} +void a3bcdefghijklmnopqrstuvwxzy() {} +void a4bcdefghijklmnopqrstuvwxzy() {} +void a5bcdefghijklmnopqrstuvwxzy() {} +void a6bcdefghijklmnopqrstuvwxzy() {} +void a7bcdefghijklmnopqrstuvwxzy() {} +void a8bcdefghijklmnopqrstuvwxzy() {} +void a9bcdefghijklmnopqrstuvwxzy() {} +void ab1cdefghijklmnopqrstuvwxzy() {} +void ab2cdefghijklmnopqrstuvwxzy() {} +void ab3cdefghijklmnopqrstuvwxzy() {} +void ab4cdefghijklmnopqrstuvwxzy() {} +void ab5cdefghijklmnopqrstuvwxzy() {} +void ab6cdefghijklmnopqrstuvwxzy() {} +void ab7cdefghijklmnopqrstuvwxzy() {} +void ab8cdefghijklmnopqrstuvwxzy() {} +void ab9cdefghijklmnopqrstuvwxzy() {} +void abc1defghijklmnopqrstuvwxzy() {} +void abc2defghijklmnopqrstuvwxzy() {} +void abc3defghijklmnopqrstuvwxzy() {} +void abc4defghijklmnopqrstuvwxzy() {} +void abc5defghijklmnopqrstuvwxzy() {} +void abc6defghijklmnopqrstuvwxzy() {} +void abc7defghijklmnopqrstuvwxzy() {} +void abc8defghijklmnopqrstuvwxzy() {} +void abc9defghijklmnopqrstuvwxzy() {} +void abcd1efghijklmnopqrstuvwxzy() {} +void abcd2efghijklmnopqrstuvwxzy() {} +void abcd3efghijklmnopqrstuvwxzy() {} +void abcd4efghijklmnopqrstuvwxzy() {} +void abcd5efghijklmnopqrstuvwxzy() {} +void abcd6efghijklmnopqrstuvwxzy() {} +void abcd7efghijklmnopqrstuvwxzy() {} +void abcd8efghijklmnopqrstuvwxzy() {} +void abcd9efghijklmnopqrstuvwxzy() {} +void abcde1fghijklmn9opqrstuvwxzy() {} +void abcde2fghijklmn9opqrstuvwxzy() {} +void abcde3fghijklmn9opqrstuvwxzy() {} +void abcde4fghijklmn9opqrstuvwxzy() {} +void abcde5fghijklmn9opqrstuvwxzy() {} +void abcde6fghijklmn9opqrstuvwxzy() {} +void abcde7fghijklmn9opqrstuvwxzy() {} +void abcde8fghijklmn9opqrstuvwxzy() {} +void abcde9fghijklmn9opqrstuvwxzy() {} diff --git a/src/dyld-apple/unit-tests/test-cases/weak-coalesce-stubs/main.c b/src/dyld-apple/unit-tests/test-cases/weak-coalesce-stubs/main.c new file mode 100644 index 000000000..11a4c89b4 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/weak-coalesce-stubs/main.c @@ -0,0 +1,22 @@ +#include +#include +#include // exit(), EXIT_SUCCESS + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +// Loading MH_DYLIB_STUB causing coalescable miscount + +int main() +{ + // try to load stub many times + for (int i=0; i < 10; ++i) { + void* handle = dlopen("libstub.dylib", RTLD_LAZY); + if ( handle != NULL ) { + FAIL("weak-coalesce-stubs: load of libstub.dylib unexpectedly succeeded"); + } + } + // try to load real dylib + dlopen("libbar.dylib", RTLD_LAZY); + PASS("weak-coalesce-stubs"); + return 0; +} diff --git a/src/dyld-apple/unit-tests/test-cases/weak-coalesce/base.c b/src/dyld-apple/unit-tests/test-cases/weak-coalesce/base.c new file mode 100644 index 000000000..0deff525c --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/weak-coalesce/base.c @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include +#include + +#include "base.h" +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +static bool wasProblem = false; + +static const char* coal1Where = NULL; +static int* coal1Addr = NULL; +static int checkInCountCoal1 = 0; + +void baseVerifyCoal1(const char* where, int* addr) +{ + //fprintf(stderr, "baseVerifyCoal1(%s, %p)\n", where, addr); + ++checkInCountCoal1; + if ( coal1Where == NULL ) { + coal1Where = where; + coal1Addr = addr; + } + else { + if ( addr != coal1Addr ) { + fprintf(stderr, "coal1 resolved to different locations. %p in %s and %p in %s\n", + coal1Addr, coal1Where, addr, where); + wasProblem = true; + } + } +} + + +static const char* coal2Where = NULL; +static int* coal2Addr = NULL; +static int checkInCountCoal2 = 0; + +void baseVerifyCoal2(const char* where, int* addr) +{ + //fprintf(stderr, "baseVerifyCoal2(%s, %p)\n", where, addr); + ++checkInCountCoal2; + if ( coal2Where == NULL ) { + coal2Where = where; + coal2Addr = addr; + } + else { + if ( addr != coal2Addr ) { + fprintf(stderr, "coal2 resolved to different locations. %p in %s and %p in %s\n", + coal2Addr, coal2Where, addr, where); + wasProblem = true; + } + } +} + + + +void baseCheck() +{ + if ( wasProblem || (checkInCountCoal1 != 4) || (checkInCountCoal2 != 4) ) + FAIL("weak-coal"); + else + PASS("weak-coal"); +} + diff --git a/src/dyld-apple/unit-tests/test-cases/weak-coalesce/base.h b/src/dyld-apple/unit-tests/test-cases/weak-coalesce/base.h new file mode 100644 index 000000000..482b83276 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/weak-coalesce/base.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ + + +extern void baseCheck(); + +extern int coal1; +extern int coal2; + +extern void baseVerifyCoal1(const char* where, int* addr); +extern void baseVerifyCoal2(const char* where, int* addr); diff --git a/src/dyld-apple/unit-tests/test-cases/weak-coalesce/foo1.c b/src/dyld-apple/unit-tests/test-cases/weak-coalesce/foo1.c new file mode 100644 index 000000000..0707e0de8 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/weak-coalesce/foo1.c @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include +#include "base.h" + + + +int __attribute__((weak)) coal1 = 1; +int __attribute__((weak)) coal2 = 1; + + +static __attribute__((constructor)) void myinit() +{ + //fprintf(stderr, "myinit() in foo1.c\n"); + baseVerifyCoal1("in foo1", &coal1); + baseVerifyCoal2("in foo1", &coal2); +} + + diff --git a/src/dyld-apple/unit-tests/test-cases/weak-coalesce/foo2.c b/src/dyld-apple/unit-tests/test-cases/weak-coalesce/foo2.c new file mode 100644 index 000000000..a5770d4b8 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/weak-coalesce/foo2.c @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include +#include "base.h" + + +int coal1 = 2; +int __attribute__((weak)) coal2 = 2; + +static __attribute__((constructor)) void myinit() +{ + //fprintf(stderr, "myinit() in foo1.c\n"); + baseVerifyCoal1("in foo2", &coal1); + baseVerifyCoal2("in foo2", &coal2); +} diff --git a/src/dyld-apple/unit-tests/test-cases/weak-coalesce/foo3.c b/src/dyld-apple/unit-tests/test-cases/weak-coalesce/foo3.c new file mode 100644 index 000000000..c92a44458 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/weak-coalesce/foo3.c @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include +#include "base.h" + +int __attribute__((weak)) coal1 = 3; +int __attribute__((weak)) coal2 = 2; + +static __attribute__((constructor)) void myinit() +{ + //fprintf(stderr, "myinit() in foo1.c\n"); + baseVerifyCoal1("in foo3", &coal1); + baseVerifyCoal2("in foo3", &coal2); +} + diff --git a/src/dyld-apple/unit-tests/test-cases/weak-coalesce/main.c b/src/dyld-apple/unit-tests/test-cases/weak-coalesce/main.c new file mode 100644 index 000000000..6f4f9e146 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/weak-coalesce/main.c @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +#include "base.h" + +int main() +{ + + //fprintf(stderr, "myinit() in foo1.c\n"); + baseVerifyCoal1("in main", &coal1); + baseVerifyCoal2("in main", &coal2); + + baseCheck(); + return EXIT_SUCCESS; +} + + diff --git a/src/dyld-apple/unit-tests/test-cases/weak-external-reloc-flat/bar.c b/src/dyld-apple/unit-tests/test-cases/weak-external-reloc-flat/bar.c new file mode 100644 index 000000000..4787d5b85 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/weak-external-reloc-flat/bar.c @@ -0,0 +1,8 @@ + + +int bar[] = { 20, 21, 22, 23 }; + + +int __attribute__((weak)) frob[] = { 30, 31, 32, 33 }; + +int bar_getfrob(int x) { return frob[x]; } diff --git a/src/dyld-apple/unit-tests/test-cases/weak-external-reloc-flat/baz.c b/src/dyld-apple/unit-tests/test-cases/weak-external-reloc-flat/baz.c new file mode 100644 index 000000000..f35d05c81 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/weak-external-reloc-flat/baz.c @@ -0,0 +1,8 @@ + + + + + +int __attribute__((weak)) bar[] = { 30, 31, 32, 33 }; + + diff --git a/src/dyld-apple/unit-tests/test-cases/weak-external-reloc-flat/foo.c b/src/dyld-apple/unit-tests/test-cases/weak-external-reloc-flat/foo.c new file mode 100644 index 000000000..d2d73efae --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/weak-external-reloc-flat/foo.c @@ -0,0 +1,14 @@ + + +extern int bar_getfrob(int x); + + +int __attribute__((weak)) foo[] = { 1, 2, 3, 4 }; +int __attribute__((weak)) bar[] = { 10, 11, 12, 13 }; +int __attribute__((weak)) frob[] = { 20, 21, 22, 23 }; + +int* pfoo = &foo[2]; + +int getfrob() { + return bar_getfrob(2); +} \ No newline at end of file diff --git a/src/dyld-apple/unit-tests/test-cases/weak-external-reloc-flat/main.c b/src/dyld-apple/unit-tests/test-cases/weak-external-reloc-flat/main.c new file mode 100644 index 000000000..e74b44db4 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/weak-external-reloc-flat/main.c @@ -0,0 +1,41 @@ + + +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +// pfoo is in libfoo.dylib +// libfoo.dylib also has weak foo[] +// pfoo should be rebound to point into the foo[] in main +int foo[] = { 5, 6, 7, 8 }; +extern int* pfoo; + +// libfoo.dylib has a weak bar[] +// libbar.dylib has a strong bar[] +// at build time linker only sees weak bar in libfoo.dylib +// at runtime, dyld uses strong bar in libbar.dylib +extern int bar[]; +int* pbar = &bar[1]; + + +// libfoo.dylib has a weak frob[] +// libbar.dylib has a weak frob[] and a funtion that refrences frob[] +// the function should use libfoo's frob[] even if libfoo is flat +extern int getfrob(); + +int main() +{ + if ( *pfoo != 7 ) + FAIL("weak-external-reloc-flat, pfoo=%d", *pfoo); + else if ( *pbar != 21 ) + FAIL("weak-external-reloc-flat, pbar=%d", *pbar); + else if ( getfrob() != 22 ) + FAIL("weak-external-reloc-flat, frob()=%d", getfrob()); + else + PASS("weak-external-reloc-flat"); + + return EXIT_SUCCESS; +} + + diff --git a/src/dyld-apple/unit-tests/test-cases/weak-external-reloc/bar.c b/src/dyld-apple/unit-tests/test-cases/weak-external-reloc/bar.c new file mode 100644 index 000000000..615196e30 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/weak-external-reloc/bar.c @@ -0,0 +1,6 @@ + + +int bar[] = { 20, 21, 22, 23 }; + +// needs something weak to join coalescing +int __attribute__((weak)) junk = 5; diff --git a/src/dyld-apple/unit-tests/test-cases/weak-external-reloc/baz.c b/src/dyld-apple/unit-tests/test-cases/weak-external-reloc/baz.c new file mode 100644 index 000000000..f35d05c81 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/weak-external-reloc/baz.c @@ -0,0 +1,8 @@ + + + + + +int __attribute__((weak)) bar[] = { 30, 31, 32, 33 }; + + diff --git a/src/dyld-apple/unit-tests/test-cases/weak-external-reloc/foo.c b/src/dyld-apple/unit-tests/test-cases/weak-external-reloc/foo.c new file mode 100644 index 000000000..9e4873a99 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/weak-external-reloc/foo.c @@ -0,0 +1,10 @@ + + + + + +int __attribute__((weak)) foo[] = { 1, 2, 3, 4 }; +int __attribute__((weak)) bar[] = { 10, 11, 12, 13 }; + +int* pfoo = &foo[2]; + diff --git a/src/dyld-apple/unit-tests/test-cases/weak-external-reloc/main.c b/src/dyld-apple/unit-tests/test-cases/weak-external-reloc/main.c new file mode 100644 index 000000000..a25c44805 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/weak-external-reloc/main.c @@ -0,0 +1,10 @@ + +extern void realmain(); + +int main() +{ + realmain(); + return 0; +} + + diff --git a/src/dyld-apple/unit-tests/test-cases/weak-external-reloc/realmain.c b/src/dyld-apple/unit-tests/test-cases/weak-external-reloc/realmain.c new file mode 100644 index 000000000..6870f5a90 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/weak-external-reloc/realmain.c @@ -0,0 +1,39 @@ + + +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +// pfoo is in libfoo.dylib +// libfoo.dylib also has weak foo[] +// pfoo should be rebound to point into the foo[] in main +int foo[] = { 5, 6, 7, 8 }; +extern int* pfoo; + +// libfoo.dylib has a weak bar[] +// libbar.dylib has a strong bar[] +// at build time linker only sees weak bar in libfoo.dylib +// at runtime, dyld uses strong bar in libbar.dylib +extern int bar[]; +int* pbar = &bar[1]; + +// there is only one stuff, but it is weak +// so we are testing the case when stuff is not overridden +int __attribute__((weak)) stuff[] = { 1, 2, 3, 4, 5 }; +int* pstuff = &stuff[3]; + + +void realmain() +{ + if ( *pfoo != 7 ) + FAIL("weak-external-reloc, pfoo=%d", *pfoo); + else if ( *pbar != 21 ) + FAIL("weak-external-reloc, pbar=%d", *pbar); + else if ( *pstuff != 4 ) + FAIL("weak-external-reloc, pstuffr=%d", *pstuff); + else + PASS("weak-external-reloc"); +} + + diff --git a/src/dyld-apple/unit-tests/test-cases/weak-in-dylib/foo.c b/src/dyld-apple/unit-tests/test-cases/weak-in-dylib/foo.c new file mode 100644 index 000000000..ec6f8367d --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/weak-in-dylib/foo.c @@ -0,0 +1,7 @@ + + + + + +int __attribute__((weak)) foo[] = { 1, 2, 3, 4 }; + diff --git a/src/dyld-apple/unit-tests/test-cases/weak-in-dylib/main.c b/src/dyld-apple/unit-tests/test-cases/weak-in-dylib/main.c new file mode 100644 index 000000000..358e0f79d --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/weak-in-dylib/main.c @@ -0,0 +1,22 @@ + +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +// libfoo.dylib has a weak foo[] +extern int foo[]; + +int* pfoo3 = &foo[3]; + +int main() +{ + if ( *pfoo3 != 4 ) + FAIL("weak-in-dylib, pfoo3=%d", *pfoo3); + else if ( foo[2] != 3 ) + FAIL("weak-in-dylib, foo[2]=%d", foo[2]); + else + PASS("weak-in-dyliby"); + return 0; +} + diff --git a/src/dyld-apple/unit-tests/test-cases/weak-lazy-slidable/bar.c b/src/dyld-apple/unit-tests/test-cases/weak-lazy-slidable/bar.c new file mode 100644 index 000000000..e52bf1133 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/weak-lazy-slidable/bar.c @@ -0,0 +1,12 @@ + + +#include + +bool bar1() +{ + return true; +} + +__attribute__((weak)) void junk() +{ +} diff --git a/src/dyld-apple/unit-tests/test-cases/weak-lazy-slidable/bar3.c b/src/dyld-apple/unit-tests/test-cases/weak-lazy-slidable/bar3.c new file mode 100644 index 000000000..0f278687f --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/weak-lazy-slidable/bar3.c @@ -0,0 +1,9 @@ + + +#include + +__attribute__((weak)) bool bar1() +{ + return false; +} + diff --git a/src/dyld-apple/unit-tests/test-cases/weak-lazy-slidable/foo.c b/src/dyld-apple/unit-tests/test-cases/weak-lazy-slidable/foo.c new file mode 100644 index 000000000..7e3c8a5cf --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/weak-lazy-slidable/foo.c @@ -0,0 +1,29 @@ +#include + + + +extern bool bar1(); + + +__attribute__((weak)) bool bar1() +{ + return false; +} + +__attribute__((weak)) bool bar2() +{ + return true; +} + + +bool foo1() +{ + return bar1(); +} + +bool foo2() +{ + return bar2(); +} + + diff --git a/src/dyld-apple/unit-tests/test-cases/weak-lazy-slidable/main.c b/src/dyld-apple/unit-tests/test-cases/weak-lazy-slidable/main.c new file mode 100644 index 000000000..a91ef8521 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/weak-lazy-slidable/main.c @@ -0,0 +1,24 @@ + + +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +extern bool foo1(); +extern bool foo2(); + + +int main() +{ + if ( foo1() && foo2() ) + PASS("weak-lazy-slidable"); + else + FAIL("weak-lazy-slidable"); + + return EXIT_SUCCESS; +} + + diff --git a/src/dyld-apple/unit-tests/test-cases/weak-lazy-slidable/other.c b/src/dyld-apple/unit-tests/test-cases/weak-lazy-slidable/other.c new file mode 100644 index 000000000..0c04195b9 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/weak-lazy-slidable/other.c @@ -0,0 +1,7 @@ +#include + +bool bar2() +{ + return false; +} + diff --git a/src/dyld-apple/unit-tests/test-cases/weak-library/foo.c b/src/dyld-apple/unit-tests/test-cases/weak-library/foo.c new file mode 100644 index 000000000..55808e471 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/weak-library/foo.c @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include "foo.h" + +int foo() +{ + return 10; +} diff --git a/src/dyld-apple/unit-tests/test-cases/weak-library/foo.h b/src/dyld-apple/unit-tests/test-cases/weak-library/foo.h new file mode 100644 index 000000000..78d1afd40 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/weak-library/foo.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +extern int foo(); diff --git a/src/dyld-apple/unit-tests/test-cases/weak-library/main.c b/src/dyld-apple/unit-tests/test-cases/weak-library/main.c new file mode 100644 index 000000000..b16a8e41f --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/weak-library/main.c @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include +#include + +#include "test.h" + +int +main(int argc, char **argv) +{ + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/weak-non-lazy/bar.c b/src/dyld-apple/unit-tests/test-cases/weak-non-lazy/bar.c new file mode 100644 index 000000000..9db55bd41 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/weak-non-lazy/bar.c @@ -0,0 +1,6 @@ + + +int bar[] = { 20, 21, 22, 23 }; + +// needs something weak to join coalescing fun +int __attribute__((weak)) junk = 5; diff --git a/src/dyld-apple/unit-tests/test-cases/weak-non-lazy/baz.c b/src/dyld-apple/unit-tests/test-cases/weak-non-lazy/baz.c new file mode 100644 index 000000000..411ae68b8 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/weak-non-lazy/baz.c @@ -0,0 +1,9 @@ + + + + + +int __attribute__((weak)) bar[] = { 30, 31, 32, 33 }; + + + diff --git a/src/dyld-apple/unit-tests/test-cases/weak-non-lazy/foo.c b/src/dyld-apple/unit-tests/test-cases/weak-non-lazy/foo.c new file mode 100644 index 000000000..a21268496 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/weak-non-lazy/foo.c @@ -0,0 +1,15 @@ + + + + + +int __attribute__((weak)) foo[] = { 1, 2, 3, 4 }; +int __attribute__((weak)) bar[] = { 10, 11, 12, 13 }; + +int* getfoo() +{ + return foo; +} + + + diff --git a/src/dyld-apple/unit-tests/test-cases/weak-non-lazy/main.c b/src/dyld-apple/unit-tests/test-cases/weak-non-lazy/main.c new file mode 100644 index 000000000..523b7be58 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/weak-non-lazy/main.c @@ -0,0 +1,12 @@ + + +// put test code in a dylib so that is slides +extern void realmain(); + +int main() +{ + realmain(); + return 0; +} + + diff --git a/src/dyld-apple/unit-tests/test-cases/weak-non-lazy/realmain.c b/src/dyld-apple/unit-tests/test-cases/weak-non-lazy/realmain.c new file mode 100644 index 000000000..ca4a6f21e --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/weak-non-lazy/realmain.c @@ -0,0 +1,36 @@ + + +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +// getfoo() is in libfoo.dylib and uses a non-lazy pointer to reference foo +// libfoo.dylib also has weak foo[] +// getfoo() should be rebound to point into the foo[] in main +int foo[] = { 5, 6, 7, 8 }; +extern int* getfoo(); + +// libfoo.dylib has a weak bar[] +// libbar.dylib has a strong bar[] +// at build time linker only sees weak bar in libfoo.dylib +// at runtime, dyld uses strong bar in libbar.dylib +extern int bar[]; + + +int __attribute__((weak)) stuff[] = { 1, 2, 3, 4, 5 }; + + +void realmain() +{ + if ( getfoo()[2] != 7 ) + FAIL("weak-non-lazy, getfoo()[2]=%d", getfoo()[2]); + else if ( bar[1] != 21 ) + FAIL("weak-non-lazy, bar[1]=%d", bar[1]); + else if ( stuff[3] != 4 ) + FAIL("weak-external-reloc, pstuffr=%d", stuff[3]); + else + PASS("weak-non-lazy"); +} + + diff --git a/src/dyld-apple/unit-tests/test-cases/weak-override/foo.c b/src/dyld-apple/unit-tests/test-cases/weak-override/foo.c new file mode 100644 index 000000000..984ee82a0 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/weak-override/foo.c @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include + + + +int __attribute__((weak)) myfunc() +{ + return 0; +} + +int __attribute__((weak)) foo() +{ + return myfunc(); +} + +int (*myfuncproc)() = &myfunc; + +int bar() +{ + return (*myfuncproc)(); +} diff --git a/src/dyld-apple/unit-tests/test-cases/weak-override/main.c b/src/dyld-apple/unit-tests/test-cases/weak-override/main.c new file mode 100644 index 000000000..8010cc0ef --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/weak-override/main.c @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2005-2008 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +// foo is defined in libfoo.dylib it calls myfunc() +extern int foo(); +// bar is defined in libfoo.dylib it calls myfunc() +extern int bar(); + +int main() +{ + if ( (foo() == 10) && (bar() == 10) ) + PASS("weak-override"); + else + FAIL("weak-override"); + return EXIT_SUCCESS; +} + + +// myfunc() also defined in libfoo.dylib but weak there, so this should override +int myfunc() +{ + return 10; +} + diff --git a/src/dyld-apple/unit-tests/test-cases/weak-symbol-flat/foo.c b/src/dyld-apple/unit-tests/test-cases/weak-symbol-flat/foo.c new file mode 100644 index 000000000..b4b8cea56 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/weak-symbol-flat/foo.c @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ + + +#ifdef SYMBOL_PRESENT +int foo() +{ + return 10; +} + +#endif + diff --git a/src/dyld-apple/unit-tests/test-cases/weak-symbol-flat/foo.h b/src/dyld-apple/unit-tests/test-cases/weak-symbol-flat/foo.h new file mode 100644 index 000000000..7f4d4ab29 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/weak-symbol-flat/foo.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include + +int foo() WEAK_IMPORT_ATTRIBUTE; diff --git a/src/dyld-apple/unit-tests/test-cases/weak-symbol-flat/main.c b/src/dyld-apple/unit-tests/test-cases/weak-symbol-flat/main.c new file mode 100644 index 000000000..146fe817a --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/weak-symbol-flat/main.c @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include +#include // EXIT_SUCCESS + +#include "test.h" +#include "foo.h" + +int main(int argc, const char *argv[]) +{ + if (argc > 1) { + // when arg is passed, libfoo is used and foo should be defined + if ( &foo == NULL ) + FAIL("weak-symbol-flat foo not found"); + else if ( foo() != 10 ) + FAIL("weak-symbol-flat foo not found"); + else + PASS("weak-symbol-flat when symbol found"); + } + else { + // when no args are passed, libfoo_missing is used and foo should be undefined + if ( &foo != NULL ) + FAIL("weak-symbol-flat foo found"); + else + PASS("weak-symbol-flat when symbol missing"); + } + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/weak-symbol/foo.c b/src/dyld-apple/unit-tests/test-cases/weak-symbol/foo.c new file mode 100644 index 000000000..c7a34098a --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/weak-symbol/foo.c @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#if SYMBOL_PRESENT +int foo() +{ + return 10; +} +#endif diff --git a/src/dyld-apple/unit-tests/test-cases/weak-symbol/foo.h b/src/dyld-apple/unit-tests/test-cases/weak-symbol/foo.h new file mode 100644 index 000000000..7f4d4ab29 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/weak-symbol/foo.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include + +int foo() WEAK_IMPORT_ATTRIBUTE; diff --git a/src/dyld-apple/unit-tests/test-cases/weak-symbol/main.c b/src/dyld-apple/unit-tests/test-cases/weak-symbol/main.c new file mode 100644 index 000000000..e5c890594 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/weak-symbol/main.c @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include +#include // EXIT_SUCCESS + +#include "test.h" +#include "foo.h" + +int main(int argc, const char* argv[]) +{ + if ( &foo == NULL ) + { + PASS("weak-symbol not available"); + } + return 0; +} diff --git a/src/dyld-apple/unit-tests/test-cases/weak_import/foo.c b/src/dyld-apple/unit-tests/test-cases/weak_import/foo.c new file mode 100644 index 000000000..8f5083556 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/weak_import/foo.c @@ -0,0 +1,19 @@ + + +#include "foo.h" + +void func1() {} +void func3() {} +int data1 = 0; +int data3; +int data5 = 0; + +#if ALL_SYMBOLS +void func2() {} +void func4() {} + +int data2 = 0; // weak_import initialized +int data4; // weak_import uninitialized +int data6 = 0; // weak_import +#endif + diff --git a/src/dyld-apple/unit-tests/test-cases/weak_import/foo.h b/src/dyld-apple/unit-tests/test-cases/weak_import/foo.h new file mode 100644 index 000000000..f455515fe --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/weak_import/foo.h @@ -0,0 +1,16 @@ + + +extern void func1(); +extern void func2() __attribute__((weak_import)); +extern void func3(); +extern void func4() __attribute__((weak_import)); + +extern int data1; +extern int data2 __attribute__((weak_import)); +extern int data3; +extern int data4 __attribute__((weak_import)); +extern int data5; +extern int data6 __attribute__((weak_import)); + + + diff --git a/src/dyld-apple/unit-tests/test-cases/weak_import/main.c b/src/dyld-apple/unit-tests/test-cases/weak_import/main.c new file mode 100644 index 000000000..a777a526f --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/weak_import/main.c @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2009 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@ + */ +#include +#include +#include + +#include "test.h" // PASS(), FAIL() + +#include "foo.h" + + +int* pdata5 = &data5; +int* pdata6 = &data6; + + +int main(int argc, const char* argv[]) +{ + if ( argc > 1 ) { + // this weak case where the even symbol names are missing at runtime + func1(); + func3(); + data1 = data3; + if ( (&func2 == NULL) && (&func4 == NULL) && (&data2 == NULL) && (&data4 == NULL) && (pdata6 == NULL) ) + PASS("weak_import"); + else + FAIL("weak_import"); + } + else { + // this is the regular case where all symbols exist at runtime + func1(); + func2(); + func3(); + func4(); + data1 = data2 + data3 + data4; + PASS("weak_import"); + } + return 0; +} + diff --git a/src/dyld-apple/unit-tests/test-cases/zero-fill-segment/foo.c b/src/dyld-apple/unit-tests/test-cases/zero-fill-segment/foo.c new file mode 100644 index 000000000..4202b55d5 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/zero-fill-segment/foo.c @@ -0,0 +1,8 @@ + +extern int foo[]; + +int getfoo(int x) +{ + return foo[x]; +} + diff --git a/src/dyld-apple/unit-tests/test-cases/zero-fill-segment/main.c b/src/dyld-apple/unit-tests/test-cases/zero-fill-segment/main.c new file mode 100644 index 000000000..0676756ec --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/zero-fill-segment/main.c @@ -0,0 +1,48 @@ +/* + * 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include // dlopen() + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +int +main(int argc, const char* argv[]) +{ + void* handle = dlopen("foo.bundle", RTLD_LAZY); + if ( handle == NULL ) { + FAIL("zero-fill-segment: dlopen(\"%s\") failed with: %s", "foo.bundle", dlerror()); + exit(0); + } + + void* sym = dlsym(handle, "getfoo"); + if ( sym == NULL ) { + FAIL("zero-fill-segment: dlsym(handle, \"getfoo\") failed"); + exit(0); + } + + dlclose(handle); + + PASS("zero-fill-segment"); + return EXIT_SUCCESS; +} diff --git a/src/dyld-apple/unit-tests/test-cases/zero-fill-segment/zero.s b/src/dyld-apple/unit-tests/test-cases/zero-fill-segment/zero.s new file mode 100644 index 000000000..cfb52eaa8 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/zero-fill-segment/zero.s @@ -0,0 +1,5 @@ + +.globl _foo +.zerofill __MYZERO, __zero, _foo, 8192 + + diff --git a/src/dyld-apple/unit-tests/test-cases/zero-length-segment/foo.c b/src/dyld-apple/unit-tests/test-cases/zero-length-segment/foo.c new file mode 100644 index 000000000..85e6cd8c3 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/zero-length-segment/foo.c @@ -0,0 +1 @@ +void foo() {} diff --git a/src/dyld-apple/unit-tests/test-cases/zero-length-segment/main.c b/src/dyld-apple/unit-tests/test-cases/zero-length-segment/main.c new file mode 100644 index 000000000..f5a0e5d79 --- /dev/null +++ b/src/dyld-apple/unit-tests/test-cases/zero-length-segment/main.c @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +int +main(int argc, const char* argv[]) +{ + return EXIT_SUCCESS; +} diff --git a/src/external/cctools-port b/src/external/cctools-port index 78f4fb343..5897f4a2c 160000 --- a/src/external/cctools-port +++ b/src/external/cctools-port @@ -1 +1 @@ -Subproject commit 78f4fb343eb7ef0f5fc4ac5c0d6d2bef37e6cd85 +Subproject commit 5897f4a2c7585a520eb5ab77635bf169201a14c3 diff --git a/src/external/compiler-rt b/src/external/compiler-rt index addf1e22f..da0a3111a 160000 --- a/src/external/compiler-rt +++ b/src/external/compiler-rt @@ -1 +1 @@ -Subproject commit addf1e22f8f6c62372ec5611de2913a700911be2 +Subproject commit da0a3111a3cb9d6bc1b5759e0df197db8e96a163 diff --git a/src/external/libcxx b/src/external/libcxx index 969c8a301..abd4f6a06 160000 --- a/src/external/libcxx +++ b/src/external/libcxx @@ -1 +1 @@ -Subproject commit 969c8a3011e76aa921e8f82f404672cfbd28dc26 +Subproject commit abd4f6a066fa0dd58b548eb7cabe135615ca26ed diff --git a/src/external/libcxxabi b/src/external/libcxxabi index 6429cbdd6..97854481e 160000 --- a/src/external/libcxxabi +++ b/src/external/libcxxabi @@ -1 +1 @@ -Subproject commit 6429cbdd6212caeb6fb8b5567404e559a8dd6428 +Subproject commit 97854481e323b3d590a4190af20f66afcc579d67 diff --git a/src/external/libdispatch b/src/external/libdispatch index 4059e1c33..125187c75 160000 --- a/src/external/libdispatch +++ b/src/external/libdispatch @@ -1 +1 @@ -Subproject commit 4059e1c33aa675fe91c8ab8444005c7f140847b7 +Subproject commit 125187c75050708129f9d4b08dda615812e11506 diff --git a/src/external/libkqueue b/src/external/libkqueue index 0728438e9..04a42a47c 160000 --- a/src/external/libkqueue +++ b/src/external/libkqueue @@ -1 +1 @@ -Subproject commit 0728438e9c36734ddccc03103f196ab8b960ce88 +Subproject commit 04a42a47c3ca11479be0d98306c2916023ae153d diff --git a/src/kernel/CMakeLists.txt b/src/kernel/CMakeLists.txt index 4619f6966..95b7ffedf 100644 --- a/src/kernel/CMakeLists.txt +++ b/src/kernel/CMakeLists.txt @@ -22,10 +22,7 @@ add_definitions(-D__APPLE__ -D__DYNAMIC__ -DPRIVATE) add_subdirectory(libsyscall) add_subdirectory(mach_server/client) -SET(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/darling") -SET(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) -SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) - +set(DYLIB_INSTALL_NAME "/usr/lib/system/libsystem_kernel.dylib") add_darling_library(system_kernel SHARED $ #$ diff --git a/src/keymgr/CMakeLists.txt b/src/keymgr/CMakeLists.txt index 7dd8b3d87..04efa1326 100644 --- a/src/keymgr/CMakeLists.txt +++ b/src/keymgr/CMakeLists.txt @@ -3,19 +3,11 @@ project(keymgr) cmake_minimum_required(VERSION 2.4.0) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -nostdinc") -set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -nostdlib -Wl,--version-script=${DARLING_TOP_DIRECTORY}/darwin.map") +set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -nostdlib") include_directories(${CMAKE_SOURCE_DIR}/libc/fbsdcompat) include_directories(${CMAKE_SOURCE_DIR}/kernel/libsyscall/wrappers) -if(CMAKE_SIZEOF_VOID_P EQUAL 4 OR CMAKE_INSTALL_LIBDIR STREQUAL "lib32") - set(BITS 32) - add_definitions(-DTARGET_CPU_X86=1) -else(CMAKE_SIZEOF_VOID_P EQUAL 4 OR CMAKE_INSTALL_LIBDIR STREQUAL "lib32") - set(BITS 64) - add_definitions(-DTARGET_CPU_X86_64=1) -endif(CMAKE_SIZEOF_VOID_P EQUAL 4 OR CMAKE_INSTALL_LIBDIR STREQUAL "lib32") - add_definitions(-DTARGET_OS_MAC=1) add_definitions(-DHAVE_STDINT_H=1) add_definitions(-D__APPLE__ -D__DYNAMIC__) @@ -24,10 +16,8 @@ set(keymgr_sources keymgr.c ) -SET(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/darling") -SET(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) -SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) - -add_library(keymgr SHARED ${keymgr_sources}) +set(DYLIB_INSTALL_NAME "/usr/lib/system/libkeymgr.dylib") +add_darling_library(keymgr SHARED ${keymgr_sources}) +target_link_libraries(keymgr PRIVATE system_c system_dyld) install(TARGETS keymgr DESTINATION ${CMAKE_INSTALL_LIBDIR}/darling) diff --git a/src/launchd/liblaunch/CMakeLists.txt b/src/launchd/liblaunch/CMakeLists.txt index 00a8e1c1a..9a34883f7 100644 --- a/src/launchd/liblaunch/CMakeLists.txt +++ b/src/launchd/liblaunch/CMakeLists.txt @@ -11,7 +11,7 @@ include_directories(${DARLING_TOP_DIRECTORY}/src/libc/darwin) include_directories(${CMAKE_CURRENT_BINARY_DIR}) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fblocks -nostdinc -include ${DARLING_TOP_DIRECTORY}/kernel-include/sys/fileport.h -ggdb") -set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -nostdlib -Wl,--version-script=${DARLING_TOP_DIRECTORY}/darwin.map") +set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -nostdlib -Wl,-flat_namespace -Wl,-undefined,suppress") mig(job.defs) mig(helper.defs) @@ -24,12 +24,9 @@ set(launch_sources ${CMAKE_CURRENT_BINARY_DIR}/helperServer.c ) -SET(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/darling") -SET(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) -SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) - -add_library(launch SHARED ${launch_sources}) -target_link_libraries(launch system_c system_kernel) +set(DYLIB_INSTALL_NAME "/usr/lib/system/liblaunch.dylib") +add_darling_library(launch SHARED ${launch_sources}) +target_link_libraries(launch PRIVATE system_c system_kernel) install(TARGETS launch DESTINATION ${CMAKE_INSTALL_LIBDIR}/darling) diff --git a/src/libc/CMakeLists.txt b/src/libc/CMakeLists.txt index 675eca67f..bc7bae04d 100644 --- a/src/libc/CMakeLists.txt +++ b/src/libc/CMakeLists.txt @@ -99,10 +99,7 @@ endif() add_subdirectory(darwin) -SET(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/darling") -SET(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) -SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) - +set(DYLIB_INSTALL_NAME "/usr/lib/system/libsystem_c.dylib") add_darling_library(system_c SHARED $ $ diff --git a/src/libffi/CMakeLists.txt b/src/libffi/CMakeLists.txt index f970b213b..9cbdc2d81 100644 --- a/src/libffi/CMakeLists.txt +++ b/src/libffi/CMakeLists.txt @@ -12,14 +12,10 @@ add_definitions(-D__APPLE__ -D__MACH__) add_definitions(-DTARGET_OS_MAC=1) add_definitions(-DHAVE_STDINT_H=1) add_definitions(-D__APPLE__ -D__DYNAMIC__) -add_definitions(-D__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__=1080) +#add_definitions(-D__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__=1080) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -nostdinc -D__DARWIN_UNIX03 -fPIC -w") -set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -nostdlib -Wl,--version-script=${DARLING_TOP_DIRECTORY}/darwin.map") - -SET(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/darling") -SET(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) -SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) +set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -nostdlib") include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include) @@ -32,7 +28,8 @@ set(ffi_SRCS x86/x86-ffi_darwin.c ) -add_library(ffi SHARED ${ffi_SRCS}) +set(DYLIB_INSTALL_NAME "/usr/lib/libffi.dylib") +add_darling_library(ffi SHARED ${ffi_SRCS}) target_link_libraries(ffi system) install(TARGETS ffi DESTINATION ${CMAKE_INSTALL_LIBDIR}/darling) diff --git a/src/libffi/x86/darwin64.S b/src/libffi/x86/darwin64.S index 4782de735..eba451e9d 100644 --- a/src/libffi/x86/darwin64.S +++ b/src/libffi/x86/darwin64.S @@ -29,11 +29,6 @@ #include #include -#ifdef DARLING -# define _ffi_call_unix64 ffi_call_unix64 -# define _ffi_closure_unix64 ffi_closure_unix64 -#endif - .file "darwin64.S" .text @@ -44,7 +39,7 @@ for this function. This has been allocated by ffi_call. We also deallocate some of the stack that has been alloca'd. */ - .align 8 + .align 3 .globl _ffi_call_unix64 _ffi_call_unix64: @@ -111,53 +106,53 @@ Lstore_table: .long Lst_int64-Lstore_table /* FFI_TYPE_POINTER */ .text - .align 8 + .align 3 Lst_void: ret - .align 8 + .align 3 Lst_uint8: movzbq %al, %rax movq %rax, (%rdi) ret - .align 8 + .align 3 Lst_sint8: movsbq %al, %rax movq %rax, (%rdi) ret - .align 8 + .align 3 Lst_uint16: movzwq %ax, %rax movq %rax, (%rdi) - .align 8 + .align 3 Lst_sint16: movswq %ax, %rax movq %rax, (%rdi) ret - .align 8 + .align 3 Lst_uint32: movl %eax, %eax movq %rax, (%rdi) - .align 8 + .align 3 Lst_sint32: cltq movq %rax, (%rdi) ret - .align 8 + .align 3 Lst_int64: movq %rax, (%rdi) ret - .align 8 + .align 3 Lst_float: movss %xmm0, (%rdi) ret - .align 8 + .align 3 Lst_double: movsd %xmm0, (%rdi) ret Lst_ldouble: fstpt (%rdi) ret - .align 8 + .align 3 Lst_struct: leaq -20(%rsp), %rsi /* Scratch area in redzone. */ @@ -188,7 +183,7 @@ Lst_struct: /* Many times we can avoid loading any SSE registers at all. It's not worth an indirect jump to load the exact set of SSE registers needed; zero or all is a good compromise. */ - .align 8 + .align 3 LUW3: Lload_sse: movdqa 48(%r10), %xmm0 @@ -202,7 +197,7 @@ Lload_sse: jmp Lret_from_load_sse LUW4: - .align 8 + .align 3 .globl _ffi_closure_unix64 _ffi_closure_unix64: @@ -224,7 +219,7 @@ Lret_from_save_sse: leaq 176(%rsp), %rsi movq %rsp, %rdx leaq 208(%rsp), %rcx - call _ffi_closure_unix64_inner@PLT + call _ffi_closure_unix64_inner /* Deallocate stack frame early; return value is now in redzone. */ addq $200, %rsp @@ -255,38 +250,38 @@ Lload_table: .long Lld_int64-Lload_table /* FFI_TYPE_POINTER */ .text - .align 8 + .align 3 Lld_void: ret - .align 8 + .align 3 Lld_int8: movzbl -24(%rsp), %eax ret - .align 8 + .align 3 Lld_int16: movzwl -24(%rsp), %eax ret - .align 8 + .align 3 Lld_int32: movl -24(%rsp), %eax ret - .align 8 + .align 3 Lld_int64: movq -24(%rsp), %rax ret - .align 8 + .align 3 Lld_float: movss -24(%rsp), %xmm0 ret - .align 8 + .align 3 Lld_double: movsd -24(%rsp), %xmm0 ret - .align 8 + .align 3 Lld_ldouble: fldt -24(%rsp) ret - .align 8 + .align 3 Lld_struct: /* There are four possibilities here, %rax/%rdx, %xmm0/%rax, %rax/%xmm0, %xmm0/%xmm1. We collapse two by always loading @@ -305,7 +300,7 @@ Lld_struct: ret /* See the comment above Lload_sse; the same logic applies here. */ - .align 8 + .align 3 LUW8: Lsave_sse: movdqa %xmm0, 48(%rsp) @@ -319,8 +314,7 @@ Lsave_sse: jmp Lret_from_save_sse LUW9: -//.section __TEXT,__eh_frame,coalesced,no_toc+strip_static_syms+live_support -.text +.section __TEXT,__eh_frame,coalesced,no_toc+strip_static_syms+live_support EH_frame1: .set L$set$0,LECIE1-LSCIE1 /* CIE Length */ .long L$set$0 @@ -338,7 +332,7 @@ LSCIE1: .byte 0x8 /* uleb128 0x8 */ .byte 0x90 /* DW_CFA_offset, column 0x10 */ .byte 0x1 - .align 8 + .align 3 LECIE1: .globl _ffi_call_unix64.eh _ffi_call_unix64.eh: @@ -382,7 +376,7 @@ LASFDE1: .long L$set$5 .byte 0xb /* DW_CFA_restore_state */ - .align 8 + .align 3 LEFDE1: .globl _ffi_closure_unix64.eh _ffi_closure_unix64.eh: @@ -414,8 +408,8 @@ LASFDE3: .long L$set$10 .byte 0xb /* DW_CFA_restore_state */ - .align 8 + .align 3 LEFDE3: - // .subsections_via_symbols + .subsections_via_symbols #endif /* __x86_64__ */ diff --git a/src/libffi/x86/x86-darwin.S b/src/libffi/x86/x86-darwin.S index 469263ca4..6c85ea61d 100644 --- a/src/libffi/x86/x86-darwin.S +++ b/src/libffi/x86/x86-darwin.S @@ -29,14 +29,6 @@ * assembly programming in 8 years. */ -#ifdef DARLING -# define _ffi_prep_args ffi_prep_args -# define _ffi_call_SYSV ffi_call_SYSV -# define _ffi_closure_SYSV ffi_closure_SYSV -# define _ffi_closure_SYSV_inner ffi_closure_SYSV_inner -# define _ffi_closure_raw_SYSV ffi_closure_raw_SYSV -#endif - #ifndef __x86_64__ #define LIBFFI_ASM @@ -54,7 +46,7 @@ .globl _ffi_prep_args - .align 16 + .align 4 .globl _ffi_call_SYSV _ffi_call_SYSV: @@ -174,7 +166,7 @@ Lepilogue: LFE1: .ffi_call_SYSV_end: - .align 16 + .align 4 FFI_HIDDEN (ffi_closure_SYSV) .globl _ffi_closure_SYSV @@ -252,7 +244,7 @@ LFE2: #define RAW_CLOSURE_USER_DATA_OFFSET (RAW_CLOSURE_FUN_OFFSET + 4) #define CIF_FLAGS_OFFSET 20 - .align 16 + .align 4 FFI_HIDDEN (ffi_closure_raw_SYSV) .globl _ffi_closure_raw_SYSV @@ -308,19 +300,14 @@ Lrcls_retllong: jmp Lrcls_epilogue LFE3: #endif -#ifndef DARLING + .section __IMPORT,__jump_table,symbol_stubs,self_modifying_code+pure_instructions,5 L_ffi_closure_SYSV_inner$stub: .indirect_symbol _ffi_closure_SYSV_inner hlt ; hlt ; hlt ; hlt ; hlt -#else -L_ffi_closure_SYSV_inner$stub: - jmp _ffi_closure_SYSV_inner -#endif -#ifndef DARLING + .section __TEXT,__eh_frame,coalesced,no_toc+strip_static_syms+live_support -#endif EH_frame1: .set L$set$0,LECIE1-LSCIE1 .long L$set$0 @@ -338,7 +325,7 @@ LSCIE1: .byte 0x4 .byte 0x88 .byte 0x1 - .align 4 + .align 2 LECIE1: .globl _ffi_call_SYSV.eh _ffi_call_SYSV.eh: @@ -363,7 +350,7 @@ LASFDE1: .long L$set$4 .byte 0xd .byte 0x4 - .align 4 + .align 2 LEFDE1: .globl _ffi_closure_SYSV.eh _ffi_closure_SYSV.eh: @@ -388,7 +375,7 @@ LASFDE2: .long L$set$8 .byte 0xd .byte 0x4 - .align 4 + .align 2 LEFDE2: #if !FFI_NO_RAW_API @@ -421,7 +408,7 @@ LASFDE3: .long L$set$14 .byte 0x85 .byte 0x3 - .align 4 + .align 2 LEFDE3: #endif diff --git a/src/libgcc/CMakeLists.txt b/src/libgcc/CMakeLists.txt index 87c895119..ea193c49d 100644 --- a/src/libgcc/CMakeLists.txt +++ b/src/libgcc/CMakeLists.txt @@ -7,17 +7,13 @@ if(COMMAND cmake_policy) endif(COMMAND cmake_policy) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -nostdinc") -set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -nostdlib -Wl,--version-script=${DARLING_TOP_DIRECTORY}/darwin.map") +set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -nostdlib") set(libgcc_sources dummy.c ) -SET(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/darling") -SET(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) -SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) - -add_library(gcc_s SHARED ${libgcc_sources}) -target_link_libraries(gcc_s compiler_rt) +add_darling_library(gcc_s SHARED ${libgcc_sources}) +target_link_libraries(gcc_s PRIVATE compiler_rt -sub_library libcompiler_rt) install(TARGETS gcc_s DESTINATION ${CMAKE_INSTALL_LIBDIR}/darling) diff --git a/src/libiconv/CMakeLists.txt b/src/libiconv/CMakeLists.txt index 36fc992be..4baf5e864 100644 --- a/src/libiconv/CMakeLists.txt +++ b/src/libiconv/CMakeLists.txt @@ -6,26 +6,13 @@ if(COMMAND cmake_policy) cmake_policy(SET CMP0003 NEW) endif(COMMAND cmake_policy) - -if(CMAKE_SIZEOF_VOID_P EQUAL 4 OR CMAKE_INSTALL_LIBDIR STREQUAL "lib32") - set(BITS 32) - add_definitions(-DTARGET_CPU_X86=1) -else(CMAKE_SIZEOF_VOID_P EQUAL 4 OR CMAKE_INSTALL_LIBDIR STREQUAL "lib32") - set(BITS 64) - add_definitions(-DTARGET_CPU_X86_64=1) -endif(CMAKE_SIZEOF_VOID_P EQUAL 4 OR CMAKE_INSTALL_LIBDIR STREQUAL "lib32") - add_definitions(-DTARGET_OS_MAC=1) add_definitions(-DHAVE_STDINT_H=1) add_definitions(-D__APPLE__ -D__DYNAMIC__) add_definitions(-D__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__=1080) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -nostdinc -D__DARWIN_UNIX03 -fPIC -w -include ${CMAKE_CURRENT_BINARY_DIR}/include.h -O0 -ggdb") -set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -nostdlib -Wl,--version-script=${DARLING_TOP_DIRECTORY}/darwin.map") - -SET(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/darling") -SET(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) -SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) +set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -nostdlib") add_definitions(-DHAVE_CONFIG_H) configure_file(include.h.in include.h) @@ -64,8 +51,8 @@ set(iconv_SRCS #libcharset/src/relocatable.c ) -add_library(iconv SHARED ${iconv_SRCS} -) +set(DYLIB_INSTALL_NAME "/usr/lib/libiconv.2.dylib") +add_darling_library(iconv SHARED ${iconv_SRCS}) target_link_libraries(iconv system) install(TARGETS iconv DESTINATION ${CMAKE_INSTALL_LIBDIR}/darling) diff --git a/src/libinfo/CMakeLists.txt b/src/libinfo/CMakeLists.txt index 5bdb7e1e9..f70047fb1 100644 --- a/src/libinfo/CMakeLists.txt +++ b/src/libinfo/CMakeLists.txt @@ -14,11 +14,9 @@ add_definitions(-DTARGET_OS_MAC=1) add_definitions(-DHAVE_STDINT_H=1) add_definitions(-D__APPLE__ -D__DYNAMIC__) add_definitions(-D__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__=1080) -add_definitions(-Ddlopen=__darwin_dlopen -Ddlsym=__darwin_dlsym -Ddlclose=__darwin_dlclose) -add_definitions(-Dh_errno=__darwin_h_errno) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -nostdinc -D__DARWIN_UNIX03 -fPIC -w -ggdb") -set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -nostdlib -Wl,--unresolved-symbols=ignore-all -Wl,--version-script=${CMAKE_CURRENT_SOURCE_DIR}/darwin.map") +set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -nostdlib") add_subdirectory(gen.subproj) add_subdirectory(util.subproj) @@ -29,11 +27,8 @@ add_subdirectory(lookup.subproj) add_subdirectory(membership.subproj) add_subdirectory(darling-resolver) -SET(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/darling") -SET(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) -SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) - -add_library(system_info SHARED $ +set(DYLIB_INSTALL_NAME "/usr/lib/system/libsystem_info.dylib") +add_darling_library(system_info SHARED $ $ $ $ @@ -42,7 +37,7 @@ add_library(system_info SHARED $ $ $ ) -target_link_libraries(system_info system_c resolv-darwin) +target_link_libraries(system_info PRIVATE system_c resolv-darwin system_dyld system_blocks libdispatch_shared system_notify) install(TARGETS system_info DESTINATION ${CMAKE_INSTALL_LIBDIR}/darling) install(FILES master.passwd DESTINATION libexec/darling/etc) diff --git a/src/libinfo/dns.subproj/herror.c b/src/libinfo/dns.subproj/herror.c index 4b7e9f930..4f4013ec5 100644 --- a/src/libinfo/dns.subproj/herror.c +++ b/src/libinfo/dns.subproj/herror.c @@ -73,16 +73,7 @@ const char * const h_errlist[] = { }; const int h_nerr = { sizeof h_errlist / sizeof h_errlist[0] }; -#ifndef DARLING int h_errno; -#else -__asm__(".section .bss\n" - ".global __darwin_h_errno\n" - ".symver __darwin_h_errno, h_errno@DARWIN\n" - ".comm __darwin_h_errno, 4\n" - "__darwin_h_errno:\n" - ".word 0\n"); -#endif /* * herror -- diff --git a/src/libm/CMakeLists.txt b/src/libm/CMakeLists.txt index 1d13eac41..3cfd891d6 100644 --- a/src/libm/CMakeLists.txt +++ b/src/libm/CMakeLists.txt @@ -5,8 +5,8 @@ enable_language(C ASM) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -msse -msse2 -msse3 -w -ggdb -O0") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D__DARWIN_UNIX03 -fPIC -w -DBUILDING_FOR_CARBONCORE_LEGACY -funroll-loops -msse3") # -DBUILDING_FOR_CARBONCORE_LEGACY") -set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -nostdlib -Wl,-flat_namespace -Wl,-undefined,suppress -Wl,-alias_list,${CMAKE_CURRENT_SOURCE_DIR}/Exports/libm_Intel.a.alias") -set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,-alias_list,${CMAKE_CURRENT_SOURCE_DIR}/Exports/libmathCommonIntel.alias") +set(CMAKE_ASM_FLAGS "${CMAKE_ASM_FLAGS} -DBUILDING_FOR_CARBONCORE_LEGACY") +set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -nostdlib -Wl,-flat_namespace -Wl,-undefined,suppress") include_directories(${CMAKE_CURRENT_SOURCE_DIR}/Source/Intel) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../kernel-include) @@ -40,7 +40,7 @@ set(libm_sources Source/Intel/s_matherr.c Source/Intel/s_tanh.c Source/Intel/xmm_arcsincostan.c - #Source/Intel/xmm_erfgamma.c + Source/Intel/xmm_erfgamma.c Source/Intel/xmm_exp.c Source/Intel/xmm_fma.c Source/Intel/xmm_log.c @@ -72,7 +72,7 @@ set(libm_sources Source/Intel/e_log10.S Source/Intel/e_log.S Source/Intel/e_remainder.S - Source/Intel/e_remquol.S + #Source/Intel/e_remquol.S Source/Intel/e_sqrt.S Source/Intel/exp2f.S Source/Intel/exp2.S @@ -122,7 +122,7 @@ set(libm_sources Source/Intel/scalbnl.S Source/Intel/scalbn.S Source/Intel/s_cosisin.S - #Source/Intel/s_cos.S + Source/Intel/s_cos.S #Source/Intel/s_ilogb.S Source/Intel/sinfcosf.S Source/Intel/sinhf.S @@ -140,114 +140,6 @@ set(libm_sources Source/Intel/truncl.S Source/Intel/trunc.S -) -set(libm_nofenv_sources - Source/Intel/s_sin.S - Source/Intel/s_tan.S - Source/Intel/acos.c - Source/Intel/asin.c - Source/Intel/atan.c - Source/Intel/complex.c - Source/Intel/containsNothingIntel.c - Source/Intel/e_acosh.c - Source/Intel/e_atanh.c - Source/Intel/e_cosh.c - Source/Intel/e_sinh.c - Source/Intel/expf_logf_powf.c - Source/Intel/fmaf.c - Source/Intel/s_asinh.c - Source/Intel/sincostan.c - Source/Intel/s_tanh.c - Source/Intel/xmm_arcsincostan.c - #Source/Intel/xmm_erfgamma.c - Source/Intel/xmm_exp.c - Source/Intel/xmm_fma.c - Source/Intel/xmm_log.c - Source/Intel/xmm_nextafter.c - Source/Intel/xmm_power.c - Source/Intel/xmm_remainder.c - Source/Intel/xmm_sqrt.c - - Source/Intel/acosf.S - Source/Intel/acoshf.S - Source/Intel/asinf.S - Source/Intel/asinhf.S - Source/Intel/atan2f.S - Source/Intel/atanf.S - Source/Intel/atanhf.S - Source/Intel/cbrtf.S - Source/Intel/ceilf.S - Source/Intel/ceill.S - Source/Intel/ceil.S - Source/Intel/copysign.S - Source/Intel/coshf.S - Source/Intel/cosh.S - Source/Intel/e_acos.S - Source/Intel/e_asin.S - Source/Intel/e_atan2.S - Source/Intel/e_cbrtl.S - Source/Intel/e_exp.S - Source/Intel/e_log10.S - Source/Intel/e_log.S - Source/Intel/e_remainder.S - Source/Intel/e_sqrt.S - Source/Intel/exp2.S - Source/Intel/expm1f.S - Source/Intel/expm1.S - Source/Intel/exp.S - Source/Intel/floorf.S - Source/Intel/floorl.S - Source/Intel/floor.S - Source/Intel/fmaxfminfdim.S - Source/Intel/fmod.S - Source/Intel/frexp.S - Source/Intel/hypotf.S - Source/Intel/hypotl.S - Source/Intel/hypot.S - Source/Intel/ilogb.S - Source/Intel/log10f.S - Source/Intel/log2f.S - Source/Intel/log2.S - Source/Intel/logf.S - #Source/Intel/log.S - Source/Intel/lrintl.S - Source/Intel/lroundf.S - Source/Intel/lroundl.S - Source/Intel/lround.S - Source/Intel/modff.S - Source/Intel/modfl.S - Source/Intel/modf.S - Source/Intel/nearbyintf.S - Source/Intel/nearbyintl.S - Source/Intel/nearbyint.S - Source/Intel/nextafterl.S - Source/Intel/nextafter.S - #Source/Intel/powf.S - Source/Intel/rintf.S - Source/Intel/rintl.S - Source/Intel/rint.S - Source/Intel/roundf.S - Source/Intel/roundl.S - Source/Intel/round.S - Source/Intel/s_atan.S - Source/Intel/scalbnf.S - Source/Intel/scalbnl.S - #Source/Intel/s_cos.S - #Source/Intel/s_ilogb.S - Source/Intel/sinfcosf.S - Source/Intel/sinhf.S - Source/Intel/sinh.S - Source/Intel/s_log1p.S - #Source/Intel/s_logb.S - Source/Intel/s_rint.S - #Source/Intel/s_significand.S - Source/Intel/tanf.S - Source/Intel/tanhf.S - Source/Intel/tanh.S - Source/Intel/truncf.S - Source/Intel/truncl.S - Source/Intel/trunc.S - ) # Gross hack to rename symbols in $fenv_access_off variants @@ -257,16 +149,13 @@ SET(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/darling SET(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) -add_library(system_m_extra OBJECT Source/Intel/xmm_misc.c Source/Intel/xmm_log.c) +add_library(system_m_extra OBJECT Source/Intel/xmm_misc.c Source/Intel/xmm_log.c Source/Intel/fmaxfminfdim.S Source/Intel/nextafter.S Source/Intel/xmm_remainder.c Source/Intel/e_remquol.S) set_target_properties(system_m_extra PROPERTIES COMPILE_FLAGS "-UBUILDING_FOR_CARBONCORE_LEGACY -O0 -ggdb") -#add_library(system_m_extra_no_fenv OBJECT Source/Intel/xmm_log.c) -#set_target_properties(system_m_extra_no_fenv PROPERTIES COMPILE_FLAGS "-UBUILDING_FOR_CARBONCORE_LEGACY -ftrapping-math -fPIC -DNO_FENV") - -#add_library(system_m_no_fenv OBJECT ${libm_nofenv_sources}) # $) -#set_target_properties(system_m_no_fenv PROPERTIES COMPILE_FLAGS "-UBUILDING_FOR_CARBONCORE_LEGACY -ftrapping-math -fPIC -DNO_FENV") - +set(DYLIB_INSTALL_NAME "/usr/lib/system/libsystem_m.dylib") add_darling_library(system_m SHARED ${libm_sources} $) +set_property(TARGET system_m APPEND_STRING PROPERTY LINK_FLAGS + "-Wl,-alias_list,${CMAKE_CURRENT_SOURCE_DIR}/Exports/libm_Intel.a.alias -Wl,-alias_list,${CMAKE_CURRENT_SOURCE_DIR}/Exports/libmathCommonIntel.alias") #target_link_libraries(system_m) install(TARGETS system_m DESTINATION ${CMAKE_INSTALL_LIBDIR}/darling) diff --git a/src/libm/Exports/libm_Intel.a.alias b/src/libm/Exports/libm_Intel.a.alias index 6492923f2..556c27191 100644 --- a/src/libm/Exports/libm_Intel.a.alias +++ b/src/libm/Exports/libm_Intel.a.alias @@ -1,4 +1,4 @@ -"#.o names are from the ppc build, and probably don't map properly to other archs" +#.o names are from the ppc build, and probably don't map properly to other archs" #libm.a(complex.o): _cabsf _cabsf$fenv_access_off diff --git a/src/libm/Source/fenv_private.h b/src/libm/Source/fenv_private.h new file mode 100644 index 000000000..75c4025a9 --- /dev/null +++ b/src/libm/Source/fenv_private.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2002 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * The contents of this file constitute Original Code as defined in and + * are subject to the Apple Public Source License Version 1.1 (the + * "License"). You may not use this file except in compliance with the + * License. Please obtain a copy of the License at + * http://www.apple.com/publicsource and read it before using this file. + * + * This 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 OR NON-INFRINGEMENT. Please see the + * License for the specific language governing rights and limitations + * under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/******************************************************************************* +* * +* File: fenv_private.h * +* * +* Contains: Defines for manipulating environmental settings directly. * +* * +*******************************************************************************/ + +#ifndef __FENV_PRIVATE__ +#define __FENV_PRIVATE__ + +/* Pick up the publicly visible enums formerly sited here */ +#include + +/* Macros to get or set environment flags doubleword */ +#define FEGETENVD(x) asm volatile ("mffs %0" : "=f" (x)); +#define FESETENVD(x) asm volatile ("mtfsf 255,%0" : : "f" (x)); + +/* exception flags */ +#define FE_SET_FX 0x80000000 /* floating-point exception summary (FX) bit */ +#define FE_CLR_FX 0x7fffffff +#define SET_INVALID 0x01000000 + +/* the bitwise negation (one's complement) of FE_ALL_EXCEPT */ +#define FE_NO_EXCEPT 0xc1ffffff + +/* the bitwise OR of all of the separate exception bits in the FPSCR */ +#define FE_ALL_FLAGS 0xfff80300 + +/* the bitwise negation (one's complement) of the previous macro */ +#define FE_NO_FLAGS 0x0007fcff + +/* the bitwise OR of all of the separate invalid stickies in the FPSCR */ +#define FE_ALL_INVALID 0x01f80300 + +/* the bitwise negation (one's complement) of the previous macro */ +#define FE_NO_INVALID 0xfe07fcff + +/* an AND mask to disable all floating-point exception enables in the FPSCR */ +#define FE_NO_ENABLES 0xffffff07 + +/* rounding direction mode bits */ +#define FE_ALL_RND 0x00000003 +#define FE_NO_RND 0xfffffffc + +#define EXCEPT_MASK 0x1ff80000 + +#endif /* __FENV_PRIVATE__ */ diff --git a/src/libmalloc/CMakeLists.txt b/src/libmalloc/CMakeLists.txt index d7b0345f1..bb9c467a6 100644 --- a/src/libmalloc/CMakeLists.txt +++ b/src/libmalloc/CMakeLists.txt @@ -4,7 +4,7 @@ cmake_minimum_required(VERSION 2.4.0) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -msse -msse2 -msse3 -w -fblocks -ggdb") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D__DARWIN_UNIX03 -fPIC -w -include ${CMAKE_CURRENT_SOURCE_DIR}/../duct/include/commpage.h -nostdinc") -set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -nostdlib -Wl,--version-script=${DARLING_TOP_DIRECTORY}/darwin.map") +set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -nostdlib") if(CMAKE_SIZEOF_VOID_P EQUAL 4 OR CMAKE_INSTALL_LIBDIR STREQUAL "lib32") set(BITS 32) @@ -32,11 +32,9 @@ set(libmalloc_sources src/stack_logging_disk.c ) -SET(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/darling") -SET(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) -SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) - -add_library(system_malloc SHARED ${libmalloc_sources}) +set(DYLIB_INSTALL_NAME "/usr/lib/system/libsystem_malloc.dylib") +add_darling_library(system_malloc SHARED ${libmalloc_sources}) +target_link_libraries(system_malloc PRIVATE system_c system_kernel system_dyld) install(TARGETS system_malloc DESTINATION ${CMAKE_INSTALL_LIBDIR}/darling) diff --git a/src/libnotify/CMakeLists.txt b/src/libnotify/CMakeLists.txt index 3923e62cb..20f14523d 100644 --- a/src/libnotify/CMakeLists.txt +++ b/src/libnotify/CMakeLists.txt @@ -11,7 +11,7 @@ include_directories(${CMAKE_CURRENT_SOURCE_DIR}) include_directories(${CMAKE_CURRENT_BINARY_DIR}) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fblocks -nostdinc -include ${DARLING_TOP_DIRECTORY}/kernel-include/sys/fileport.h") -set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -nostdlib -Wl,--version-script=${DARLING_TOP_DIRECTORY}/darwin.map") +set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -nostdlib") mig(notify_ipc.defs) @@ -22,12 +22,9 @@ set(notify_sources ${CMAKE_CURRENT_BINARY_DIR}/notify_ipcUser.c ) -SET(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/darling") -SET(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) -SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) - -add_library(system_notify SHARED ${notify_sources}) -target_link_libraries(system_notify system_c system_kernel) +set(DYLIB_INSTALL_NAME "/usr/lib/system/libsystem_notify.dylib") +add_darling_library(system_notify SHARED ${notify_sources}) +target_link_libraries(system_notify system_c system_kernel system_blocks libdispatch_shared launch system_dyld) install(TARGETS system_notify DESTINATION ${CMAKE_INSTALL_LIBDIR}/darling) diff --git a/src/libremovefile/CMakeLists.txt b/src/libremovefile/CMakeLists.txt index 0c77043c9..e1c651e53 100644 --- a/src/libremovefile/CMakeLists.txt +++ b/src/libremovefile/CMakeLists.txt @@ -7,7 +7,7 @@ include_directories(${DARLING_TOP_DIRECTORY}/src/libc/gen) include_directories(${CMAKE_CURRENT_SOURCE_DIR}) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fblocks -nostdinc") -set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -nostdlib -Wl,--version-script=${DARLING_TOP_DIRECTORY}/darwin.map") +set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -nostdlib") set(removefile_sources removefile.c @@ -17,12 +17,9 @@ set(removefile_sources removefile_tree_walker.c ) -SET(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/darling") -SET(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) -SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) - -add_library(removefile SHARED ${removefile_sources}) -target_link_libraries(removefile system_c system_kernel) +set(DYLIB_INSTALL_NAME "/usr/lib/system/libremovefile.dylib") +add_darling_library(removefile SHARED ${removefile_sources}) +target_link_libraries(removefile system_c system_kernel system_dyld) install(TARGETS removefile DESTINATION ${CMAKE_INSTALL_LIBDIR}/darling) diff --git a/src/libresolv/CMakeLists.txt b/src/libresolv/CMakeLists.txt index 685f43fb1..2a1d6a52e 100644 --- a/src/libresolv/CMakeLists.txt +++ b/src/libresolv/CMakeLists.txt @@ -9,9 +9,8 @@ include_directories(${DARLING_TOP_DIRECTORY}/src/libinfo/lookup.subproj/) include_directories(${CMAKE_CURRENT_SOURCE_DIR}) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/dnsinfo) -add_definitions(-Dh_errno=__darwin_h_errno) -set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fblocks -nostdinc") -set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -nostdlib -Wl,--version-script=${DARLING_TOP_DIRECTORY}/darwin.map") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fblocks -nostdinc -DUSE__RES_9") +set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -nostdlib -Wl,-flat_namespace -Wl,-undefined,suppress") # Hide warnings add_definitions( @@ -55,8 +54,10 @@ SET(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/darling SET(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) -add_library(resolv-darwin SHARED ${resolv_sources}) -target_link_libraries(resolv-darwin system_c system_kernel) +set(DYLIB_INSTALL_NAME "/usr/lib/libresolv.9.dylib") +add_darling_library(resolv-darwin SHARED ${resolv_sources}) +set_target_properties(resolv-darwin PROPERTIES OUTPUT_NAME "resolv.9") +#target_link_libraries(resolv-darwin PRIVATE system) install(TARGETS resolv-darwin DESTINATION ${CMAKE_INSTALL_LIBDIR}/darling) diff --git a/src/libstdcxx/CMakeLists.txt b/src/libstdcxx/CMakeLists.txt index 7e9d46786..e522fd689 100644 --- a/src/libstdcxx/CMakeLists.txt +++ b/src/libstdcxx/CMakeLists.txt @@ -5,11 +5,7 @@ enable_language(C ASM) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -msse -msse2 -msse3 -w -nostdinc -nostdinc++") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D__DARWIN_UNIX03 -fPIC -w -include ${CMAKE_CURRENT_SOURCE_DIR}/build.h") -set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -nostdlib -Wl,--version-script=${DARLING_TOP_DIRECTORY}/darwin.map -Wl,-Bsymbolic-functions") - -if (${CMAKE_SYSTEM_PROCESSOR} STREQUAL "i686") - set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,${CMAKE_CURRENT_SOURCE_DIR}/i386-aliases") -endif (${CMAKE_SYSTEM_PROCESSOR} STREQUAL "i686") +set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -nostdlib") include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include/c++/4.2.1) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include/c++/4.2.1/backward) @@ -92,12 +88,10 @@ set(libstdcxx_sources src/dso_handle.c ) -SET(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/darling") -SET(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) -SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) - -add_library(stdcxx SHARED ${libstdcxx_sources}) -target_link_libraries(stdcxx cxxabi_shared) +set(DYLIB_INSTALL_NAME "/usr/lib/libstdc++.6.dylib") +add_darling_library(stdcxx SHARED ${libstdcxx_sources}) +target_link_libraries(stdcxx PRIVATE system cxxabi_shared) +set_target_properties(stdcxx PROPERTIES OUTPUT_NAME "stdc++.6") install(TARGETS stdcxx DESTINATION ${CMAKE_INSTALL_LIBDIR}/darling) diff --git a/src/libsystem/CMakeLists.txt b/src/libsystem/CMakeLists.txt index 0ffc55210..10dd106dd 100644 --- a/src/libsystem/CMakeLists.txt +++ b/src/libsystem/CMakeLists.txt @@ -8,23 +8,14 @@ endif(COMMAND cmake_policy) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -nostdinc -ggdb") -set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -nostdlib -Wl,--warn-unresolved-symbols -Wl,--version-script=${DARLING_TOP_DIRECTORY}/darwin.map") +set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -nostdlib") include_directories(${DARLING_TOP_DIRECTORY}/src/libc/fbsdcompat) include_directories(${DARLING_TOP_DIRECTORY}/src/kernel/libsyscall/wrappers) -if(CMAKE_SIZEOF_VOID_P EQUAL 4 OR CMAKE_INSTALL_LIBDIR STREQUAL "lib32") - set(BITS 32) - add_definitions(-DTARGET_CPU_X86=1) -else(CMAKE_SIZEOF_VOID_P EQUAL 4 OR CMAKE_INSTALL_LIBDIR STREQUAL "lib32") - set(BITS 64) - add_definitions(-DTARGET_CPU_X86_64=1) -endif(CMAKE_SIZEOF_VOID_P EQUAL 4 OR CMAKE_INSTALL_LIBDIR STREQUAL "lib32") - add_definitions(-DTARGET_OS_MAC=1) add_definitions(-DHAVE_STDINT_H=1) add_definitions(-D__APPLE__ -D__DYNAMIC__) -add_definitions(-Ddlsym=__darwin_dlsym) # Hide warnings add_definitions( @@ -43,11 +34,29 @@ SET(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/darling SET(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) -add_library(system SHARED ${libsystem_sources}) -target_link_libraries(system PRIVATE dyld) -target_link_libraries(system PUBLIC system_malloc system_c system_kernel keymgr - system_m system_info system_notify system_quarantine libdispatch_shared objc launch - removefile system_copyfile unwind system_coreservices CommonCrypto) +set(DYLIB_INSTALL_NAME "/usr/lib/libSystem.B.dylib") +add_darling_library(system SHARED ${libsystem_sources}) +target_link_libraries(system PRIVATE system_malloc system_c system_kernel keymgr + system_m system_info system_notify system_quarantine libdispatch_shared launch compiler_rt + removefile system_copyfile unwind system_coreservices system_dyld) # objc CommonCrypto +set_target_properties(system PROPERTIES OUTPUT_NAME "System.B") +set_property(TARGET system APPEND_STRING PROPERTY LINK_FLAGS + " -sub_library libsystem_malloc \ + -sub_library libsystem_c \ + -sub_library libsystem_kernel \ + -sub_library libkeymgr \ + -sub_library libsystem_m \ + -sub_library libsystem_info \ + -sub_library libsystem_notify \ + -sub_library libsystem_quarantine \ + -sub_library libdispatch \ + -sub_library liblaunch \ + -sub_library libremovefile \ + -sub_library libcopyfile \ + -sub_library libunwind \ + -sub_library libdyld \ + -sub_library libcompiler_rt \ + -sub_library libsystem_coreservices") install(TARGETS system DESTINATION ${CMAKE_INSTALL_LIBDIR}/darling) diff --git a/src/libsystem/init.c b/src/libsystem/init.c index 2fec89af8..329a8e923 100644 --- a/src/libsystem/init.c +++ b/src/libsystem/init.c @@ -113,7 +113,7 @@ void libSystem_initializer(int argc, const char* argv[], const char* envp[] /*, struct ProgramVars vars; /* Early initialization - original Apple code assumes pthread_init() doesn't print errors */ - __darling_get_args(&x_argc, &x_argv, &x_envp, &vars); + /*__darling_get_args(&x_argc, &x_argv, &x_envp, &vars); if (!*x_argc) { @@ -138,7 +138,7 @@ void libSystem_initializer(int argc, const char* argv[], const char* envp[] /*, __darling_set_environ(envp); // Initialize glibc's environ } - __darling_set_libc_vars(x_argc, x_argv, x_envp); + __darling_set_libc_vars(x_argc, x_argv, x_envp);*/ /* cerror() calls require working pthread_self() */ char dummy_self[4096]; @@ -165,15 +165,9 @@ void libSystem_initializer(int argc, const char* argv[], const char* envp[] /*, * to zero by any library function." */ errno = 0; - __objc_initialize(); + // __objc_initialize(); // TODO: temporarily commented out } -void (*const init_array []) (void) - __attribute__ ((section (".init_array"), aligned (sizeof (void *)))) = - { - &libSystem_initializer - }; - /* * libSystem_atfork_{prepare,parent,child}() are called by libc when we fork, then we deal with running fork handlers * for everyone else. diff --git a/src/libsystem_coreservices/CMakeLists.txt b/src/libsystem_coreservices/CMakeLists.txt index 6d5087a2b..09469180f 100644 --- a/src/libsystem_coreservices/CMakeLists.txt +++ b/src/libsystem_coreservices/CMakeLists.txt @@ -15,13 +15,10 @@ add_definitions(-D__APPLE__ -D__DYNAMIC__) add_definitions(-D__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__=1080) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -nostdinc -D__DARWIN_UNIX03 -fPIC -w") -set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -nostdlib -Wl,--unresolved-symbols=ignore-all -Wl,--version-script=${DARLING_TOP_DIRECTORY}/darwin.map") +set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -nostdlib") -SET(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/darling") -SET(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) -SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) - -add_library(system_coreservices SHARED dirhelper.c NSSystemDirectories.c) -target_link_libraries(system_coreservices PRIVATE system_c) +set(DYLIB_INSTALL_NAME "/usr/lib/system/libsystem_coreservices.dylib") +add_darling_library(system_coreservices SHARED dirhelper.c NSSystemDirectories.c) +target_link_libraries(system_coreservices PRIVATE system_c system_dyld) install(TARGETS system_coreservices DESTINATION ${CMAKE_INSTALL_LIBDIR}/darling) diff --git a/src/libunwind/CMakeLists.txt b/src/libunwind/CMakeLists.txt index 5a85afb8b..a6fa45f03 100644 --- a/src/libunwind/CMakeLists.txt +++ b/src/libunwind/CMakeLists.txt @@ -10,7 +10,7 @@ set(CMAKE_CXX_IMPLICIT_LINK_DIRECTORIES "") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -nostdinc -nostdinc++ -ggdb -O0") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -msse -msse2 -msse3 -w -nostdinc -funwind-tables -ggdb -O0") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D__DARWIN_UNIX03 -fPIC -w") -set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -nostdlib -Wl,--version-script=${DARLING_TOP_DIRECTORY}/darwin.map") +set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -nostdlib -Wl,-flat_namespace -Wl,-undefined,suppress") include_directories(${CMAKE_CURRENT_SOURCE_DIR}/) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../external/libcxx/include) @@ -21,8 +21,6 @@ include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../libc/stdtime/FreeBSD) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../libc/pthreads) include_directories(${DARLING_TOP_DIRECTORY}/platform-include) -add_definitions(-Ddladdr=__darwin_dladdr) - set(libunwind_sources src/libuwind.cxx src/Registers.S @@ -32,12 +30,9 @@ set(libunwind_sources src/Unwind-sjlj.c ) -SET(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/darling") -SET(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) -SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) - -add_library(unwind SHARED ${libunwind_sources}) -target_link_libraries(unwind system_c) +set(DYLIB_INSTALL_NAME "/usr/lib/system/libunwind.dylib") +add_darling_library(unwind SHARED ${libunwind_sources}) +target_link_libraries(unwind PRIVATE system_c keymgr) set_target_properties(unwind PROPERTIES LINKER_LANGUAGE C) install(TARGETS unwind DESTINATION ${CMAKE_INSTALL_LIBDIR}/darling) diff --git a/src/libunwind/src/Registers.S b/src/libunwind/src/Registers.S index 664bfd4fa..80b5592b1 100644 --- a/src/libunwind/src/Registers.S +++ b/src/libunwind/src/Registers.S @@ -1,17 +1,9 @@ -#ifdef DARLING -# define __ZN9libunwind13Registers_x866jumptoEv _ZN9libunwind13Registers_x866jumptoEv -# define __ZN9libunwind16Registers_x86_646jumptoEv _ZN9libunwind16Registers_x86_646jumptoEv -#endif #if __i386__ .text .globl __ZN9libunwind13Registers_x866jumptoEv -#ifdef DARLING - .hidden __ZN9libunwind13Registers_x866jumptoEv -#else .private_extern __ZN9libunwind13Registers_x866jumptoEv - #endif __ZN9libunwind13Registers_x866jumptoEv: # # void libunwind::Registers_x86::jumpto() @@ -56,11 +48,7 @@ __ZN9libunwind13Registers_x866jumptoEv: .text .globl __ZN9libunwind16Registers_x86_646jumptoEv -#ifdef DARLING - .hidden __ZN9libunwind16Registers_x86_646jumptoEv -#else .private_extern __ZN9libunwind16Registers_x86_646jumptoEv -#endif __ZN9libunwind16Registers_x86_646jumptoEv: # # void libunwind::Registers_x86_64::jumpto() diff --git a/src/libunwind/src/unw_getcontext.S b/src/libunwind/src/unw_getcontext.S index d0a913963..8d3a451fd 100644 --- a/src/libunwind/src/unw_getcontext.S +++ b/src/libunwind/src/unw_getcontext.S @@ -1,8 +1,4 @@ -#ifdef DARLING -# define _unw_getcontext unw_getcontext -#endif - #if __i386__ || __x86_64__ || __ppc__ .text diff --git a/src/libutil/CMakeLists.txt b/src/libutil/CMakeLists.txt index d6c51fe0f..8972b50d9 100644 --- a/src/libutil/CMakeLists.txt +++ b/src/libutil/CMakeLists.txt @@ -9,7 +9,7 @@ endif(COMMAND cmake_policy) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -nostdinc -D__DARWIN_UNIX03 -fPIC -w") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -nostdinc -nostdinc++ -D__DARWIN_UNIX03 -fPIC -w") -set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -nostdlib -Wl,--unresolved-symbols=ignore-all -Wl,--version-script=${DARLING_TOP_DIRECTORY}/darwin.map") +set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -nostdlib") include_directories(${CMAKE_CURRENT_SOURCE_DIR}) include_directories(${DARLING_TOP_DIRECTORY}/src/libc/include) include_directories(${DARLING_TOP_DIRECTORY}/src/external/libcxx/include) @@ -26,11 +26,8 @@ set(util_SRCS wipefs.cpp ) -SET(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/darling") -SET(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) -SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) - -add_library(util SHARED ${util_SRCS}) -target_link_libraries(util system) +set(DYLIB_INSTALL_NAME "/usr/lib/libutil1.0.dylib") +add_darling_library(util SHARED ${util_SRCS}) +target_link_libraries(util PRIVATE system cxx) install(TARGETS util DESTINATION ${CMAKE_INSTALL_LIBDIR}/darling) diff --git a/src/ncurses/CMakeLists.txt b/src/ncurses/CMakeLists.txt index e27d7a162..17b436cf4 100644 --- a/src/ncurses/CMakeLists.txt +++ b/src/ncurses/CMakeLists.txt @@ -3,7 +3,7 @@ project(ncurses-top) cmake_minimum_required(VERSION 2.4.0) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -nostdinc") -set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -nostdlib -Wl,--version-script=${DARLING_TOP_DIRECTORY}/darwin.map") +set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -nostdlib") include_directories(${DARLING_TOP_DIRECTORY}/platform-include) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include) diff --git a/src/ncurses/menu/CMakeLists.txt b/src/ncurses/menu/CMakeLists.txt index e1b2b018f..8ff49d158 100644 --- a/src/ncurses/menu/CMakeLists.txt +++ b/src/ncurses/menu/CMakeLists.txt @@ -37,11 +37,8 @@ set(menu_sources m_win.c ) -SET(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/darling") -SET(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) -SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) - -add_library(menu SHARED ${menu_sources}) -target_link_libraries(menu system) +set(DYLIB_INSTALL_NAME "/usr/lib/libmenu.5.4.dylib") +add_darling_library(menu SHARED ${menu_sources}) +target_link_libraries(menu system ncurses) install(TARGETS menu DESTINATION ${CMAKE_INSTALL_LIBDIR}/darling) diff --git a/src/ncurses/ncurses/CMakeLists.txt b/src/ncurses/ncurses/CMakeLists.txt index 029feb7fe..2dd5a2f2a 100644 --- a/src/ncurses/ncurses/CMakeLists.txt +++ b/src/ncurses/ncurses/CMakeLists.txt @@ -189,11 +189,8 @@ set(ncurses_sources base/fallback.c ) -SET(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/darling") -SET(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) -SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) - -add_library(ncurses SHARED ${ncurses_sources}) +set(DYLIB_INSTALL_NAME "/usr/lib/libncurses.5.4.dylib") +add_darling_library(ncurses SHARED ${ncurses_sources}) target_link_libraries(ncurses system) install(TARGETS ncurses DESTINATION ${CMAKE_INSTALL_LIBDIR}/darling) diff --git a/src/ncurses/panel/CMakeLists.txt b/src/ncurses/panel/CMakeLists.txt index 505218da9..ac14028d4 100644 --- a/src/ncurses/panel/CMakeLists.txt +++ b/src/ncurses/panel/CMakeLists.txt @@ -23,11 +23,8 @@ set(panel_sources p_win.c ) -SET(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/darling") -SET(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) -SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) - -add_library(panel SHARED ${panel_sources}) -target_link_libraries(panel system) +set(DYLIB_INSTALL_NAME "/usr/lib/libpanel.5.4.dylib") +add_darling_library(panel SHARED ${panel_sources}) +target_link_libraries(panel system ncurses) install(TARGETS panel DESTINATION ${CMAKE_INSTALL_LIBDIR}/darling) diff --git a/src/quarantine/CMakeLists.txt b/src/quarantine/CMakeLists.txt index 21420193a..d2eb365b9 100644 --- a/src/quarantine/CMakeLists.txt +++ b/src/quarantine/CMakeLists.txt @@ -2,25 +2,15 @@ project(libquarantine) cmake_minimum_required(VERSION 2.4.0) -if(CMAKE_SIZEOF_VOID_P EQUAL 4 OR CMAKE_INSTALL_LIBDIR STREQUAL "lib32") - set(BITS 32) - add_definitions(-DTARGET_CPU_X86=1) -else(CMAKE_SIZEOF_VOID_P EQUAL 4 OR CMAKE_INSTALL_LIBDIR STREQUAL "lib32") - set(BITS 64) - add_definitions(-DTARGET_CPU_X86_64=1) -endif(CMAKE_SIZEOF_VOID_P EQUAL 4 OR CMAKE_INSTALL_LIBDIR STREQUAL "lib32") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -nostdinc -D__DARWIN_UNIX03 -fPIC -w -ggdb") -set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -nostdlib -Wl,--unresolved-symbols=ignore-all -Wl,--version-script=${CMAKE_SOURCE_DIR}/darwin.map") - -SET(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/darling") -SET(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) -SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) +set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -nostdlib") -add_library(system_quarantine SHARED +set(DYLIB_INSTALL_NAME "/usr/lib/system/libquarantine.dylib") +add_darling_library(system_quarantine SHARED quarantine.c ) -target_link_libraries(system_quarantine system_c) +set_target_properties(system_quarantine PROPERTIES OUTPUT_NAME "quarantine") +target_link_libraries(system_quarantine PRIVATE system_c system_dyld) install(TARGETS system_quarantine DESTINATION ${CMAKE_INSTALL_LIBDIR}/darling) diff --git a/src/sandbox/private.h b/src/sandbox/private.h new file mode 100644 index 000000000..e69de29bb