]> git.saurik.com Git - apple/dyld.git/commitdiff
dyld-519.2.1.tar.gz macos-1013 macos-10131 v519.2.1
authorApple <opensource@apple.com>
Tue, 26 Sep 2017 16:36:15 +0000 (16:36 +0000)
committerApple <opensource@apple.com>
Tue, 26 Sep 2017 16:36:15 +0000 (16:36 +0000)
228 files changed:
configs/closured.xcconfig [new file with mode: 0644]
configs/dyld.xcconfig
configs/libdyld.xcconfig
configs/update_dyld_shared_cache.xcconfig
configs/update_dyld_sim_shared_cache.xcconfig [new file with mode: 0644]
doc/man/man1/closured.1 [new file with mode: 0644]
doc/man/man1/dyld.1
doc/man/man1/update_dyld_shared_cache.1
dyld.xcodeproj/project.pbxproj
dyld3/APIs.cpp [new file with mode: 0644]
dyld3/APIs.h [new file with mode: 0644]
dyld3/APIs_macOS.cpp [new file with mode: 0644]
dyld3/AllImages.cpp [new file with mode: 0644]
dyld3/AllImages.h [new file with mode: 0644]
dyld3/ClosureBuffer.cpp [new file with mode: 0644]
dyld3/ClosureBuffer.h [new file with mode: 0644]
dyld3/CodeSigningTypes.h [new file with mode: 0644]
dyld3/Diagnostics.cpp [new file with mode: 0644]
dyld3/Diagnostics.h [new file with mode: 0644]
dyld3/DyldCacheParser.cpp [new file with mode: 0644]
dyld3/DyldCacheParser.h [new file with mode: 0644]
dyld3/LaunchCache.h [new file with mode: 0644]
dyld3/LaunchCacheFormat.h [new file with mode: 0644]
dyld3/LaunchCachePrinter.cpp [new file with mode: 0644]
dyld3/LaunchCacheReader.cpp [new file with mode: 0644]
dyld3/LaunchCacheWriter.cpp [new file with mode: 0644]
dyld3/LaunchCacheWriter.h [new file with mode: 0644]
dyld3/Loading.cpp [new file with mode: 0644]
dyld3/Loading.h [new file with mode: 0644]
dyld3/Logging.cpp [new file with mode: 0644]
dyld3/Logging.h [new file with mode: 0644]
dyld3/MachOParser.cpp [new file with mode: 0644]
dyld3/MachOParser.h [new file with mode: 0644]
dyld3/PathOverrides.cpp [new file with mode: 0644]
dyld3/PathOverrides.h [new file with mode: 0644]
dyld3/SharedCacheRuntime.cpp [new file with mode: 0644]
dyld3/SharedCacheRuntime.h [new file with mode: 0644]
dyld3/Tracing.cpp [new file with mode: 0644]
dyld3/Tracing.h [new file with mode: 0644]
dyld3/closured/closured.cpp [new file with mode: 0644]
dyld3/closured/closuredProtocol.defs [new file with mode: 0644]
dyld3/closured/closured_entitlements.plist [new file with mode: 0644]
dyld3/closured/closuredtypes.h [new file with mode: 0644]
dyld3/closured/com.apple.dyld.closured.plist [new file with mode: 0644]
dyld3/closured/com.apple.dyld.closured.sb [new file with mode: 0644]
dyld3/dyld-potential-framework-overrides [new file with mode: 0644]
dyld3/libclosured-stub.cpp [new file with mode: 0644]
dyld3/libdyldEntryVector.cpp [new file with mode: 0644]
dyld3/libdyldEntryVector.h [new file with mode: 0644]
dyld3/shared-cache/AdjustDylibSegments.cpp [new file with mode: 0644]
dyld3/shared-cache/BuilderUtils.h [new file with mode: 0644]
dyld3/shared-cache/BuilderUtils.mm [new file with mode: 0644]
dyld3/shared-cache/CacheBuilder.cpp [new file with mode: 0644]
dyld3/shared-cache/CacheBuilder.h [new file with mode: 0644]
dyld3/shared-cache/DyldSharedCache.cpp [new file with mode: 0644]
dyld3/shared-cache/DyldSharedCache.h [new file with mode: 0644]
dyld3/shared-cache/FileAbstraction.hpp [new file with mode: 0644]
dyld3/shared-cache/FileUtils.cpp [new file with mode: 0644]
dyld3/shared-cache/FileUtils.h [new file with mode: 0644]
dyld3/shared-cache/ImageProxy.cpp [new file with mode: 0644]
dyld3/shared-cache/ImageProxy.h [new file with mode: 0644]
dyld3/shared-cache/MachOFileAbstraction.hpp [new file with mode: 0644]
dyld3/shared-cache/Manifest.h [new file with mode: 0644]
dyld3/shared-cache/Manifest.mm [new file with mode: 0644]
dyld3/shared-cache/ObjC1Abstraction.hpp [new file with mode: 0644]
dyld3/shared-cache/ObjC2Abstraction.hpp [new file with mode: 0644]
dyld3/shared-cache/OptimizerBranches.cpp [new file with mode: 0644]
dyld3/shared-cache/OptimizerLinkedit.cpp [new file with mode: 0644]
dyld3/shared-cache/OptimizerObjC.cpp [new file with mode: 0644]
dyld3/shared-cache/StringUtils.h [new file with mode: 0644]
dyld3/shared-cache/Trie.hpp [new file with mode: 0644]
dyld3/shared-cache/dyld_cache_format.h [new file with mode: 0644]
dyld3/shared-cache/dyld_closure_util.cpp [new file with mode: 0644]
dyld3/shared-cache/dyld_shared_cache_builder.mm [new file with mode: 0644]
dyld3/shared-cache/make_ios_dyld_cache.cpp [new file with mode: 0644]
dyld3/shared-cache/multi_dyld_shared_cache_builder.mm [new file with mode: 0644]
dyld3/shared-cache/update_dyld_shared_cache.cpp [new file with mode: 0644]
dyld3/shared-cache/update_dyld_shared_cache_entitlements.plist [new file with mode: 0644]
dyld3/shared-cache/update_dyld_sim_shared_cache.cpp [new file with mode: 0644]
include/mach-o/dyld.h
include/mach-o/dyld_gdb.h
include/mach-o/dyld_images.h
include/mach-o/dyld_priv.h
include/mach-o/dyld_process_info.h
interlinked-dylibs/AdjustForNewSegmentLocation.cpp [deleted file]
interlinked-dylibs/BindAllImages.cpp [deleted file]
interlinked-dylibs/CodeSigningTypes.h [deleted file]
interlinked-dylibs/FileCache.cpp [deleted file]
interlinked-dylibs/Logging.cpp [deleted file]
interlinked-dylibs/Logging.h [deleted file]
interlinked-dylibs/MachOProxy.cpp [deleted file]
interlinked-dylibs/MachOProxy.h [deleted file]
interlinked-dylibs/Manifest.h [deleted file]
interlinked-dylibs/Manifest.mm [deleted file]
interlinked-dylibs/MultiCacheBuilder.h [deleted file]
interlinked-dylibs/MultiCacheBuilder.mm [deleted file]
interlinked-dylibs/ObjC1Abstraction.hpp [deleted file]
interlinked-dylibs/ObjC2Abstraction.hpp [deleted file]
interlinked-dylibs/OptimizerBranches.cpp [deleted file]
interlinked-dylibs/OptimizerBranches.h [deleted file]
interlinked-dylibs/OptimizerLinkedit.cpp [deleted file]
interlinked-dylibs/OptimizerObjC.cpp [deleted file]
interlinked-dylibs/SharedCache.cpp [deleted file]
interlinked-dylibs/Trie.hpp [deleted file]
interlinked-dylibs/dyld_shared_cache_builder.mm [deleted file]
interlinked-dylibs/mega-dylib-utils.h [deleted file]
interlinked-dylibs/multi_dyld_shared_cache_builder.mm [deleted file]
interlinked-dylibs/update_dyld_shared_cache.mm [deleted file]
launch-cache/CacheFileAbstraction.hpp
launch-cache/MachOBinder.hpp [deleted file]
launch-cache/MachOFileAbstraction.hpp
launch-cache/MachOLayout.hpp [deleted file]
launch-cache/MachORebaser.hpp [deleted file]
launch-cache/dsc_extractor.cpp
launch-cache/dsc_iterator.cpp
launch-cache/dsc_iterator.h
launch-cache/dyld_cache_format.h
launch-cache/dyld_shared_cache_util.cpp
launch-cache/update_dyld_shared_cache_entitlements.plist [deleted file]
src/ImageLoader.cpp
src/ImageLoader.h
src/ImageLoaderMachO.cpp
src/ImageLoaderMachOClassic.cpp
src/ImageLoaderMachOCompressed.cpp
src/ImageLoaderMachOCompressed.h
src/ImageLoaderMegaDylib.cpp
src/ImageLoaderMegaDylib.h
src/dyld.cpp
src/dyld.exp
src/dyld.h
src/dyldAPIs.cpp
src/dyldAPIsInLibSystem.cpp
src/dyldSyscallInterface.h
src/dyld_gdb.cpp
src/dyld_process_info.cpp
src/dyld_process_info_internal.h
src/dyld_process_info_notify.cpp
src/glue.c
src/threadLocalVariables.c
testing/build_tests.py
testing/test-cases/NSAddImage-fail.dtest/main.c [new file with mode: 0644]
testing/test-cases/NSAddImage-loaded.dtest/main.c [new file with mode: 0644]
testing/test-cases/NSAddressOfSymbol-basic.dtest/main.c [new file with mode: 0644]
testing/test-cases/NSCreateObjectFileImageFromFile-basic.dtest/foo.c [new file with mode: 0644]
testing/test-cases/NSCreateObjectFileImageFromFile-basic.dtest/main.c [new file with mode: 0644]
testing/test-cases/NSCreateObjectFileImageFromMemory-basic.dtest/foo.c [new file with mode: 0644]
testing/test-cases/NSCreateObjectFileImageFromMemory-basic.dtest/main.c [new file with mode: 0644]
testing/test-cases/NSLookupSymbolInImage-basic.dtest/main.c [new file with mode: 0644]
testing/test-cases/_dyld_is_memory_immutable.dtest/bar.c [new file with mode: 0644]
testing/test-cases/_dyld_is_memory_immutable.dtest/foo.c [new file with mode: 0644]
testing/test-cases/_dyld_is_memory_immutable.dtest/main.c [new file with mode: 0644]
testing/test-cases/_dyld_register_func_for_add_image.dtest/foo.c [new file with mode: 0644]
testing/test-cases/_dyld_register_func_for_add_image.dtest/main.cxx [new file with mode: 0644]
testing/test-cases/dladdr-basic.dtest/main-no-syms.c
testing/test-cases/dladdr-basic.dtest/main.c
testing/test-cases/dladdr-dylib.dtest/foo.c [new file with mode: 0644]
testing/test-cases/dladdr-dylib.dtest/main.c [new file with mode: 0644]
testing/test-cases/dlopen-DYLD_LIBRARY_PATH.dtest/bar.c [new file with mode: 0644]
testing/test-cases/dlopen-DYLD_LIBRARY_PATH.dtest/foo.c [new file with mode: 0644]
testing/test-cases/dlopen-DYLD_LIBRARY_PATH.dtest/main.c [new file with mode: 0644]
testing/test-cases/dlopen-bad-file.dtest/bad.txt [new file with mode: 0644]
testing/test-cases/dlopen-bad-file.dtest/main.c [new file with mode: 0644]
testing/test-cases/dlopen-empty-data.dtest/foo.c [new file with mode: 0644]
testing/test-cases/dlopen-empty-data.dtest/main.c [new file with mode: 0644]
testing/test-cases/dlopen-flat.dtest/bar.c [new file with mode: 0644]
testing/test-cases/dlopen-flat.dtest/foo.c [new file with mode: 0644]
testing/test-cases/dlopen-flat.dtest/main.c [new file with mode: 0644]
testing/test-cases/dlopen-indirect-groupNum.dtest/bar.c [new file with mode: 0644]
testing/test-cases/dlopen-indirect-groupNum.dtest/baz.c [new file with mode: 0644]
testing/test-cases/dlopen-indirect-groupNum.dtest/foo.c [new file with mode: 0644]
testing/test-cases/dlopen-indirect-groupNum.dtest/main.c [new file with mode: 0644]
testing/test-cases/dlopen-intertwined.dtest/A.c [new file with mode: 0644]
testing/test-cases/dlopen-intertwined.dtest/B.c [new file with mode: 0644]
testing/test-cases/dlopen-intertwined.dtest/C.c [new file with mode: 0644]
testing/test-cases/dlopen-intertwined.dtest/D.c [new file with mode: 0644]
testing/test-cases/dlopen-intertwined.dtest/E.c [new file with mode: 0644]
testing/test-cases/dlopen-intertwined.dtest/F.c [new file with mode: 0644]
testing/test-cases/dlopen-intertwined.dtest/base.c [new file with mode: 0644]
testing/test-cases/dlopen-intertwined.dtest/main.c [new file with mode: 0644]
testing/test-cases/dlopen-long-error-message.dtest/main.c [new file with mode: 0644]
testing/test-cases/dlopen-race.dtest/foo.c [new file with mode: 0644]
testing/test-cases/dlopen-race.dtest/main.c [new file with mode: 0644]
testing/test-cases/dlopen-recurse.dtest/bar.c [new file with mode: 0644]
testing/test-cases/dlopen-recurse.dtest/foo.c [new file with mode: 0644]
testing/test-cases/dlopen-recurse.dtest/main.c [new file with mode: 0644]
testing/test-cases/dlsym-RTLD_DEFAULT.dtest/foo.c [new file with mode: 0644]
testing/test-cases/dlsym-RTLD_DEFAULT.dtest/main.c [new file with mode: 0644]
testing/test-cases/dlsym-RTLD_MAIN_ONLY.dtest/foo.c [new file with mode: 0644]
testing/test-cases/dlsym-RTLD_MAIN_ONLY.dtest/main.c [new file with mode: 0644]
testing/test-cases/dlsym-RTLD_NEXT.dtest/foo.c [new file with mode: 0644]
testing/test-cases/dlsym-RTLD_NEXT.dtest/main.c [new file with mode: 0644]
testing/test-cases/dlsym-RTLD_SELF.dtest/foo.c [new file with mode: 0644]
testing/test-cases/dlsym-RTLD_SELF.dtest/main.c [new file with mode: 0644]
testing/test-cases/dlsym-handle.dtest/bar.c [new file with mode: 0644]
testing/test-cases/dlsym-handle.dtest/base.c [new file with mode: 0644]
testing/test-cases/dlsym-handle.dtest/foo.c [new file with mode: 0644]
testing/test-cases/dlsym-handle.dtest/main.c [new file with mode: 0644]
testing/test-cases/dlsym-re-export.dtest/foo.c [new file with mode: 0644]
testing/test-cases/dlsym-re-export.dtest/main.c [new file with mode: 0644]
testing/test-cases/dlsym-re-export.dtest/sub1.c [new file with mode: 0644]
testing/test-cases/dlsym-re-export.dtest/sub2.c [new file with mode: 0644]
testing/test-cases/dtrace.dtest/main.c [new file with mode: 0644]
testing/test-cases/dtrace.dtest/main.d [new file with mode: 0644]
testing/test-cases/dyld_get_sdk_version.dtest/bad.txt [new file with mode: 0644]
testing/test-cases/dyld_get_sdk_version.dtest/main.c [new file with mode: 0644]
testing/test-cases/dyld_image_path_containing_address.dtest/main.c [new file with mode: 0644]
testing/test-cases/dyld_process_info.dtest/linksWithCF.c
testing/test-cases/dyld_process_info.dtest/main.c
testing/test-cases/dyld_process_info_notify.dtest/main.c
testing/test-cases/dyld_process_info_notify.dtest/target.c
testing/test-cases/dyld_process_info_unload.dtest/main.c
testing/test-cases/env-DYLD_FALLBACK_FRAMEWORK_PATH.dtest/foo.c [new file with mode: 0644]
testing/test-cases/env-DYLD_FALLBACK_FRAMEWORK_PATH.dtest/main.c [new file with mode: 0644]
testing/test-cases/env-DYLD_FALLBACK_LIBRARY_PATH.dtest/foo.c [new file with mode: 0644]
testing/test-cases/env-DYLD_FALLBACK_LIBRARY_PATH.dtest/main.c [new file with mode: 0644]
testing/test-cases/env-DYLD_FRAMEWORK_PATH.dtest/foo.c [new file with mode: 0644]
testing/test-cases/env-DYLD_FRAMEWORK_PATH.dtest/main.c [new file with mode: 0644]
testing/test-cases/env-DYLD_LIBRARY_PATH.dtest/foo.c [new file with mode: 0644]
testing/test-cases/env-DYLD_LIBRARY_PATH.dtest/main.c [new file with mode: 0644]
testing/test-cases/flat-namespace.dtest/foo.c [new file with mode: 0644]
testing/test-cases/flat-namespace.dtest/main.c [new file with mode: 0644]
testing/test-cases/interpose-malloc.dtest/foo.c [new file with mode: 0644]
testing/test-cases/interpose-malloc.dtest/interposer.c [new file with mode: 0644]
testing/test-cases/interpose-malloc.dtest/main.c [new file with mode: 0644]
testing/test-cases/operator-new.dtest/main.cxx [new file with mode: 0644]
testing/test-cases/thread-local-cleanup.dtest/foo.c [new file with mode: 0644]
testing/test-cases/thread-local-cleanup.dtest/main.c [new file with mode: 0644]
unit-tests/test-cases/dyld_shared_cache_iterate_text/main.c

diff --git a/configs/closured.xcconfig b/configs/closured.xcconfig
new file mode 100644 (file)
index 0000000..215e217
--- /dev/null
@@ -0,0 +1,2 @@
+
+CODE_SIGN_ENTITLEMENTS[sdk=embedded*] = dyld3/closured/closured_entitlements.plist
index 13863b843fe15409a462d8251409d601ae072b51..d1ec099584ca0c9b721f682cbf02fd66e65e9518 100644 (file)
@@ -13,3 +13,9 @@ PRODUCT_NAME[sdk=iphoneos*]   = dyld
 PRODUCT_NAME[sdk=macosx*]     = dyld
 
 INSTALL_PATH   = /usr/lib
 PRODUCT_NAME[sdk=macosx*]     = dyld
 
 INSTALL_PATH   = /usr/lib
+
+//:configuration = Debug
+GCC_PREPROCESSOR_DEFINITIONS = DYLD_IN_PROCESS=1 DYLD_VERSION=$(RC_ProjectSourceVersion) BUILDING_DYLD=1 DEBUG=1
+
+//:configuration = Release
+GCC_PREPROCESSOR_DEFINITIONS = DYLD_IN_PROCESS=1 DYLD_VERSION=$(RC_ProjectSourceVersion) BUILDING_DYLD=1
index d3c6d1cc70bcf3237a2462064d206d2bc9d2d0a6..8f06e5ae65b931a68b0cdf579b31b818a80c1e96 100644 (file)
@@ -1,8 +1,14 @@
 
 
-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
+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 -Wl,-upward-lcommonCrypto  -Wl,-upward-lclosured
+LIBSYSTEM_LIBS[sdk=embedded*]       = -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 -Wl,-upward-lcommonCrypto  -Wl,-upward-lclosured  -Wl,-upward-lcompiler_rt
+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 -Wl,-upward-lcommonCrypto  -Wl,-upward-lclosured
 
 INSTALL_PATH = /usr/lib/system
 
 
 
 INSTALL_PATH = /usr/lib/system
 
 
+//:configuration = Debug
+GCC_PREPROCESSOR_DEFINITIONS = DYLD_IN_PROCESS=1 BUILDING_LIBDYLD=1 DEBUG=1
+
+//:configuration = Release
+GCC_PREPROCESSOR_DEFINITIONS = DYLD_IN_PROCESS=1 BUILDING_LIBDYLD=1
+
index 1bed8641fe0fabde472dee98ca8cf33c61299ce7..e45c7141c92595ec0e10a066417f661e54d43ddb 100644 (file)
@@ -1,18 +1,4 @@
 
 
-//: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
+CODE_SIGN_ENTITLEMENTS = dyld3/shared-cache/update_dyld_shared_cache_entitlements.plist
 
 
diff --git a/configs/update_dyld_sim_shared_cache.xcconfig b/configs/update_dyld_sim_shared_cache.xcconfig
new file mode 100644 (file)
index 0000000..e4f7bfa
--- /dev/null
@@ -0,0 +1,3 @@
+
+#include "<DEVELOPER_DIR>/AppleInternal/XcodeConfig/PlatformSupportHost.xcconfig"
+
diff --git a/doc/man/man1/closured.1 b/doc/man/man1/closured.1
new file mode 100644 (file)
index 0000000..a13b005
--- /dev/null
@@ -0,0 +1,10 @@
+.Dd 3/1/17
+.Dt closured 1
+.Os Darwin
+.Sh NAME
+.Nm closured
+.Nd Daemon for building dyld closures.
+.Sh SYNOPSIS
+.Nm closured is a launchd managed daemon.
+.Sh DESCRIPTION
+.Nm closured is a launchd managed daemon.
index ad1cec34a641cba182bab98acb7a4598c26ceb99..3c58b801af4c53a448caeb4c15d50106c8a969cb 100644 (file)
@@ -1,6 +1,6 @@
-.TH DYLD 1 "December 14, 2009" "Apple Inc."
+.TH DYLD 1 "June 1, 2017" "Apple Inc."
 .SH NAME
 .SH NAME
-dyld \- the dynamic link editor
+dyld \- the dynamic linker
 .SH SYNOPSIS
 DYLD_FRAMEWORK_PATH
 .br
 .SH SYNOPSIS
 DYLD_FRAMEWORK_PATH
 .br
@@ -30,8 +30,6 @@ DYLD_PRINT_ENV
 .br
 DYLD_PRINT_LIBRARIES
 .br
 .br
 DYLD_PRINT_LIBRARIES
 .br
-DYLD_PRINT_LIBRARIES_POST_LAUNCH
-.br
 DYLD_BIND_AT_LAUNCH
 .br
 DYLD_DISABLE_DOFS
 DYLD_BIND_AT_LAUNCH
 .br
 DYLD_DISABLE_DOFS
@@ -56,8 +54,12 @@ DYLD_SHARED_CACHE_DIR
 .br
 DYLD_SHARED_CACHE_DONT_VALIDATE
 .SH DESCRIPTION
 .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.
+The dynamic linker checks the following environment variables during the launch
+of each process.
+.br
+.br
+Note: If System Integrity Protection is enabled, these environment variables are ignored
+when executing binaries protected by System Integrity Protection.
 .TP
 .B DYLD_FRAMEWORK_PATH
 This is a colon separated list of directories that contain frameworks.
 .TP
 .B DYLD_FRAMEWORK_PATH
 This is a colon separated list of directories that contain frameworks.
@@ -197,11 +199,6 @@ This is useful to make sure that the use of
 .SM DYLD_LIBRARY_PATH
 is getting what you want.
 .TP
 .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 
 .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 
@@ -300,4 +297,4 @@ with -rpath @loader_path/zzz, where zzz is the path from the executable to the a
 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"
 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)
+dyldinfo(1), ld(1), otool(1)
index 7161fc5c02251e818758b4c571d256d2cfef010b..f1c23307231ab211a9d747931ffbc901a9b4bd5a 100644 (file)
@@ -1,4 +1,4 @@
-.Dd June 23, 2016
+.Dd June 1, 2017
 .Dt update_dyld_shared_cache 1
 .Os Darwin
 .Sh NAME
 .Dt update_dyld_shared_cache 1
 .Os Darwin
 .Sh NAME
@@ -12,7 +12,6 @@
 .Op Fl force 
 .Op Fl debug
 .Op Fl universal_boot
 .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
 .Sh DESCRIPTION
 .Nm update_dyld_shared_cache
 ensures that dyld's shared cache is up-to-date.  This tool is normally
@@ -23,9 +22,6 @@ used another mechanism to alter an OS dylib, you should manually run
 .Pp
 Note that the new cache does not take effect until the OS is rebooted.  
 .Pp
 .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
 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
@@ -34,11 +30,8 @@ the original file.  This results in significant performance improvements to
 launch time.
 .Pp
 .Nm update_dyld_shared_cache
 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.
+scans the directory /System/Library/Receipts/ for .bom files which list all files
+installed.  From that info it creates the set of OS dylibs to build into the dyld cache.
 .Pp
 .Nm update_dyld_shared_cache
 builds a separate cache file for each architecture.  The cache files and a readable text
 .Pp
 .Nm update_dyld_shared_cache
 builds a separate cache file for each architecture.  The cache files and a readable text
@@ -77,14 +70,6 @@ to regenerated the shared cache files even if they appear to be already up-to-da
 This option prints out additional information about the work being done.
 .It Fl universal_boot
 This option builds caches for all machines.
 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
 .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
 .Sh SEE ALSO
 .Xr dyld 1
index 49d05fff96724bcd7a1418870b4953b3520abac8..121b7fac78bda25bd7cb4fdb956ed11423edd61b 100644 (file)
                        isa = PBXAggregateTarget;
                        buildConfigurationList = 37A0AD0B1C15FFF500731E50 /* Build configuration list for PBXAggregateTarget "update_dyld_shared_cache" */;
                        buildPhases = (
                        isa = PBXAggregateTarget;
                        buildConfigurationList = 37A0AD0B1C15FFF500731E50 /* Build configuration list for PBXAggregateTarget "update_dyld_shared_cache" */;
                        buildPhases = (
+                               F94182D61E60E74E00D8EF25 /* pre-platform builds */,
                        );
                        dependencies = (
                        );
                        dependencies = (
-                               37A0AD1B1C16004600731E50 /* PBXTargetDependency */,
-                               37A0AD131C16003600731E50 /* PBXTargetDependency */,
-                               37A0AD151C16003600731E50 /* PBXTargetDependency */,
-                               37A0AD171C16003600731E50 /* PBXTargetDependency */,
-                               37A0AD191C16003600731E50 /* PBXTargetDependency */,
-                               37A0AD111C16003600731E50 /* PBXTargetDependency */,
+                               D8668AD01ECE335F005E7D31 /* PBXTargetDependency */,
+                               F94182D81E60F0BE00D8EF25 /* PBXTargetDependency */,
+                               F94182DA1E60F0C000D8EF25 /* PBXTargetDependency */,
+                               F94182DC1E60F16900D8EF25 /* PBXTargetDependency */,
                        );
                        name = update_dyld_shared_cache;
                        productName = update_dyld_shared_cache;
                        );
                        name = update_dyld_shared_cache;
                        productName = update_dyld_shared_cache;
                        isa = PBXAggregateTarget;
                        buildConfigurationList = F908135211D3ED9000626CC1 /* Build configuration list for PBXAggregateTarget "libdyld" */;
                        buildPhases = (
                        isa = PBXAggregateTarget;
                        buildConfigurationList = F908135211D3ED9000626CC1 /* Build configuration list for PBXAggregateTarget "libdyld" */;
                        buildPhases = (
-                               F908135111D3ED9000626CC1 /* usr|include|mach-o */,
-                               F908137011D3FB5000626CC1 /* usr|include */,
                                F9C69EFC14EC8AB8009CAE2E /* usr|local|include */,
                                F9C69EFC14EC8AB8009CAE2E /* usr|local|include */,
-                               F908137111D3FB5000626CC1 /* usr|local|include|mach-o */,
                                F908137211D3FB5000626CC1 /* usr|share|man|man1 */,
                                F908137311D3FB5000626CC1 /* usr|share|man|man3 */,
                                F908137211D3FB5000626CC1 /* usr|share|man|man1 */,
                                F908137311D3FB5000626CC1 /* usr|share|man|man3 */,
-                               F96D19701D7F62C3007AF3CE /* Install dyld_priv.h */,
                        );
                        dependencies = (
                                F9B4D78012AD9736000605A6 /* PBXTargetDependency */,
                        );
                        dependencies = (
                                F9B4D78012AD9736000605A6 /* PBXTargetDependency */,
 /* End PBXAggregateTarget section */
 
 /* Begin PBXBuildFile section */
 /* 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 */; };
+               37554F3B1E3F0FD200407388 /* Manifest.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37908A281E3A853E009613FA /* Manifest.mm */; };
+               37554F3C1E3F0FD200407388 /* DyldSharedCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692141DC3EF6C00CBEDE6 /* DyldSharedCache.cpp */; };
+               37554F3D1E3F0FD200407388 /* FileUtils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986920D1DC3EF6C00CBEDE6 /* FileUtils.cpp */; };
+               37554F3E1E3F0FD200407388 /* multi_dyld_shared_cache_builder.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37908A291E3A853E009613FA /* multi_dyld_shared_cache_builder.mm */; };
+               37554F3F1E3F165100407388 /* Diagnostics.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986921B1DC3F07C00CBEDE6 /* Diagnostics.cpp */; };
+               37554F401E3F167A00407388 /* MachOParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F96D19BE1D94A6DC007AF3CE /* MachOParser.cpp */; };
+               37554F411E3F169500407388 /* CacheBuilder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986921C1DC3F86C00CBEDE6 /* CacheBuilder.cpp */; };
+               37554F421E3F169600407388 /* CacheBuilder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986921C1DC3F86C00CBEDE6 /* CacheBuilder.cpp */; };
+               37554F431E3F16A800407388 /* OptimizerObjC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692131DC3EF6C00CBEDE6 /* OptimizerObjC.cpp */; };
+               37554F441E3F16A900407388 /* OptimizerObjC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692131DC3EF6C00CBEDE6 /* OptimizerObjC.cpp */; };
+               37554F451E3F16B500407388 /* OptimizerLinkedit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692121DC3EF6C00CBEDE6 /* OptimizerLinkedit.cpp */; };
+               37554F461E3F16B600407388 /* OptimizerLinkedit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692121DC3EF6C00CBEDE6 /* OptimizerLinkedit.cpp */; };
+               37554F471E3F16B900407388 /* OptimizerBranches.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692111DC3EF6C00CBEDE6 /* OptimizerBranches.cpp */; };
+               37554F481E3F16BA00407388 /* OptimizerBranches.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692111DC3EF6C00CBEDE6 /* OptimizerBranches.cpp */; };
+               37554F491E3F76E400407388 /* DyldSharedCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692141DC3EF6C00CBEDE6 /* DyldSharedCache.cpp */; };
+               37554F4A1E3F76E800407388 /* AdjustDylibSegments.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692091DC3EF6C00CBEDE6 /* AdjustDylibSegments.cpp */; };
+               37554F4B1E3F76E900407388 /* AdjustDylibSegments.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692091DC3EF6C00CBEDE6 /* AdjustDylibSegments.cpp */; };
+               37554F511E3F78EB00407388 /* ImageProxy.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F963546B1DD8F2A800895049 /* ImageProxy.cpp */; };
+               37554F521E3F78EB00407388 /* ImageProxy.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F963546B1DD8F2A800895049 /* ImageProxy.cpp */; };
+               37554F531E3F7B1E00407388 /* LaunchCacheReader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692061DC3EF4800CBEDE6 /* LaunchCacheReader.cpp */; };
+               37554F541E3F7B1F00407388 /* LaunchCacheReader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692061DC3EF4800CBEDE6 /* LaunchCacheReader.cpp */; };
+               37554F551E3F7B4200407388 /* LaunchCacheWriter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692071DC3EF4800CBEDE6 /* LaunchCacheWriter.cpp */; };
+               37554F561E3F7B4300407388 /* LaunchCacheWriter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692071DC3EF4800CBEDE6 /* LaunchCacheWriter.cpp */; };
+               37554F571E3F7B6400407388 /* PathOverrides.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9F76FAE1E08CFF200828678 /* PathOverrides.cpp */; };
+               37554F581E3F7B6500407388 /* PathOverrides.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9F76FAE1E08CFF200828678 /* PathOverrides.cpp */; };
                376ABDB61C592CC0009F0011 /* Metabom.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 376ED1D71C46F2710051DD54 /* Metabom.framework */; };
                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 */; };
                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 */; };
                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 */; };
+               37908A2E1E3A8632009613FA /* Manifest.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37908A281E3A853E009613FA /* Manifest.mm */; };
+               37908A2F1E3A864E009613FA /* dyld_shared_cache_builder.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37908A271E3A853E009613FA /* dyld_shared_cache_builder.mm */; };
+               37908A301E3ADD15009613FA /* Diagnostics.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986921B1DC3F07C00CBEDE6 /* Diagnostics.cpp */; };
+               37908A311E3EB585009613FA /* MachOParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F96D19BE1D94A6DC007AF3CE /* MachOParser.cpp */; };
+               37908A321E3ED667009613FA /* FileUtils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986920D1DC3EF6C00CBEDE6 /* FileUtils.cpp */; };
+               37C5C2FD1E5CD154006B32C9 /* BuilderUtils.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37C5C2FB1E5CD154006B32C9 /* BuilderUtils.mm */; };
+               37C5C2FE1E5CD154006B32C9 /* BuilderUtils.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37C5C2FB1E5CD154006B32C9 /* BuilderUtils.mm */; };
+               37C5C2FF1E60D7DE006B32C9 /* OptimizerBranches.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692111DC3EF6C00CBEDE6 /* OptimizerBranches.cpp */; };
+               37D7DB001E96F0ED00D52CEA /* Tracing.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 37D7DAFE1E96F0ED00D52CEA /* Tracing.cpp */; };
+               37D7DB011E96F3EB00D52CEA /* Tracing.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 37D7DAFE1E96F0ED00D52CEA /* Tracing.cpp */; };
+               F90108611E2AD96000870568 /* PathOverrides.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9F76FAE1E08CFF200828678 /* PathOverrides.cpp */; };
                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 */; };
                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 */; };
                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 */; };
                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 */; };
+               F90F7C081E9C6B8B00535722 /* libCrashReporterClient.a in Frameworks */ = {isa = PBXBuildFile; fileRef = F90F7C071E9C6B8B00535722 /* libCrashReporterClient.a */; };
                F913FADA0630A8AE00B7AE9D /* dyldAPIsInLibSystem.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F913FAD90630A8AE00B7AE9D /* dyldAPIsInLibSystem.cpp */; };
                F913FADA0630A8AE00B7AE9D /* dyldAPIsInLibSystem.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F913FAD90630A8AE00B7AE9D /* dyldAPIsInLibSystem.cpp */; };
+               F92015701DDFEBAF00816A4A /* Bom.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 37F7A5961BB363820039043A /* Bom.framework */; };
+               F92015711DE3F3B000816A4A /* DyldSharedCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692141DC3EF6C00CBEDE6 /* DyldSharedCache.cpp */; };
+               F922AE581EF0D3C300926F9D /* DyldCacheParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9B3CAEB1EEB5CFA00C9A48B /* DyldCacheParser.cpp */; };
+               F922AE591EF0DBA500926F9D /* DyldCacheParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9B3CAEB1EEB5CFA00C9A48B /* DyldCacheParser.cpp */; };
+               F922AE5A1EF0DC7200926F9D /* DyldCacheParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9B3CAEB1EEB5CFA00C9A48B /* DyldCacheParser.cpp */; };
+               F922C81C1F33B88400D8F479 /* libclosured-stub.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F922C81B1F33B86300D8F479 /* libclosured-stub.cpp */; };
+               F926C0471DDBFB7A00941CB1 /* ImageProxy.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F963546B1DD8F2A800895049 /* ImageProxy.cpp */; };
                F9280B7B1AB9DCA000B18AEC /* ImageLoaderMegaDylib.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9280B791AB9DCA000B18AEC /* ImageLoaderMegaDylib.cpp */; };
                F9280B7B1AB9DCA000B18AEC /* ImageLoaderMegaDylib.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9280B791AB9DCA000B18AEC /* ImageLoaderMegaDylib.cpp */; };
+               F94182D51E60A2F100D8EF25 /* OptimizerBranches.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692111DC3EF6C00CBEDE6 /* OptimizerBranches.cpp */; };
+               F9460DCD1E09FFFC00FEC613 /* PathOverrides.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9F76FAE1E08CFF200828678 /* PathOverrides.cpp */; };
+               F9460DCE1E0A000600FEC613 /* PathOverrides.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9F76FAE1E08CFF200828678 /* PathOverrides.cpp */; };
+               F94942B31E6796D70019AE08 /* closured.1 in install man page */ = {isa = PBXBuildFile; fileRef = F94942B21E6796D40019AE08 /* closured.1 */; };
+               F94C22251E513CA90079E5DD /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F94C22241E513CA90079E5DD /* CoreFoundation.framework */; };
                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 */; };
                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, ); }; };
+               F960A78A1E40569400840176 /* dyld-interposing.h in Headers */ = {isa = PBXBuildFile; fileRef = F918691408B16D2500E0F9DB /* dyld-interposing.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               F960A78B1E405DE300840176 /* dyld_cache_format.h in Headers */ = {isa = PBXBuildFile; fileRef = F986921E1DC3F86C00CBEDE6 /* dyld_cache_format.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               F96354331DCD74A400895049 /* DyldSharedCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692141DC3EF6C00CBEDE6 /* DyldSharedCache.cpp */; };
+               F96354341DCD74A400895049 /* CacheBuilder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986921C1DC3F86C00CBEDE6 /* CacheBuilder.cpp */; };
+               F96354351DCD74A400895049 /* AdjustDylibSegments.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692091DC3EF6C00CBEDE6 /* AdjustDylibSegments.cpp */; };
+               F96354361DCD74A400895049 /* FileUtils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986920D1DC3EF6C00CBEDE6 /* FileUtils.cpp */; };
+               F96354371DCD74A400895049 /* Diagnostics.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986921B1DC3F07C00CBEDE6 /* Diagnostics.cpp */; };
+               F96354381DCD74A400895049 /* MachOParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F96D19BE1D94A6DC007AF3CE /* MachOParser.cpp */; };
+               F96354391DCD74A400895049 /* OptimizerObjC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692131DC3EF6C00CBEDE6 /* OptimizerObjC.cpp */; };
+               F963543A1DCD74A400895049 /* LaunchCacheWriter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692071DC3EF4800CBEDE6 /* LaunchCacheWriter.cpp */; };
+               F963543B1DCD74A400895049 /* LaunchCacheReader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692061DC3EF4800CBEDE6 /* LaunchCacheReader.cpp */; };
+               F963543C1DCD74A400895049 /* OptimizerLinkedit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692121DC3EF6C00CBEDE6 /* OptimizerLinkedit.cpp */; };
+               F96354461DCD74BC00895049 /* update_dyld_sim_shared_cache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F963542E1DCD736000895049 /* update_dyld_sim_shared_cache.cpp */; };
+               F963546C1DD8F38300895049 /* ImageProxy.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F963546B1DD8F2A800895049 /* ImageProxy.cpp */; };
+               F96D19A81D93661A007AF3CE /* APIs.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F96D19A51D9363D6007AF3CE /* APIs.cpp */; };
+               F96D19BF1D94A6DC007AF3CE /* MachOParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F96D19BE1D94A6DC007AF3CE /* MachOParser.cpp */; };
+               F96D19C01D94BFCE007AF3CE /* AllImages.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F96D19A61D9363D6007AF3CE /* AllImages.cpp */; };
+               F977DDCB1E53BF5500609230 /* SharedCacheRuntime.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F977DDC91E53BEA700609230 /* SharedCacheRuntime.cpp */; };
+               F97C619F1D9829AA00A84CD7 /* libdyldEntryVector.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F97C619E1D98292700A84CD7 /* libdyldEntryVector.cpp */; };
+               F97C61A21D9CAE3500A84CD7 /* Logging.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F97C61A01D9CA6B800A84CD7 /* Logging.cpp */; };
+               F97C61B31DBAE14200A84CD7 /* MachOParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F96D19BE1D94A6DC007AF3CE /* MachOParser.cpp */; };
                F97FF3611C23640C000ACDD2 /* nocr.c in Sources */ = {isa = PBXBuildFile; fileRef = F97FF35F1C236402000ACDD2 /* nocr.c */; };
                F97FF3641C237F68000ACDD2 /* nocr.1 in install man page */ = {isa = PBXBuildFile; fileRef = F97FF3631C237F5C000ACDD2 /* nocr.1 */; };
                F97FF3611C23640C000ACDD2 /* nocr.c in Sources */ = {isa = PBXBuildFile; fileRef = F97FF35F1C236402000ACDD2 /* nocr.c */; };
                F97FF3641C237F68000ACDD2 /* nocr.1 in install man page */ = {isa = PBXBuildFile; fileRef = F97FF3631C237F5C000ACDD2 /* nocr.1 */; };
+               F981C8BD1EEF447500452F35 /* DyldCacheParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9B3CAEB1EEB5CFA00C9A48B /* DyldCacheParser.cpp */; };
+               F981C8BE1EEF733800452F35 /* ClosureBuffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9E5E2C41EB00A870013A0BB /* ClosureBuffer.cpp */; };
+               F981C8BF1EEF733C00452F35 /* DyldCacheParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9B3CAEB1EEB5CFA00C9A48B /* DyldCacheParser.cpp */; };
+               F981C8C01EEF7D4100452F35 /* DyldCacheParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9B3CAEB1EEB5CFA00C9A48B /* DyldCacheParser.cpp */; };
+               F981C8C11EF06A7800452F35 /* DyldCacheParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9B3CAEB1EEB5CFA00C9A48B /* DyldCacheParser.cpp */; };
+               F98692171DC3EFD500CBEDE6 /* update_dyld_shared_cache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692151DC3EF6C00CBEDE6 /* update_dyld_shared_cache.cpp */; };
+               F98692181DC3EFD700CBEDE6 /* DyldSharedCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692141DC3EF6C00CBEDE6 /* DyldSharedCache.cpp */; };
+               F98692191DC3EFDA00CBEDE6 /* FileUtils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986920D1DC3EF6C00CBEDE6 /* FileUtils.cpp */; };
+               F986921F1DC3F98700CBEDE6 /* CacheBuilder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986921C1DC3F86C00CBEDE6 /* CacheBuilder.cpp */; };
+               F98692201DC3F99300CBEDE6 /* Diagnostics.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986921B1DC3F07C00CBEDE6 /* Diagnostics.cpp */; };
+               F98692211DC401B900CBEDE6 /* MachOParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F96D19BE1D94A6DC007AF3CE /* MachOParser.cpp */; };
+               F98692231DC403F900CBEDE6 /* AdjustDylibSegments.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692091DC3EF6C00CBEDE6 /* AdjustDylibSegments.cpp */; };
+               F988F0BB1E2FDF5B003AED79 /* execserver.defs in Sources */ = {isa = PBXBuildFile; fileRef = F988F0BA1E2FDF5B003AED79 /* execserver.defs */; settings = {ATTRIBUTES = (Server, ); }; };
                F98C78F00F7C02E8006257D2 /* dsc_iterator.h in usr|local|include|mach-o */ = {isa = PBXBuildFile; fileRef = F9F2A56F0F7AEEE300B7C9EB /* dsc_iterator.h */; };
                F98C78F00F7C02E8006257D2 /* dsc_iterator.h in usr|local|include|mach-o */ = {isa = PBXBuildFile; fileRef = F9F2A56F0F7AEEE300B7C9EB /* dsc_iterator.h */; };
+               F98F1FBD1E4029E400EF868D /* dyld_priv.h in Headers */ = {isa = PBXBuildFile; fileRef = F9ED4CE90630A80600DF4E74 /* dyld_priv.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               F98F1FBF1E4031F800EF868D /* dyld_process_info.h in Headers */ = {isa = PBXBuildFile; fileRef = F95090D01C5AB89A0031F81D /* dyld_process_info.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               F99006DD1E411BA70013456D /* dyld_images.h in Headers */ = {isa = PBXBuildFile; fileRef = F98D274C0AA79D7400416316 /* dyld_images.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               F99006DE1E411BBC0013456D /* dyld.h in Headers */ = {isa = PBXBuildFile; fileRef = F9ED4CEA0630A80600DF4E74 /* dyld.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               F99006E01E4130AE0013456D /* dyld_gdb.h in Headers */ = {isa = PBXBuildFile; fileRef = F9ED4CE80630A80600DF4E74 /* dyld_gdb.h */; settings = {ATTRIBUTES = (Private, ); }; };
                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 */; };
                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 */; };
+               F9A548B31DDBBC75002B4422 /* ImageProxy.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F963546B1DD8F2A800895049 /* ImageProxy.cpp */; };
                F9A6D6E4116F9DF20051CC16 /* threadLocalVariables.c in Sources */ = {isa = PBXBuildFile; fileRef = F9A6D6E2116F9DF20051CC16 /* threadLocalVariables.c */; };
                F9A6D70C116FBBD10051CC16 /* threadLocalHelpers.s in Sources */ = {isa = PBXBuildFile; fileRef = F9A6D70B116FBBD10051CC16 /* threadLocalHelpers.s */; };
                F9A6D6E4116F9DF20051CC16 /* threadLocalVariables.c in Sources */ = {isa = PBXBuildFile; fileRef = F9A6D6E2116F9DF20051CC16 /* threadLocalVariables.c */; };
                F9A6D70C116FBBD10051CC16 /* threadLocalHelpers.s in Sources */ = {isa = PBXBuildFile; fileRef = F9A6D70B116FBBD10051CC16 /* threadLocalHelpers.s */; };
+               F9AB02C31F329FE000EE96C4 /* ImageProxy.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F963546B1DD8F2A800895049 /* ImageProxy.cpp */; };
+               F9AB02C41F329FF400EE96C4 /* DyldSharedCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692141DC3EF6C00CBEDE6 /* DyldSharedCache.cpp */; };
+               F9AB02C51F329FFE00EE96C4 /* LaunchCacheWriter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692071DC3EF4800CBEDE6 /* LaunchCacheWriter.cpp */; };
+               F9AB02C61F32A1F400EE96C4 /* Diagnostics.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986921B1DC3F07C00CBEDE6 /* Diagnostics.cpp */; };
+               F9AB02C71F32A22B00EE96C4 /* LaunchCacheReader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692061DC3EF4800CBEDE6 /* LaunchCacheReader.cpp */; };
+               F9AB02C81F32A23B00EE96C4 /* MachOParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F96D19BE1D94A6DC007AF3CE /* MachOParser.cpp */; };
+               F9AB02C91F32A24B00EE96C4 /* ClosureBuffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9E5E2C41EB00A870013A0BB /* ClosureBuffer.cpp */; };
+               F9AB02CA1F32A25F00EE96C4 /* DyldCacheParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9B3CAEB1EEB5CFA00C9A48B /* DyldCacheParser.cpp */; };
+               F9AB02CB1F32A26700EE96C4 /* PathOverrides.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9F76FAE1E08CFF200828678 /* PathOverrides.cpp */; };
+               F9AB02CC1F32A33C00EE96C4 /* FileUtils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986920D1DC3EF6C00CBEDE6 /* FileUtils.cpp */; };
+               F9ABA06E1E289B72000F21B4 /* closuredProtocol.defs in Sources */ = {isa = PBXBuildFile; fileRef = F9DDEDAB1E28787900A753DC /* closuredProtocol.defs */; };
+               F9B3CAEA1EE20FE200C9A48B /* ClosureBuffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9E5E2C41EB00A870013A0BB /* ClosureBuffer.cpp */; };
+               F9B3CAEC1EEB5CFB00C9A48B /* DyldCacheParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9B3CAEB1EEB5CFA00C9A48B /* DyldCacheParser.cpp */; };
                F9BA514B0ECE4F4200D1D62E /* dyld_stub_binder.s in Sources */ = {isa = PBXBuildFile; fileRef = F99EFC0D0EAD60E8001032B8 /* dyld_stub_binder.s */; };
                F9BA514B0ECE4F4200D1D62E /* dyld_stub_binder.s in Sources */ = {isa = PBXBuildFile; fileRef = F99EFC0D0EAD60E8001032B8 /* dyld_stub_binder.s */; };
+               F9C15A4A1E1F7DAC0006E570 /* APIs_macOS.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9C15A491E1F7D960006E570 /* APIs_macOS.cpp */; };
+               F9C275571DA5D67F007A5D8A /* MachOParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F96D19BE1D94A6DC007AF3CE /* MachOParser.cpp */; };
+               F9C2755A1DA71CE8007A5D8A /* Loading.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9C275581DA71A13007A5D8A /* Loading.cpp */; };
+               F9C2755B1DA73EA1007A5D8A /* Loading.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9C275581DA71A13007A5D8A /* Loading.cpp */; };
                F9C69EFE14EC8AD2009CAE2E /* objc-shared-cache.h in usr|local|include */ = {isa = PBXBuildFile; fileRef = F9C69EFD14EC8ABF009CAE2E /* objc-shared-cache.h */; };
                F9C69EFE14EC8AD2009CAE2E /* objc-shared-cache.h in usr|local|include */ = {isa = PBXBuildFile; fileRef = F9C69EFD14EC8ABF009CAE2E /* objc-shared-cache.h */; };
+               F9C73AC21E2992730025C89E /* closuredProtocol.defs in Sources */ = {isa = PBXBuildFile; fileRef = F9DDEDAB1E28787900A753DC /* closuredProtocol.defs */; };
+               F9C86B651E2B16C600FD8669 /* closuredProtocol.defs in Sources */ = {isa = PBXBuildFile; fileRef = F9DDEDAB1E28787900A753DC /* closuredProtocol.defs */; };
                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 */; };
                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 */; };
+               F9D8623F1DC41043000A199A /* OptimizerLinkedit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692121DC3EF6C00CBEDE6 /* OptimizerLinkedit.cpp */; };
+               F9D862401DC57A27000A199A /* OptimizerObjC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692131DC3EF6C00CBEDE6 /* OptimizerObjC.cpp */; };
+               F9D862411DC65A4E000A199A /* LaunchCacheWriter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692071DC3EF4800CBEDE6 /* LaunchCacheWriter.cpp */; };
+               F9D862421DC65A53000A199A /* LaunchCacheReader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692061DC3EF4800CBEDE6 /* LaunchCacheReader.cpp */; };
+               F9D862431DC90A4F000A199A /* LaunchCacheReader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692061DC3EF4800CBEDE6 /* LaunchCacheReader.cpp */; };
+               F9D862451DC975A5000A199A /* dyld_closure_util.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9D862441DC9759C000A199A /* dyld_closure_util.cpp */; };
+               F9D862461DC975AA000A199A /* Diagnostics.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986921B1DC3F07C00CBEDE6 /* Diagnostics.cpp */; };
+               F9D862471DC975B1000A199A /* LaunchCacheWriter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692071DC3EF4800CBEDE6 /* LaunchCacheWriter.cpp */; };
+               F9D862481DC975B3000A199A /* LaunchCacheReader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692061DC3EF4800CBEDE6 /* LaunchCacheReader.cpp */; };
+               F9D8624B1DC976E4000A199A /* LaunchCachePrinter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692051DC3EF4800CBEDE6 /* LaunchCachePrinter.cpp */; };
+               F9D8624C1DC97717000A199A /* DyldSharedCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692141DC3EF6C00CBEDE6 /* DyldSharedCache.cpp */; };
+               F9D8624D1DC9783E000A199A /* FileUtils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986920D1DC3EF6C00CBEDE6 /* FileUtils.cpp */; };
+               F9D8624E1DCBD06A000A199A /* Diagnostics.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986921B1DC3F07C00CBEDE6 /* Diagnostics.cpp */; };
+               F9D8624F1DCBD318000A199A /* Diagnostics.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986921B1DC3F07C00CBEDE6 /* Diagnostics.cpp */; };
+               F9D862501DCBD31D000A199A /* LaunchCacheReader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692061DC3EF4800CBEDE6 /* LaunchCacheReader.cpp */; };
+               F9D862511DCBD330000A199A /* DyldSharedCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692141DC3EF6C00CBEDE6 /* DyldSharedCache.cpp */; };
+               F9DDEDB91E2878EC00A753DC /* closured.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9DDEDAA1E28787900A753DC /* closured.cpp */; };
+               F9DDEDBA1E2878F100A753DC /* closuredProtocol.defs in Sources */ = {isa = PBXBuildFile; fileRef = F9DDEDAB1E28787900A753DC /* closuredProtocol.defs */; settings = {ATTRIBUTES = (Server, ); }; };
+               F9DDEDBB1E287C9500A753DC /* DyldSharedCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692141DC3EF6C00CBEDE6 /* DyldSharedCache.cpp */; };
+               F9DDEDBC1E287CA100A753DC /* Diagnostics.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986921B1DC3F07C00CBEDE6 /* Diagnostics.cpp */; };
+               F9DDEDBD1E287CB100A753DC /* LaunchCacheWriter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692071DC3EF4800CBEDE6 /* LaunchCacheWriter.cpp */; };
+               F9DDEDBE1E287CF600A753DC /* FileUtils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986920D1DC3EF6C00CBEDE6 /* FileUtils.cpp */; };
+               F9DDEDBF1E287CF600A753DC /* ImageProxy.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F963546B1DD8F2A800895049 /* ImageProxy.cpp */; };
+               F9DDEDC01E287CF600A753DC /* LaunchCacheReader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692061DC3EF4800CBEDE6 /* LaunchCacheReader.cpp */; };
+               F9DDEDC11E287D3C00A753DC /* MachOParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F96D19BE1D94A6DC007AF3CE /* MachOParser.cpp */; };
+               F9DDEDC21E287D8A00A753DC /* PathOverrides.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9F76FAE1E08CFF200828678 /* PathOverrides.cpp */; };
+               F9E5E2C61EB00A9F0013A0BB /* ClosureBuffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9E5E2C41EB00A870013A0BB /* ClosureBuffer.cpp */; };
+               F9E5E2C71EB00AAA0013A0BB /* ClosureBuffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9E5E2C41EB00A870013A0BB /* ClosureBuffer.cpp */; };
                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 */; };
                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 */; };
                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 */; };
                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 */; };
+               F9F76FB01E09CDF400828678 /* PathOverrides.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9F76FAE1E08CFF200828678 /* PathOverrides.cpp */; };
 /* End PBXBuildFile section */
 
 /* Begin PBXBuildRule section */
 /* End PBXBuildFile section */
 
 /* Begin PBXBuildRule section */
                        remoteGlobalIDString = 37A0AD0A1C15FFF500731E50;
                        remoteInfo = update_dyld_shared_cache;
                };
                        remoteGlobalIDString = 37A0AD0A1C15FFF500731E50;
                        remoteInfo = update_dyld_shared_cache;
                };
-               37A0AD101C16003600731E50 /* PBXContainerItemProxy */ = {
+               D8668ACF1ECE335F005E7D31 /* PBXContainerItemProxy */ = {
                        isa = PBXContainerItemProxy;
                        containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */;
                        proxyType = 1;
                        isa = PBXContainerItemProxy;
                        containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */;
                        proxyType = 1;
-                       remoteGlobalIDString = F93937310A94FAF700070A07;
-                       remoteInfo = update_dyld_shared_cache_tool;
+                       remoteGlobalIDString = F97C61A61DBAD1A900A84CD7;
+                       remoteInfo = dyld_closure_util;
                };
                };
-               37A0AD121C16003600731E50 /* PBXContainerItemProxy */ = {
+               F908134711D3ED1A00626CC1 /* PBXContainerItemProxy */ = {
                        isa = PBXContainerItemProxy;
                        containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */;
                        proxyType = 1;
                        isa = PBXContainerItemProxy;
                        containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */;
                        proxyType = 1;
-                       remoteGlobalIDString = F9F2A5580F7AEE9800B7C9EB;
-                       remoteInfo = libdsc;
+                       remoteGlobalIDString = F9ED4C9E0630A76B00DF4E74;
+                       remoteInfo = libdyld.dylib;
                };
                };
-               37A0AD141C16003600731E50 /* PBXContainerItemProxy */ = {
+               F922C8111F33B62700D8F479 /* PBXContainerItemProxy */ = {
                        isa = PBXContainerItemProxy;
                        containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */;
                        proxyType = 1;
                        isa = PBXContainerItemProxy;
                        containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */;
                        proxyType = 1;
-                       remoteGlobalIDString = F9D1001114D8D0BA00099D91;
-                       remoteInfo = dsc_extractor;
+                       remoteGlobalIDString = F9AB02B71F329FAA00EE96C4;
+                       remoteInfo = libclosured;
                };
                };
-               37A0AD161C16003600731E50 /* PBXContainerItemProxy */ = {
+               F922C81D1F33B96300D8F479 /* PBXContainerItemProxy */ = {
                        isa = PBXContainerItemProxy;
                        containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */;
                        proxyType = 1;
                        isa = PBXContainerItemProxy;
                        containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */;
                        proxyType = 1;
-                       remoteGlobalIDString = 377685F21AC4B27D00026E6C;
-                       remoteInfo = multi_dyld_shared_cache_builder;
+                       remoteGlobalIDString = F922C8161F33B73800D8F479;
+                       remoteInfo = "libclosured-stub";
                };
                };
-               37A0AD181C16003600731E50 /* PBXContainerItemProxy */ = {
+               F94182D71E60F0BE00D8EF25 /* PBXContainerItemProxy */ = {
                        isa = PBXContainerItemProxy;
                        containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */;
                        proxyType = 1;
                        isa = PBXContainerItemProxy;
                        containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */;
                        proxyType = 1;
-                       remoteGlobalIDString = 3703A1111B38C1B300ADBA7F;
-                       remoteInfo = dyld_shared_cache_builder;
+                       remoteGlobalIDString = F99B8E550FEC10F600701838;
+                       remoteInfo = dyld_shared_cache_util;
                };
                };
-               37A0AD1A1C16004600731E50 /* PBXContainerItemProxy */ = {
+               F94182D91E60F0C000D8EF25 /* PBXContainerItemProxy */ = {
                        isa = PBXContainerItemProxy;
                        containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */;
                        proxyType = 1;
                        isa = PBXContainerItemProxy;
                        containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */;
                        proxyType = 1;
-                       remoteGlobalIDString = F99B8E550FEC10F600701838;
-                       remoteInfo = dyld_shared_cache_util;
+                       remoteGlobalIDString = F9F2A5580F7AEE9800B7C9EB;
+                       remoteInfo = libdsc;
                };
                };
-               F908134711D3ED1A00626CC1 /* PBXContainerItemProxy */ = {
+               F94182DB1E60F16900D8EF25 /* PBXContainerItemProxy */ = {
                        isa = PBXContainerItemProxy;
                        containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */;
                        proxyType = 1;
                        isa = PBXContainerItemProxy;
                        containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */;
                        proxyType = 1;
-                       remoteGlobalIDString = F9ED4C9E0630A76B00DF4E74;
-                       remoteInfo = libdyld.dylib;
+                       remoteGlobalIDString = F9D1001114D8D0BA00099D91;
+                       remoteInfo = dsc_extractor;
+               };
+               F96543A01E343601003C5540 /* PBXContainerItemProxy */ = {
+                       isa = PBXContainerItemProxy;
+                       containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */;
+                       proxyType = 1;
+                       remoteGlobalIDString = F97C61A61DBAD1A900A84CD7;
+                       remoteInfo = dyld_closure_util;
                };
                F97FF3651C237F97000ACDD2 /* PBXContainerItemProxy */ = {
                        isa = PBXContainerItemProxy;
                };
                F97FF3651C237F97000ACDD2 /* PBXContainerItemProxy */ = {
                        isa = PBXContainerItemProxy;
                        );
                        runOnlyForDeploymentPostprocessing = 1;
                };
                        );
                        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 */,
-                       );
-                       name = "usr|local|include|mach-o";
-                       runOnlyForDeploymentPostprocessing = 1;
-               };
                F908137211D3FB5000626CC1 /* usr|share|man|man1 */ = {
                        isa = PBXCopyFilesBuildPhase;
                        buildActionMask = 8;
                F908137211D3FB5000626CC1 /* usr|share|man|man1 */ = {
                        isa = PBXCopyFilesBuildPhase;
                        buildActionMask = 8;
                        name = "usr|share|man|man3";
                        runOnlyForDeploymentPostprocessing = 1;
                };
                        name = "usr|share|man|man3";
                        runOnlyForDeploymentPostprocessing = 1;
                };
+               F94942B11E67965C0019AE08 /* install man page */ = {
+                       isa = PBXCopyFilesBuildPhase;
+                       buildActionMask = 8;
+                       dstPath = /usr/share/man/man1;
+                       dstSubfolderSpec = 0;
+                       files = (
+                               F94942B31E6796D70019AE08 /* closured.1 in install man page */,
+                       );
+                       name = "install man page";
+                       runOnlyForDeploymentPostprocessing = 1;
+               };
+               F97C61A51DBAD1A900A84CD7 /* Copy Files */ = {
+                       isa = PBXCopyFilesBuildPhase;
+                       buildActionMask = 2147483647;
+                       dstPath = /usr/share/man/man1/;
+                       dstSubfolderSpec = 0;
+                       files = (
+                       );
+                       name = "Copy Files";
+                       runOnlyForDeploymentPostprocessing = 1;
+               };
                F97FF3541C23638F000ACDD2 /* install man page */ = {
                        isa = PBXCopyFilesBuildPhase;
                        buildActionMask = 2147483647;
                F97FF3541C23638F000ACDD2 /* install man page */ = {
                        isa = PBXCopyFilesBuildPhase;
                        buildActionMask = 2147483647;
 
 /* 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; };
 
 /* 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 = "<group>"; 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 = "<group>"; usesTabs = 0; };
-               370E5F411CC06CF8000158F2 /* Logging.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Logging.h; sourceTree = "<group>"; usesTabs = 0; };
-               371D29811B2F53C8000BBE48 /* MultiCacheBuilder.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MultiCacheBuilder.mm; sourceTree = "<group>"; usesTabs = 0; };
-               371D29831B30E587000BBE48 /* MultiCacheBuilder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MultiCacheBuilder.h; sourceTree = "<group>"; usesTabs = 0; };
-               374DDAE11AC0A0F70097CFF0 /* Trie.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Trie.hpp; sourceTree = "<group>"; };
-               376ABDB71C5930E7009F0011 /* MachOProxy.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MachOProxy.cpp; sourceTree = "<group>"; usesTabs = 0; };
-               376ABDB81C5930E7009F0011 /* MachOProxy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MachOProxy.h; sourceTree = "<group>"; 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; };
                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 = "<group>"; usesTabs = 0; };
-               37BF1D731B6168150048BC27 /* Manifest.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = Manifest.mm; sourceTree = "<group>"; usesTabs = 0; };
-               37BF1D741B6168150048BC27 /* Manifest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Manifest.h; sourceTree = "<group>"; usesTabs = 0; };
-               37E93E291AAFB0B10080E640 /* update_dyld_shared_cache.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = update_dyld_shared_cache.mm; sourceTree = "<group>"; usesTabs = 0; };
+               37908A271E3A853E009613FA /* dyld_shared_cache_builder.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = dyld_shared_cache_builder.mm; path = "dyld3/shared-cache/dyld_shared_cache_builder.mm"; sourceTree = "<group>"; };
+               37908A281E3A853E009613FA /* Manifest.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = Manifest.mm; path = "dyld3/shared-cache/Manifest.mm"; sourceTree = "<group>"; };
+               37908A291E3A853E009613FA /* multi_dyld_shared_cache_builder.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = multi_dyld_shared_cache_builder.mm; path = "dyld3/shared-cache/multi_dyld_shared_cache_builder.mm"; sourceTree = "<group>"; };
+               37908A2A1E3A85A4009613FA /* FileAbstraction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = FileAbstraction.hpp; path = "dyld3/shared-cache/FileAbstraction.hpp"; sourceTree = "<group>"; };
+               37908A2B1E3A85A4009613FA /* MachOFileAbstraction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = MachOFileAbstraction.hpp; path = "dyld3/shared-cache/MachOFileAbstraction.hpp"; sourceTree = "<group>"; };
+               37908A2C1E3A85A4009613FA /* Manifest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Manifest.h; path = "dyld3/shared-cache/Manifest.h"; sourceTree = "<group>"; };
+               37908A2D1E3A85A4009613FA /* Trie.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Trie.hpp; path = "dyld3/shared-cache/Trie.hpp"; sourceTree = "<group>"; };
+               37C5C2FB1E5CD154006B32C9 /* BuilderUtils.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = BuilderUtils.mm; path = "dyld3/shared-cache/BuilderUtils.mm"; sourceTree = "<group>"; };
+               37C5C2FC1E5CD154006B32C9 /* BuilderUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BuilderUtils.h; path = "dyld3/shared-cache/BuilderUtils.h"; sourceTree = "<group>"; };
+               37D7DAFE1E96F0ED00D52CEA /* Tracing.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Tracing.cpp; path = dyld3/Tracing.cpp; sourceTree = "<group>"; };
+               37D7DAFF1E96F0ED00D52CEA /* Tracing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Tracing.h; path = dyld3/Tracing.h; sourceTree = "<group>"; };
                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; };
                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; };
                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; };
                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 = "<group>"; };
+               F902031F1DEE83C000AC3F76 /* StringUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = StringUtils.h; path = "dyld3/shared-cache/StringUtils.h"; sourceTree = "<group>"; };
+               F90F7C071E9C6B8B00535722 /* libCrashReporterClient.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libCrashReporterClient.a; path = usr/local/lib/libCrashReporterClient.a; sourceTree = SDKROOT; };
+               F913C8501E9312A100458AA3 /* com.apple.dyld.closured.sb */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = com.apple.dyld.closured.sb; path = dyld3/closured/com.apple.dyld.closured.sb; sourceTree = "<group>"; };
                F913FAD90630A8AE00B7AE9D /* dyldAPIsInLibSystem.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = dyldAPIsInLibSystem.cpp; path = src/dyldAPIsInLibSystem.cpp; sourceTree = "<group>"; };
                F918691408B16D2500E0F9DB /* dyld-interposing.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = "dyld-interposing.h"; path = "include/mach-o/dyld-interposing.h"; sourceTree = "<group>"; };
                F913FAD90630A8AE00B7AE9D /* dyldAPIsInLibSystem.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = dyldAPIsInLibSystem.cpp; path = src/dyldAPIsInLibSystem.cpp; sourceTree = "<group>"; };
                F918691408B16D2500E0F9DB /* dyld-interposing.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = "dyld-interposing.h"; path = "include/mach-o/dyld-interposing.h"; sourceTree = "<group>"; };
+               F922C8171F33B73800D8F479 /* libclosured.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libclosured.dylib; sourceTree = BUILT_PRODUCTS_DIR; };
+               F922C81B1F33B86300D8F479 /* libclosured-stub.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "libclosured-stub.cpp"; path = "dyld3/libclosured-stub.cpp"; sourceTree = "<group>"; };
                F9280B791AB9DCA000B18AEC /* ImageLoaderMegaDylib.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ImageLoaderMegaDylib.cpp; path = src/ImageLoaderMegaDylib.cpp; sourceTree = "<group>"; };
                F9280B7A1AB9DCA000B18AEC /* ImageLoaderMegaDylib.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ImageLoaderMegaDylib.h; path = src/ImageLoaderMegaDylib.h; sourceTree = "<group>"; };
                F93937320A94FAF700070A07 /* update_dyld_shared_cache */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = update_dyld_shared_cache; sourceTree = BUILT_PRODUCTS_DIR; };
                F9280B791AB9DCA000B18AEC /* ImageLoaderMegaDylib.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ImageLoaderMegaDylib.cpp; path = src/ImageLoaderMegaDylib.cpp; sourceTree = "<group>"; };
                F9280B7A1AB9DCA000B18AEC /* ImageLoaderMegaDylib.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ImageLoaderMegaDylib.h; path = src/ImageLoaderMegaDylib.h; sourceTree = "<group>"; };
                F93937320A94FAF700070A07 /* update_dyld_shared_cache */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = update_dyld_shared_cache; sourceTree = BUILT_PRODUCTS_DIR; };
                F93937400A94FC4700070A07 /* dyld_cache_format.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = dyld_cache_format.h; sourceTree = "<group>"; };
                F93937410A94FC4700070A07 /* FileAbstraction.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; path = FileAbstraction.hpp; sourceTree = "<group>"; };
                F93937430A94FC4700070A07 /* MachOFileAbstraction.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; path = MachOFileAbstraction.hpp; sourceTree = "<group>"; };
                F93937400A94FC4700070A07 /* dyld_cache_format.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = dyld_cache_format.h; sourceTree = "<group>"; };
                F93937410A94FC4700070A07 /* FileAbstraction.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; path = FileAbstraction.hpp; sourceTree = "<group>"; };
                F93937430A94FC4700070A07 /* MachOFileAbstraction.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; path = MachOFileAbstraction.hpp; sourceTree = "<group>"; };
-               F93937440A94FC4700070A07 /* MachOLayout.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; path = MachOLayout.hpp; sourceTree = "<group>"; };
+               F94182DE1E60FFDC00D8EF25 /* update_dyld_sim_shared_cache.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = update_dyld_sim_shared_cache.xcconfig; sourceTree = "<group>"; };
+               F94942B21E6796D40019AE08 /* closured.1 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.man; path = closured.1; sourceTree = "<group>"; };
+               F94C22241E513CA90079E5DD /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; };
                F94DB9000F0A9B1700323715 /* ImageLoaderMachOClassic.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ImageLoaderMachOClassic.cpp; path = src/ImageLoaderMachOClassic.cpp; sourceTree = "<group>"; };
                F94DB9010F0A9B1700323715 /* ImageLoaderMachOClassic.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ImageLoaderMachOClassic.h; path = src/ImageLoaderMachOClassic.h; sourceTree = "<group>"; };
                F94DB9020F0A9B1700323715 /* ImageLoaderMachOCompressed.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ImageLoaderMachOCompressed.cpp; path = src/ImageLoaderMachOCompressed.cpp; sourceTree = "<group>"; };
                F94DB9000F0A9B1700323715 /* ImageLoaderMachOClassic.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ImageLoaderMachOClassic.cpp; path = src/ImageLoaderMachOClassic.cpp; sourceTree = "<group>"; };
                F94DB9010F0A9B1700323715 /* ImageLoaderMachOClassic.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ImageLoaderMachOClassic.h; path = src/ImageLoaderMachOClassic.h; sourceTree = "<group>"; };
                F94DB9020F0A9B1700323715 /* ImageLoaderMachOCompressed.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ImageLoaderMachOCompressed.cpp; path = src/ImageLoaderMachOCompressed.cpp; sourceTree = "<group>"; };
                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 = "<group>"; 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 = "<group>"; 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 = "<group>"; };
                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 = "<group>"; 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 = "<group>"; 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 = "<group>"; };
-               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 = "<group>"; };
+               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 = "<group>"; usesTabs = 0; };
                F95C95160E994796007B7CB8 /* MachOTrie.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = MachOTrie.hpp; sourceTree = "<group>"; };
                F95C95160E994796007B7CB8 /* MachOTrie.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = MachOTrie.hpp; sourceTree = "<group>"; };
+               F963542E1DCD736000895049 /* update_dyld_sim_shared_cache.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = update_dyld_sim_shared_cache.cpp; path = "dyld3/shared-cache/update_dyld_sim_shared_cache.cpp"; sourceTree = "<group>"; usesTabs = 0; };
+               F96354451DCD74A400895049 /* update_dyld_sim_shared_cache */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = update_dyld_sim_shared_cache; sourceTree = BUILT_PRODUCTS_DIR; };
+               F963546A1DD8D8D300895049 /* ImageProxy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ImageProxy.h; path = "dyld3/shared-cache/ImageProxy.h"; sourceTree = "<group>"; usesTabs = 0; };
+               F963546B1DD8F2A800895049 /* ImageProxy.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ImageProxy.cpp; path = "dyld3/shared-cache/ImageProxy.cpp"; sourceTree = "<group>"; usesTabs = 0; };
                F96D19711D7F63EE007AF3CE /* expand.pl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.perl; name = expand.pl; path = bin/expand.pl; sourceTree = "<group>"; };
                F96D19711D7F63EE007AF3CE /* expand.pl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.perl; name = expand.pl; path = bin/expand.pl; sourceTree = "<group>"; };
+               F96D19A51D9363D6007AF3CE /* APIs.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = APIs.cpp; path = dyld3/APIs.cpp; sourceTree = "<group>"; usesTabs = 0; };
+               F96D19A61D9363D6007AF3CE /* AllImages.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = AllImages.cpp; path = dyld3/AllImages.cpp; sourceTree = "<group>"; usesTabs = 0; };
+               F96D19A71D9363D6007AF3CE /* AllImages.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AllImages.h; path = dyld3/AllImages.h; sourceTree = "<group>"; usesTabs = 0; };
+               F96D19A91D94576E007AF3CE /* MachOParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MachOParser.h; path = dyld3/MachOParser.h; sourceTree = "<group>"; usesTabs = 0; };
+               F96D19BE1D94A6DC007AF3CE /* MachOParser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MachOParser.cpp; path = dyld3/MachOParser.cpp; sourceTree = "<group>"; usesTabs = 0; };
+               F96D19C11D95C6D6007AF3CE /* APIs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = APIs.h; path = dyld3/APIs.h; sourceTree = "<group>"; usesTabs = 0; };
                F971DD131A4A0E0700BBDD52 /* base.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = base.xcconfig; sourceTree = "<group>"; };
                F971DD141A4A0E0700BBDD52 /* dyld.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = dyld.xcconfig; sourceTree = "<group>"; };
                F971DD151A4A0E0700BBDD52 /* libdyld.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = libdyld.xcconfig; sourceTree = "<group>"; };
                F971DD161A4A0E0700BBDD52 /* update_dyld_shared_cache.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = update_dyld_shared_cache.xcconfig; sourceTree = "<group>"; };
                F976F548127B90F8004BA2A5 /* dyld.order */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = dyld.order; path = src/dyld.order; sourceTree = "<group>"; };
                F971DD131A4A0E0700BBDD52 /* base.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = base.xcconfig; sourceTree = "<group>"; };
                F971DD141A4A0E0700BBDD52 /* dyld.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = dyld.xcconfig; sourceTree = "<group>"; };
                F971DD151A4A0E0700BBDD52 /* libdyld.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = libdyld.xcconfig; sourceTree = "<group>"; };
                F971DD161A4A0E0700BBDD52 /* update_dyld_shared_cache.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = update_dyld_shared_cache.xcconfig; sourceTree = "<group>"; };
                F976F548127B90F8004BA2A5 /* dyld.order */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = dyld.order; path = src/dyld.order; sourceTree = "<group>"; };
+               F977DDC91E53BEA700609230 /* SharedCacheRuntime.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SharedCacheRuntime.cpp; path = dyld3/SharedCacheRuntime.cpp; sourceTree = "<group>"; };
+               F977DDCA1E53BEA700609230 /* SharedCacheRuntime.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SharedCacheRuntime.h; path = dyld3/SharedCacheRuntime.h; sourceTree = "<group>"; };
+               F97C619D1D96C5BE00A84CD7 /* libdyldEntryVector.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = libdyldEntryVector.h; path = dyld3/libdyldEntryVector.h; sourceTree = "<group>"; usesTabs = 0; };
+               F97C619E1D98292700A84CD7 /* libdyldEntryVector.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = libdyldEntryVector.cpp; path = dyld3/libdyldEntryVector.cpp; sourceTree = "<group>"; usesTabs = 0; };
+               F97C61A01D9CA6B800A84CD7 /* Logging.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Logging.cpp; path = dyld3/Logging.cpp; sourceTree = "<group>"; usesTabs = 0; };
+               F97C61A11D9CA6B800A84CD7 /* Logging.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Logging.h; path = dyld3/Logging.h; sourceTree = "<group>"; usesTabs = 0; };
+               F97C61A71DBAD1A900A84CD7 /* dyld_closure_util */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = dyld_closure_util; sourceTree = BUILT_PRODUCTS_DIR; };
                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 = "<group>"; };
                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 = "<group>"; };
-               F97FF35E1C236402000ACDD2 /* execserver.defs */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.mig; name = execserver.defs; path = testing/nocr/execserver.defs; sourceTree = "<group>"; };
                F97FF35F1C236402000ACDD2 /* nocr.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = nocr.c; path = testing/nocr/nocr.c; sourceTree = "<group>"; };
                F97FF3631C237F5C000ACDD2 /* nocr.1 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.man; name = nocr.1; path = ../../../testing/nocr/nocr.1; sourceTree = "<group>"; };
                F981BB8B170FC24400A686D6 /* dyldSyscallInterface.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = dyldSyscallInterface.h; path = src/dyldSyscallInterface.h; sourceTree = "<group>"; };
                F97FF35F1C236402000ACDD2 /* nocr.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = nocr.c; path = testing/nocr/nocr.c; sourceTree = "<group>"; };
                F97FF3631C237F5C000ACDD2 /* nocr.1 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.man; name = nocr.1; path = ../../../testing/nocr/nocr.1; sourceTree = "<group>"; };
                F981BB8B170FC24400A686D6 /* dyldSyscallInterface.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = dyldSyscallInterface.h; path = src/dyldSyscallInterface.h; sourceTree = "<group>"; };
-               F98935B90A9A412B00FB6228 /* MachOBinder.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; path = MachOBinder.hpp; sourceTree = "<group>"; };
-               F98935BA0A9A412B00FB6228 /* MachORebaser.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; path = MachORebaser.hpp; sourceTree = "<group>"; };
+               F98692001DC3EF4800CBEDE6 /* Diagnostics.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Diagnostics.h; path = dyld3/Diagnostics.h; sourceTree = "<group>"; usesTabs = 0; };
+               F98692021DC3EF4800CBEDE6 /* LaunchCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = LaunchCache.h; path = dyld3/LaunchCache.h; sourceTree = "<group>"; usesTabs = 0; };
+               F98692041DC3EF4800CBEDE6 /* LaunchCacheFormat.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = LaunchCacheFormat.h; path = dyld3/LaunchCacheFormat.h; sourceTree = "<group>"; usesTabs = 0; };
+               F98692051DC3EF4800CBEDE6 /* LaunchCachePrinter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = LaunchCachePrinter.cpp; path = dyld3/LaunchCachePrinter.cpp; sourceTree = "<group>"; usesTabs = 0; };
+               F98692061DC3EF4800CBEDE6 /* LaunchCacheReader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = LaunchCacheReader.cpp; path = dyld3/LaunchCacheReader.cpp; sourceTree = "<group>"; usesTabs = 0; };
+               F98692071DC3EF4800CBEDE6 /* LaunchCacheWriter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = LaunchCacheWriter.cpp; path = dyld3/LaunchCacheWriter.cpp; sourceTree = "<group>"; usesTabs = 0; };
+               F98692081DC3EF4800CBEDE6 /* LaunchCacheWriter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = LaunchCacheWriter.h; path = dyld3/LaunchCacheWriter.h; sourceTree = "<group>"; usesTabs = 0; };
+               F98692091DC3EF6C00CBEDE6 /* AdjustDylibSegments.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = AdjustDylibSegments.cpp; path = "dyld3/shared-cache/AdjustDylibSegments.cpp"; sourceTree = "<group>"; usesTabs = 0; };
+               F986920C1DC3EF6C00CBEDE6 /* DyldSharedCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DyldSharedCache.h; path = "dyld3/shared-cache/DyldSharedCache.h"; sourceTree = "<group>"; usesTabs = 0; };
+               F986920D1DC3EF6C00CBEDE6 /* FileUtils.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = FileUtils.cpp; path = "dyld3/shared-cache/FileUtils.cpp"; sourceTree = "<group>"; usesTabs = 0; };
+               F986920E1DC3EF6C00CBEDE6 /* FileUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FileUtils.h; path = "dyld3/shared-cache/FileUtils.h"; sourceTree = "<group>"; usesTabs = 0; };
+               F986920F1DC3EF6C00CBEDE6 /* ObjC1Abstraction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = ObjC1Abstraction.hpp; path = "dyld3/shared-cache/ObjC1Abstraction.hpp"; sourceTree = "<group>"; usesTabs = 0; };
+               F98692101DC3EF6C00CBEDE6 /* ObjC2Abstraction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = ObjC2Abstraction.hpp; path = "dyld3/shared-cache/ObjC2Abstraction.hpp"; sourceTree = "<group>"; usesTabs = 0; };
+               F98692111DC3EF6C00CBEDE6 /* OptimizerBranches.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = OptimizerBranches.cpp; path = "dyld3/shared-cache/OptimizerBranches.cpp"; sourceTree = "<group>"; usesTabs = 0; };
+               F98692121DC3EF6C00CBEDE6 /* OptimizerLinkedit.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = OptimizerLinkedit.cpp; path = "dyld3/shared-cache/OptimizerLinkedit.cpp"; sourceTree = "<group>"; usesTabs = 0; };
+               F98692131DC3EF6C00CBEDE6 /* OptimizerObjC.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = OptimizerObjC.cpp; path = "dyld3/shared-cache/OptimizerObjC.cpp"; sourceTree = "<group>"; usesTabs = 0; };
+               F98692141DC3EF6C00CBEDE6 /* DyldSharedCache.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DyldSharedCache.cpp; path = "dyld3/shared-cache/DyldSharedCache.cpp"; sourceTree = "<group>"; usesTabs = 0; };
+               F98692151DC3EF6C00CBEDE6 /* update_dyld_shared_cache.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = update_dyld_shared_cache.cpp; path = "dyld3/shared-cache/update_dyld_shared_cache.cpp"; sourceTree = "<group>"; usesTabs = 0; };
+               F986921B1DC3F07C00CBEDE6 /* Diagnostics.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Diagnostics.cpp; path = dyld3/Diagnostics.cpp; sourceTree = "<group>"; usesTabs = 0; };
+               F986921C1DC3F86C00CBEDE6 /* CacheBuilder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CacheBuilder.cpp; path = "dyld3/shared-cache/CacheBuilder.cpp"; sourceTree = "<group>"; usesTabs = 0; };
+               F986921D1DC3F86C00CBEDE6 /* CacheBuilder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CacheBuilder.h; path = "dyld3/shared-cache/CacheBuilder.h"; sourceTree = "<group>"; usesTabs = 0; };
+               F986921E1DC3F86C00CBEDE6 /* dyld_cache_format.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = dyld_cache_format.h; path = "dyld3/shared-cache/dyld_cache_format.h"; sourceTree = "<group>"; usesTabs = 0; };
+               F98692221DC4028B00CBEDE6 /* CodeSigningTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CodeSigningTypes.h; path = dyld3/CodeSigningTypes.h; sourceTree = "<group>"; usesTabs = 0; };
+               F988F0BA1E2FDF5B003AED79 /* execserver.defs */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.mig; name = execserver.defs; path = testing/nocr/execserver.defs; sourceTree = "<group>"; };
                F98D274C0AA79D7400416316 /* dyld_images.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = dyld_images.h; path = "include/mach-o/dyld_images.h"; sourceTree = "<group>"; };
                F98D274C0AA79D7400416316 /* dyld_images.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = dyld_images.h; path = "include/mach-o/dyld_images.h"; sourceTree = "<group>"; };
-               F98E18FF1A3FC1A9008DB73F /* OptimizerBranches.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OptimizerBranches.cpp; sourceTree = "<group>"; usesTabs = 0; };
-               F98E19001A3FC1A9008DB73F /* OptimizerBranches.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OptimizerBranches.h; sourceTree = "<group>"; usesTabs = 0; };
-               F98E19011A3FC1A9008DB73F /* OptimizerLinkedit.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OptimizerLinkedit.cpp; sourceTree = "<group>"; usesTabs = 0; };
-               F98E19031A3FC1A9008DB73F /* OptimizerObjC.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OptimizerObjC.cpp; sourceTree = "<group>"; usesTabs = 0; };
-               F98E190E1A40D167008DB73F /* ObjC1Abstraction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ObjC1Abstraction.hpp; sourceTree = "<group>"; usesTabs = 0; };
-               F98E190F1A40D167008DB73F /* ObjC2Abstraction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ObjC2Abstraction.hpp; sourceTree = "<group>"; usesTabs = 0; };
+               F99986FA1F198C0C00D523F5 /* dyld-potential-framework-overrides */ = {isa = PBXFileReference; explicitFileType = text; name = "dyld-potential-framework-overrides"; path = "dyld3/dyld-potential-framework-overrides"; sourceTree = "<group>"; };
                F99B8E620FEC11B400701838 /* dyld_shared_cache_util.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = dyld_shared_cache_util.cpp; sourceTree = "<group>"; };
                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 = "<group>"; };
                F99B8E620FEC11B400701838 /* dyld_shared_cache_util.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = dyld_shared_cache_util.cpp; sourceTree = "<group>"; };
                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 = "<group>"; };
                F9A221E60F3A6D7C00D15F73 /* dyldLibSystemGlue.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = dyldLibSystemGlue.c; path = src/dyldLibSystemGlue.c; sourceTree = "<group>"; };
                F9A6D6E2116F9DF20051CC16 /* threadLocalVariables.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = threadLocalVariables.c; path = src/threadLocalVariables.c; sourceTree = "<group>"; };
                F9A6D70B116FBBD10051CC16 /* threadLocalHelpers.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; name = threadLocalHelpers.s; path = src/threadLocalHelpers.s; sourceTree = "<group>"; };
                F9A221E60F3A6D7C00D15F73 /* dyldLibSystemGlue.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = dyldLibSystemGlue.c; path = src/dyldLibSystemGlue.c; sourceTree = "<group>"; };
                F9A6D6E2116F9DF20051CC16 /* threadLocalVariables.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = threadLocalVariables.c; path = src/threadLocalVariables.c; sourceTree = "<group>"; };
                F9A6D70B116FBBD10051CC16 /* threadLocalHelpers.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; name = threadLocalHelpers.s; path = src/threadLocalHelpers.s; sourceTree = "<group>"; };
+               F9AB02B81F329FAA00EE96C4 /* libclosured.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libclosured.dylib; sourceTree = BUILT_PRODUCTS_DIR; };
                F9AB709D0BA75730002F6068 /* dyldLibSystemInterface.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = dyldLibSystemInterface.h; path = src/dyldLibSystemInterface.h; sourceTree = "<group>"; };
                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 = "<group>"; };
                F9B01E3D0739ABDE00CF981B /* dyld.exp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.exports; name = dyld.exp; path = src/dyld.exp; sourceTree = SOURCE_ROOT; };
                F9AB709D0BA75730002F6068 /* dyldLibSystemInterface.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = dyldLibSystemInterface.h; path = src/dyldLibSystemInterface.h; sourceTree = "<group>"; };
                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 = "<group>"; };
                F9B01E3D0739ABDE00CF981B /* dyld.exp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.exports; name = dyld.exp; path = src/dyld.exp; sourceTree = SOURCE_ROOT; };
+               F9B3CAEB1EEB5CFA00C9A48B /* DyldCacheParser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DyldCacheParser.cpp; path = dyld3/DyldCacheParser.cpp; sourceTree = "<group>"; };
+               F9B3CAED1EEB5D0D00C9A48B /* DyldCacheParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DyldCacheParser.h; path = dyld3/DyldCacheParser.h; sourceTree = "<group>"; };
+               F9C15A451E19C2F50006E570 /* make_ios_dyld_cache.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = make_ios_dyld_cache.cpp; path = "dyld3/shared-cache/make_ios_dyld_cache.cpp"; sourceTree = "<group>"; };
+               F9C15A491E1F7D960006E570 /* APIs_macOS.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = APIs_macOS.cpp; path = dyld3/APIs_macOS.cpp; sourceTree = "<group>"; };
+               F9C275581DA71A13007A5D8A /* Loading.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Loading.cpp; path = dyld3/Loading.cpp; sourceTree = "<group>"; usesTabs = 0; };
+               F9C275591DA71A13007A5D8A /* Loading.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Loading.h; path = dyld3/Loading.h; sourceTree = "<group>"; usesTabs = 0; };
                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 = "<group>"; usesTabs = 0; };
                F9CE30781208F1B50098B590 /* dsc_extractor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = dsc_extractor.cpp; sourceTree = "<group>"; };
                F9CE30791208F1B50098B590 /* dsc_extractor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = dsc_extractor.h; sourceTree = "<group>"; };
                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 = "<group>"; usesTabs = 0; };
                F9CE30781208F1B50098B590 /* dsc_extractor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = dsc_extractor.cpp; sourceTree = "<group>"; };
                F9CE30791208F1B50098B590 /* dsc_extractor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = dsc_extractor.h; sourceTree = "<group>"; };
-               F9D0FE6D1A367093001E839B /* AdjustForNewSegmentLocation.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = AdjustForNewSegmentLocation.cpp; sourceTree = "<group>"; usesTabs = 0; };
-               F9D0FE6E1A367093001E839B /* BindAllImages.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = BindAllImages.cpp; sourceTree = "<group>"; usesTabs = 0; };
-               F9D0FE6F1A367093001E839B /* FileCache.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FileCache.cpp; sourceTree = "<group>"; usesTabs = 0; };
-               F9D0FE701A367093001E839B /* SharedCache.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = SharedCache.cpp; sourceTree = "<group>"; usesTabs = 0; };
-               F9D0FE711A367093001E839B /* mega-dylib-utils.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "mega-dylib-utils.h"; sourceTree = "<group>"; 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 = "<group>"; };
                F9D49CCB1458B95200F86ADD /* start_glue.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; name = start_glue.s; path = src/start_glue.s; sourceTree = "<group>"; };
                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 = "<group>"; };
                F9D49CCB1458B95200F86ADD /* start_glue.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; name = start_glue.s; path = src/start_glue.s; sourceTree = "<group>"; };
+               F9D862441DC9759C000A199A /* dyld_closure_util.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = dyld_closure_util.cpp; path = "dyld3/shared-cache/dyld_closure_util.cpp"; sourceTree = "<group>"; usesTabs = 0; };
+               F9DDEDAA1E28787900A753DC /* closured.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = closured.cpp; path = dyld3/closured/closured.cpp; sourceTree = "<group>"; };
+               F9DDEDAB1E28787900A753DC /* closuredProtocol.defs */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.mig; name = closuredProtocol.defs; path = dyld3/closured/closuredProtocol.defs; sourceTree = "<group>"; };
+               F9DDEDAC1E28787900A753DC /* closuredtypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = closuredtypes.h; path = dyld3/closured/closuredtypes.h; sourceTree = "<group>"; };
+               F9DDEDAD1E28787900A753DC /* com.apple.dyld.closured.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = com.apple.dyld.closured.plist; path = dyld3/closured/com.apple.dyld.closured.plist; sourceTree = "<group>"; };
+               F9DDEDB21E2878CA00A753DC /* closured */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = closured; sourceTree = BUILT_PRODUCTS_DIR; };
                F9E572000A66EF41007D9BE9 /* dlopen_preflight.3 */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text; path = dlopen_preflight.3; sourceTree = "<group>"; };
                F9E572000A66EF41007D9BE9 /* dlopen_preflight.3 */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text; path = dlopen_preflight.3; sourceTree = "<group>"; };
-               F9E7AD981BE47CE30025D311 /* CodeSigningTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CodeSigningTypes.h; sourceTree = "<group>"; };
+               F9E5E2C41EB00A870013A0BB /* ClosureBuffer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ClosureBuffer.cpp; path = dyld3/ClosureBuffer.cpp; sourceTree = "<group>"; };
+               F9E5E2C51EB00A870013A0BB /* ClosureBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ClosureBuffer.h; path = dyld3/ClosureBuffer.h; sourceTree = "<group>"; };
                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; };
                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; };
                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; };
                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; };
+               F9EDC09E1F04767300B030F4 /* update_dyld_shared_cache_entitlements.plist */ = {isa = PBXFileReference; explicitFileType = text.plist.info; name = update_dyld_shared_cache_entitlements.plist; path = "dyld3/shared-cache/update_dyld_shared_cache_entitlements.plist"; sourceTree = "<group>"; };
+               F9EDC09F1F0478A300B030F4 /* closured_entitlements.plist */ = {isa = PBXFileReference; explicitFileType = text.plist.info; name = closured_entitlements.plist; path = dyld3/closured/closured_entitlements.plist; sourceTree = "<group>"; };
+               F9EDC0A01F0481B400B030F4 /* closured.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = closured.xcconfig; sourceTree = "<group>"; };
                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 = "<group>"; };
                F9F2A56F0F7AEEE300B7C9EB /* dsc_iterator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = dsc_iterator.h; sourceTree = "<group>"; };
                F9F6F4261C1FAF8000BD8FED /* testing */ = {isa = PBXFileReference; lastKnownFileType = folder; path = testing; sourceTree = "<group>"; };
                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 = "<group>"; };
                F9F2A56F0F7AEEE300B7C9EB /* dsc_iterator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = dsc_iterator.h; sourceTree = "<group>"; };
                F9F6F4261C1FAF8000BD8FED /* testing */ = {isa = PBXFileReference; lastKnownFileType = folder; path = testing; sourceTree = "<group>"; };
+               F9F76FAE1E08CFF200828678 /* PathOverrides.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = PathOverrides.cpp; path = dyld3/PathOverrides.cpp; sourceTree = "<group>"; };
+               F9F76FAF1E08CFF200828678 /* PathOverrides.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PathOverrides.h; path = dyld3/PathOverrides.h; sourceTree = "<group>"; };
 /* End PBXFileReference section */
 
 /* Begin PBXFrameworksBuildPhase section */
 /* End PBXFileReference section */
 
 /* Begin PBXFrameworksBuildPhase section */
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
+               F922C8141F33B73800D8F479 /* Frameworks */ = {
+                       isa = PBXFrameworksBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
                F93937300A94FAF700070A07 /* Frameworks */ = {
                        isa = PBXFrameworksBuildPhase;
                        buildActionMask = 2147483647;
                        files = (
                F93937300A94FAF700070A07 /* Frameworks */ = {
                        isa = PBXFrameworksBuildPhase;
                        buildActionMask = 2147483647;
                        files = (
-                               370C6E541BDEF08000387223 /* libspindump.dylib in Frameworks */,
+                               F94C22251E513CA90079E5DD /* CoreFoundation.framework in Frameworks */,
+                               F92015701DDFEBAF00816A4A /* Bom.framework in Frameworks */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               F963543D1DCD74A400895049 /* Frameworks */ = {
+                       isa = PBXFrameworksBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               F97C61A41DBAD1A900A84CD7 /* Frameworks */ = {
+                       isa = PBXFrameworksBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
+               F9AB02B51F329FAA00EE96C4 /* Frameworks */ = {
+                       isa = PBXFrameworksBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
                F9D1001014D8D0BA00099D91 /* Frameworks */ = {
                        isa = PBXFrameworksBuildPhase;
                        buildActionMask = 2147483647;
                F9D1001014D8D0BA00099D91 /* Frameworks */ = {
                        isa = PBXFrameworksBuildPhase;
                        buildActionMask = 2147483647;
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
+               F9DDEDAF1E2878CA00A753DC /* Frameworks */ = {
+                       isa = PBXFrameworksBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               F90F7C081E9C6B8B00535722 /* libCrashReporterClient.a in Frameworks */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
                F9F2A5570F7AEE9800B7C9EB /* Frameworks */ = {
                        isa = PBXFrameworksBuildPhase;
                        buildActionMask = 2147483647;
                F9F2A5570F7AEE9800B7C9EB /* Frameworks */ = {
                        isa = PBXFrameworksBuildPhase;
                        buildActionMask = 2147483647;
                EF799FE8070D27BB00F78484 /* man1 */ = {
                        isa = PBXGroup;
                        children = (
                EF799FE8070D27BB00F78484 /* man1 */ = {
                        isa = PBXGroup;
                        children = (
+                               F94942B21E6796D40019AE08 /* closured.1 */,
                                EF799FE9070D27BB00F78484 /* dyld.1 */,
                                F97FF3631C237F5C000ACDD2 /* nocr.1 */,
                                F9D238D90A9E19A0002B55C7 /* update_dyld_shared_cache.1 */,
                                EF799FE9070D27BB00F78484 /* dyld.1 */,
                                F97FF3631C237F5C000ACDD2 /* nocr.1 */,
                                F9D238D90A9E19A0002B55C7 /* update_dyld_shared_cache.1 */,
                F939373D0A94FC4700070A07 /* launch-cache */ = {
                        isa = PBXGroup;
                        children = (
                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 */,
                                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 */,
                                F95C95160E994796007B7CB8 /* MachOTrie.hpp */,
                                F9F2A56E0F7AEEE300B7C9EB /* dsc_iterator.cpp */,
                                F9F2A56F0F7AEEE300B7C9EB /* dsc_iterator.h */,
                        path = "launch-cache";
                        sourceTree = "<group>";
                };
                        path = "launch-cache";
                        sourceTree = "<group>";
                };
+               F94C22231E513CA90079E5DD /* Frameworks */ = {
+                       isa = PBXGroup;
+                       children = (
+                               F90F7C071E9C6B8B00535722 /* libCrashReporterClient.a */,
+                               37F7A5961BB363820039043A /* Bom.framework */,
+                               376ED1D71C46F2710051DD54 /* Metabom.framework */,
+                               F94C22241E513CA90079E5DD /* CoreFoundation.framework */,
+                       );
+                       name = Frameworks;
+                       sourceTree = "<group>";
+               };
+               F96D19A41D9363B7007AF3CE /* dyld3 */ = {
+                       isa = PBXGroup;
+                       children = (
+                               F99986FA1F198C0C00D523F5 /* dyld-potential-framework-overrides */,
+                               F9DDEDA91E28785800A753DC /* closured */,
+                               F98692161DC3EF7700CBEDE6 /* shared-cache */,
+                               F977DDC91E53BEA700609230 /* SharedCacheRuntime.cpp */,
+                               F977DDCA1E53BEA700609230 /* SharedCacheRuntime.h */,
+                               F9E5E2C41EB00A870013A0BB /* ClosureBuffer.cpp */,
+                               F9E5E2C51EB00A870013A0BB /* ClosureBuffer.h */,
+                               F9C275581DA71A13007A5D8A /* Loading.cpp */,
+                               F9C275591DA71A13007A5D8A /* Loading.h */,
+                               F97C61A01D9CA6B800A84CD7 /* Logging.cpp */,
+                               F97C61A11D9CA6B800A84CD7 /* Logging.h */,
+                               37D7DAFE1E96F0ED00D52CEA /* Tracing.cpp */,
+                               37D7DAFF1E96F0ED00D52CEA /* Tracing.h */,
+                               F98692221DC4028B00CBEDE6 /* CodeSigningTypes.h */,
+                               F97C619D1D96C5BE00A84CD7 /* libdyldEntryVector.h */,
+                               F97C619E1D98292700A84CD7 /* libdyldEntryVector.cpp */,
+                               F986921B1DC3F07C00CBEDE6 /* Diagnostics.cpp */,
+                               F98692001DC3EF4800CBEDE6 /* Diagnostics.h */,
+                               F98692021DC3EF4800CBEDE6 /* LaunchCache.h */,
+                               F98692041DC3EF4800CBEDE6 /* LaunchCacheFormat.h */,
+                               F98692051DC3EF4800CBEDE6 /* LaunchCachePrinter.cpp */,
+                               F98692061DC3EF4800CBEDE6 /* LaunchCacheReader.cpp */,
+                               F98692071DC3EF4800CBEDE6 /* LaunchCacheWriter.cpp */,
+                               F98692081DC3EF4800CBEDE6 /* LaunchCacheWriter.h */,
+                               F9F76FAE1E08CFF200828678 /* PathOverrides.cpp */,
+                               F9F76FAF1E08CFF200828678 /* PathOverrides.h */,
+                               F96D19C11D95C6D6007AF3CE /* APIs.h */,
+                               F96D19A51D9363D6007AF3CE /* APIs.cpp */,
+                               F9C15A491E1F7D960006E570 /* APIs_macOS.cpp */,
+                               F96D19A71D9363D6007AF3CE /* AllImages.h */,
+                               F96D19A61D9363D6007AF3CE /* AllImages.cpp */,
+                               F9B3CAED1EEB5D0D00C9A48B /* DyldCacheParser.h */,
+                               F9B3CAEB1EEB5CFA00C9A48B /* DyldCacheParser.cpp */,
+                               F96D19A91D94576E007AF3CE /* MachOParser.h */,
+                               F96D19BE1D94A6DC007AF3CE /* MachOParser.cpp */,
+                               F922C81B1F33B86300D8F479 /* libclosured-stub.cpp */,
+                       );
+                       name = dyld3;
+                       sourceTree = "<group>";
+               };
                F971DD121A4A0E0700BBDD52 /* configs */ = {
                        isa = PBXGroup;
                        children = (
                F971DD121A4A0E0700BBDD52 /* configs */ = {
                        isa = PBXGroup;
                        children = (
                                F971DD141A4A0E0700BBDD52 /* dyld.xcconfig */,
                                F971DD151A4A0E0700BBDD52 /* libdyld.xcconfig */,
                                F971DD161A4A0E0700BBDD52 /* update_dyld_shared_cache.xcconfig */,
                                F971DD141A4A0E0700BBDD52 /* dyld.xcconfig */,
                                F971DD151A4A0E0700BBDD52 /* libdyld.xcconfig */,
                                F971DD161A4A0E0700BBDD52 /* update_dyld_shared_cache.xcconfig */,
+                               F94182DE1E60FFDC00D8EF25 /* update_dyld_sim_shared_cache.xcconfig */,
+                               F9EDC0A01F0481B400B030F4 /* closured.xcconfig */,
                        );
                        path = configs;
                        sourceTree = SOURCE_ROOT;
                        );
                        path = configs;
                        sourceTree = SOURCE_ROOT;
                        path = nocr;
                        sourceTree = "<group>";
                };
                        path = nocr;
                        sourceTree = "<group>";
                };
-               F9D0FE6C1A367093001E839B /* interlinked-dylibs */ = {
+               F98692161DC3EF7700CBEDE6 /* shared-cache */ = {
                        isa = PBXGroup;
                        children = (
                        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;
+                               37908A2A1E3A85A4009613FA /* FileAbstraction.hpp */,
+                               37908A2B1E3A85A4009613FA /* MachOFileAbstraction.hpp */,
+                               F986921E1DC3F86C00CBEDE6 /* dyld_cache_format.h */,
+                               F98692091DC3EF6C00CBEDE6 /* AdjustDylibSegments.cpp */,
+                               37C5C2FC1E5CD154006B32C9 /* BuilderUtils.h */,
+                               37C5C2FB1E5CD154006B32C9 /* BuilderUtils.mm */,
+                               F986921D1DC3F86C00CBEDE6 /* CacheBuilder.h */,
+                               F986921C1DC3F86C00CBEDE6 /* CacheBuilder.cpp */,
+                               F986920C1DC3EF6C00CBEDE6 /* DyldSharedCache.h */,
+                               F98692141DC3EF6C00CBEDE6 /* DyldSharedCache.cpp */,
+                               F986920E1DC3EF6C00CBEDE6 /* FileUtils.h */,
+                               F986920D1DC3EF6C00CBEDE6 /* FileUtils.cpp */,
+                               F963546A1DD8D8D300895049 /* ImageProxy.h */,
+                               F963546B1DD8F2A800895049 /* ImageProxy.cpp */,
+                               37908A2C1E3A85A4009613FA /* Manifest.h */,
+                               37908A281E3A853E009613FA /* Manifest.mm */,
+                               F986920F1DC3EF6C00CBEDE6 /* ObjC1Abstraction.hpp */,
+                               F98692101DC3EF6C00CBEDE6 /* ObjC2Abstraction.hpp */,
+                               F98692111DC3EF6C00CBEDE6 /* OptimizerBranches.cpp */,
+                               F98692121DC3EF6C00CBEDE6 /* OptimizerLinkedit.cpp */,
+                               F98692131DC3EF6C00CBEDE6 /* OptimizerObjC.cpp */,
+                               F902031F1DEE83C000AC3F76 /* StringUtils.h */,
+                               37908A2D1E3A85A4009613FA /* Trie.hpp */,
+                               F9D862441DC9759C000A199A /* dyld_closure_util.cpp */,
+                               37908A271E3A853E009613FA /* dyld_shared_cache_builder.mm */,
+                               37908A291E3A853E009613FA /* multi_dyld_shared_cache_builder.mm */,
+                               F9C15A451E19C2F50006E570 /* make_ios_dyld_cache.cpp */,
+                               F963542E1DCD736000895049 /* update_dyld_sim_shared_cache.cpp */,
+                               F98692151DC3EF6C00CBEDE6 /* update_dyld_shared_cache.cpp */,
+                               F9EDC09E1F04767300B030F4 /* update_dyld_shared_cache_entitlements.plist */,
+                       );
+                       name = "shared-cache";
+                       sourceTree = "<group>";
+               };
+               F9DDEDA91E28785800A753DC /* closured */ = {
+                       isa = PBXGroup;
+                       children = (
+                               F9DDEDAA1E28787900A753DC /* closured.cpp */,
+                               F9EDC09F1F0478A300B030F4 /* closured_entitlements.plist */,
+                               F9DDEDAB1E28787900A753DC /* closuredProtocol.defs */,
+                               F9DDEDAC1E28787900A753DC /* closuredtypes.h */,
+                               F913C8501E9312A100458AA3 /* com.apple.dyld.closured.sb */,
+                               F9DDEDAD1E28787900A753DC /* com.apple.dyld.closured.plist */,
+                       );
+                       name = closured;
+                       sourceTree = "<group>";
                };
                F9ED4C870630A72200DF4E74 = {
                        isa = PBXGroup;
                        children = (
                };
                F9ED4C870630A72200DF4E74 = {
                        isa = PBXGroup;
                        children = (
+                               F988F0BA1E2FDF5B003AED79 /* execserver.defs */,
                                F9F6F4261C1FAF8000BD8FED /* testing */,
                                F971DD121A4A0E0700BBDD52 /* configs */,
                                F9ED4CBB0630A7AA00DF4E74 /* src */,
                                F9F6F4261C1FAF8000BD8FED /* testing */,
                                F971DD121A4A0E0700BBDD52 /* configs */,
                                F9ED4CBB0630A7AA00DF4E74 /* src */,
                                F9ED4CBE0630A7B100DF4E74 /* include */,
                                F97FF3571C23638F000ACDD2 /* nocr */,
                                F9ED4C990630A76000DF4E74 /* Products */,
                                F9ED4CBE0630A7B100DF4E74 /* include */,
                                F97FF3571C23638F000ACDD2 /* nocr */,
                                F9ED4C990630A76000DF4E74 /* Products */,
-                               F9D0FE6C1A367093001E839B /* interlinked-dylibs */,
+                               F96D19A41D9363B7007AF3CE /* dyld3 */,
                                F939373D0A94FC4700070A07 /* launch-cache */,
                                F939373D0A94FC4700070A07 /* launch-cache */,
+                               F94C22231E513CA90079E5DD /* Frameworks */,
                        );
                        indentWidth = 4;
                        sourceTree = "<group>";
                        tabWidth = 4;
                        );
                        indentWidth = 4;
                        sourceTree = "<group>";
                        tabWidth = 4;
-                       usesTabs = 1;
+                       usesTabs = 0;
                };
                F9ED4C990630A76000DF4E74 /* Products */ = {
                        isa = PBXGroup;
                };
                F9ED4C990630A76000DF4E74 /* Products */ = {
                        isa = PBXGroup;
                                377686021AC4B27D00026E6C /* multi_dyld_shared_cache_builder */,
                                3703A1241B38C1B300ADBA7F /* dyld_shared_cache_builder */,
                                F97FF3561C23638F000ACDD2 /* nocr */,
                                377686021AC4B27D00026E6C /* multi_dyld_shared_cache_builder */,
                                3703A1241B38C1B300ADBA7F /* dyld_shared_cache_builder */,
                                F97FF3561C23638F000ACDD2 /* nocr */,
+                               F97C61A71DBAD1A900A84CD7 /* dyld_closure_util */,
+                               F96354451DCD74A400895049 /* update_dyld_sim_shared_cache */,
+                               F9DDEDB21E2878CA00A753DC /* closured */,
+                               F9AB02B81F329FAA00EE96C4 /* libclosured.dylib */,
+                               F922C8171F33B73800D8F479 /* libclosured.dylib */,
                        );
                        name = Products;
                        sourceTree = "<group>";
                        );
                        name = Products;
                        sourceTree = "<group>";
                F9ED4CBB0630A7AA00DF4E74 /* src */ = {
                        isa = PBXGroup;
                        children = (
                F9ED4CBB0630A7AA00DF4E74 /* src */ = {
                        isa = PBXGroup;
                        children = (
-                               F97FF35E1C236402000ACDD2 /* execserver.defs */,
                                F97FF35F1C236402000ACDD2 /* nocr.c */,
                                F9ED4CC60630A7F100DF4E74 /* dyld_gdb.cpp */,
                                F9ED4CC70630A7F100DF4E74 /* dyld.cpp */,
                                F97FF35F1C236402000ACDD2 /* nocr.c */,
                                F9ED4CC60630A7F100DF4E74 /* dyld_gdb.cpp */,
                                F9ED4CC70630A7F100DF4E74 /* dyld.cpp */,
                                F94DB9010F0A9B1700323715 /* ImageLoaderMachOClassic.h */,
                                F94DB9020F0A9B1700323715 /* ImageLoaderMachOCompressed.cpp */,
                                F94DB9030F0A9B1700323715 /* ImageLoaderMachOCompressed.h */,
                                F94DB9010F0A9B1700323715 /* ImageLoaderMachOClassic.h */,
                                F94DB9020F0A9B1700323715 /* ImageLoaderMachOCompressed.cpp */,
                                F94DB9030F0A9B1700323715 /* ImageLoaderMachOCompressed.h */,
-                               F9280B791AB9DCA000B18AEC /* ImageLoaderMegaDylib.cpp */,
                                F9280B7A1AB9DCA000B18AEC /* ImageLoaderMegaDylib.h */,
                                F9280B7A1AB9DCA000B18AEC /* ImageLoaderMegaDylib.h */,
+                               F9280B791AB9DCA000B18AEC /* ImageLoaderMegaDylib.cpp */,
                                F99DE0361AAE4F0400669496 /* libdyld_data_symbols.dirty */,
                                F9ED4CD50630A7F100DF4E74 /* stub_binding_helper.s */,
                                F9B01E3D0739ABDE00CF981B /* dyld.exp */,
                                F99DE0361AAE4F0400669496 /* libdyld_data_symbols.dirty */,
                                F9ED4CD50630A7F100DF4E74 /* stub_binding_helper.s */,
                                F9B01E3D0739ABDE00CF981B /* dyld.exp */,
                        children = (
                                F96D19711D7F63EE007AF3CE /* expand.pl */,
                                F95090D01C5AB89A0031F81D /* dyld_process_info.h */,
                        children = (
                                F96D19711D7F63EE007AF3CE /* expand.pl */,
                                F95090D01C5AB89A0031F81D /* dyld_process_info.h */,
-                               F918691408B16D2500E0F9DB /* dyld-interposing.h */,
                                F98D274C0AA79D7400416316 /* dyld_images.h */,
                                F98D274C0AA79D7400416316 /* dyld_images.h */,
+                               F918691408B16D2500E0F9DB /* dyld-interposing.h */,
+                               F9ED4CEA0630A80600DF4E74 /* dyld.h */,
                                F9ED4CE80630A80600DF4E74 /* dyld_gdb.h */,
                                F9ED4CE90630A80600DF4E74 /* dyld_priv.h */,
                                F9ED4CE80630A80600DF4E74 /* dyld_gdb.h */,
                                F9ED4CE90630A80600DF4E74 /* dyld_priv.h */,
-                               F9ED4CEA0630A80600DF4E74 /* dyld.h */,
                                F99EE6AE06B48D4200BF1992 /* dlfcn.h */,
                                F9C69EFD14EC8ABF009CAE2E /* objc-shared-cache.h */,
                        );
                                F99EE6AE06B48D4200BF1992 /* dlfcn.h */,
                                F9C69EFD14EC8ABF009CAE2E /* objc-shared-cache.h */,
                        );
                };
 /* End PBXGroup section */
 
                };
 /* End PBXGroup section */
 
+/* Begin PBXHeadersBuildPhase section */
+               F922C8151F33B73800D8F479 /* Headers */ = {
+                       isa = PBXHeadersBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               F98F1FBB1E4029CA00EF868D /* Headers */ = {
+                       isa = PBXHeadersBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               F99006DD1E411BA70013456D /* dyld_images.h in Headers */,
+                               F99006DE1E411BBC0013456D /* dyld.h in Headers */,
+                               F98F1FBD1E4029E400EF868D /* dyld_priv.h in Headers */,
+                               F960A78A1E40569400840176 /* dyld-interposing.h in Headers */,
+                               F98F1FBF1E4031F800EF868D /* dyld_process_info.h in Headers */,
+                               F99006E01E4130AE0013456D /* dyld_gdb.h in Headers */,
+                               F960A78B1E405DE300840176 /* dyld_cache_format.h in Headers */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               F9AB02B61F329FAA00EE96C4 /* Headers */ = {
+                       isa = PBXHeadersBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+/* End PBXHeadersBuildPhase section */
+
 /* Begin PBXNativeTarget section */
                3703A1111B38C1B300ADBA7F /* dyld_shared_cache_builder */ = {
                        isa = PBXNativeTarget;
 /* Begin PBXNativeTarget section */
                3703A1111B38C1B300ADBA7F /* dyld_shared_cache_builder */ = {
                        isa = PBXNativeTarget;
                        productReference = 377686021AC4B27D00026E6C /* multi_dyld_shared_cache_builder */;
                        productType = "com.apple.product-type.tool";
                };
                        productReference = 377686021AC4B27D00026E6C /* multi_dyld_shared_cache_builder */;
                        productType = "com.apple.product-type.tool";
                };
+               F922C8161F33B73800D8F479 /* libclosured-stub */ = {
+                       isa = PBXNativeTarget;
+                       buildConfigurationList = F922C8181F33B73800D8F479 /* Build configuration list for PBXNativeTarget "libclosured-stub" */;
+                       buildPhases = (
+                               F922C8131F33B73800D8F479 /* Sources */,
+                               F922C8141F33B73800D8F479 /* Frameworks */,
+                               F922C8151F33B73800D8F479 /* Headers */,
+                       );
+                       buildRules = (
+                       );
+                       dependencies = (
+                       );
+                       name = "libclosured-stub";
+                       productName = "libclosured-stub";
+                       productReference = F922C8171F33B73800D8F479 /* libclosured.dylib */;
+                       productType = "com.apple.product-type.library.dynamic";
+               };
                F93937310A94FAF700070A07 /* update_dyld_shared_cache_tool */ = {
                        isa = PBXNativeTarget;
                        buildConfigurationList = F93937340A94FB2900070A07 /* Build configuration list for PBXNativeTarget "update_dyld_shared_cache_tool" */;
                F93937310A94FAF700070A07 /* update_dyld_shared_cache_tool */ = {
                        isa = PBXNativeTarget;
                        buildConfigurationList = F93937340A94FB2900070A07 /* Build configuration list for PBXNativeTarget "update_dyld_shared_cache_tool" */;
                                F93937300A94FAF700070A07 /* Frameworks */,
                                F9D238DD0A9E2FEE002B55C7 /* usr|share|man|man1 */,
                                F991E3030FF1A4EC0082CCC9 /* do not install duplicates */,
                                F93937300A94FAF700070A07 /* Frameworks */,
                                F9D238DD0A9E2FEE002B55C7 /* usr|share|man|man1 */,
                                F991E3030FF1A4EC0082CCC9 /* do not install duplicates */,
+                               F981C8C21F058F8200452F35 /* mkdir /var/db/dyld */,
                        );
                        buildRules = (
                        );
                        );
                        buildRules = (
                        );
                        productReference = F93937320A94FAF700070A07 /* update_dyld_shared_cache */;
                        productType = "com.apple.product-type.tool";
                };
                        productReference = F93937320A94FAF700070A07 /* update_dyld_shared_cache */;
                        productType = "com.apple.product-type.tool";
                };
+               F963542F1DCD74A400895049 /* update_dyld_sim_shared_cache */ = {
+                       isa = PBXNativeTarget;
+                       buildConfigurationList = F96354421DCD74A400895049 /* Build configuration list for PBXNativeTarget "update_dyld_sim_shared_cache" */;
+                       buildPhases = (
+                               F96354301DCD74A400895049 /* create dyld_cache_config.h */,
+                               F96354311DCD74A400895049 /* Sources */,
+                               F963543D1DCD74A400895049 /* Frameworks */,
+                       );
+                       buildRules = (
+                       );
+                       dependencies = (
+                       );
+                       name = update_dyld_sim_shared_cache;
+                       productName = update_dyld_shared_cache;
+                       productReference = F96354451DCD74A400895049 /* update_dyld_sim_shared_cache */;
+                       productType = "com.apple.product-type.tool";
+               };
+               F97C61A61DBAD1A900A84CD7 /* dyld_closure_util */ = {
+                       isa = PBXNativeTarget;
+                       buildConfigurationList = F97C61AB1DBAD1A900A84CD7 /* Build configuration list for PBXNativeTarget "dyld_closure_util" */;
+                       buildPhases = (
+                               F97C61A31DBAD1A900A84CD7 /* Sources */,
+                               F97C61A41DBAD1A900A84CD7 /* Frameworks */,
+                               F97C61A51DBAD1A900A84CD7 /* Copy Files */,
+                       );
+                       buildRules = (
+                       );
+                       dependencies = (
+                       );
+                       name = dyld_closure_util;
+                       productName = dyld_closure_util;
+                       productReference = F97C61A71DBAD1A900A84CD7 /* dyld_closure_util */;
+                       productType = "com.apple.product-type.tool";
+               };
                F97FF3551C23638F000ACDD2 /* nocr */ = {
                        isa = PBXNativeTarget;
                        buildConfigurationList = F97FF35C1C23638F000ACDD2 /* Build configuration list for PBXNativeTarget "nocr" */;
                F97FF3551C23638F000ACDD2 /* nocr */ = {
                        isa = PBXNativeTarget;
                        buildConfigurationList = F97FF35C1C23638F000ACDD2 /* Build configuration list for PBXNativeTarget "nocr" */;
                        productReference = F99B8E670FEC121100701838 /* dyld_shared_cache_util */;
                        productType = "com.apple.product-type.tool";
                };
                        productReference = F99B8E670FEC121100701838 /* dyld_shared_cache_util */;
                        productType = "com.apple.product-type.tool";
                };
+               F9AB02B71F329FAA00EE96C4 /* libclosured */ = {
+                       isa = PBXNativeTarget;
+                       buildConfigurationList = F9AB02C21F329FAA00EE96C4 /* Build configuration list for PBXNativeTarget "libclosured" */;
+                       buildPhases = (
+                               F9AB02B41F329FAA00EE96C4 /* Sources */,
+                               F9AB02B51F329FAA00EE96C4 /* Frameworks */,
+                               F9AB02B61F329FAA00EE96C4 /* Headers */,
+                       );
+                       buildRules = (
+                       );
+                       dependencies = (
+                       );
+                       name = libclosured;
+                       productName = libclosured;
+                       productReference = F9AB02B81F329FAA00EE96C4 /* libclosured.dylib */;
+                       productType = "com.apple.product-type.library.dynamic";
+               };
                F9D1001114D8D0BA00099D91 /* dsc_extractor */ = {
                        isa = PBXNativeTarget;
                        buildConfigurationList = F9D1001714D8D0F100099D91 /* Build configuration list for PBXNativeTarget "dsc_extractor" */;
                F9D1001114D8D0BA00099D91 /* dsc_extractor */ = {
                        isa = PBXNativeTarget;
                        buildConfigurationList = F9D1001714D8D0F100099D91 /* Build configuration list for PBXNativeTarget "dsc_extractor" */;
                        productReference = F9D1001214D8D0BA00099D91 /* dsc_extractor.bundle */;
                        productType = "com.apple.product-type.library.dynamic";
                };
                        productReference = F9D1001214D8D0BA00099D91 /* dsc_extractor.bundle */;
                        productType = "com.apple.product-type.library.dynamic";
                };
+               F9DDEDB11E2878CA00A753DC /* closured */ = {
+                       isa = PBXNativeTarget;
+                       buildConfigurationList = F9DDEDB61E2878CB00A753DC /* Build configuration list for PBXNativeTarget "closured" */;
+                       buildPhases = (
+                               F9DDEDAE1E2878CA00A753DC /* Sources */,
+                               F9DDEDAF1E2878CA00A753DC /* Frameworks */,
+                               F94942B01E6794650019AE08 /* installl plist */,
+                               F94942B11E67965C0019AE08 /* install man page */,
+                               F913C8511E93137700458AA3 /* Install sandbox profile */,
+                       );
+                       buildRules = (
+                       );
+                       dependencies = (
+                       );
+                       name = closured;
+                       productName = closured;
+                       productReference = F9DDEDB21E2878CA00A753DC /* closured */;
+                       productType = "com.apple.product-type.tool";
+               };
                F9ED4C970630A76000DF4E74 /* dyld */ = {
                        isa = PBXNativeTarget;
                        buildConfigurationList = F9D8C7DD087B087300E93EFB /* Build configuration list for PBXNativeTarget "dyld" */;
                F9ED4C970630A76000DF4E74 /* dyld */ = {
                        isa = PBXNativeTarget;
                        buildConfigurationList = F9D8C7DD087B087300E93EFB /* Build configuration list for PBXNativeTarget "dyld" */;
                                F921D3160703769A000D1056 /* PBXBuildRule */,
                        );
                        dependencies = (
                                F921D3160703769A000D1056 /* PBXBuildRule */,
                        );
                        dependencies = (
+                               F922C8121F33B62700D8F479 /* PBXTargetDependency */,
                                F99B8EB20FEC220C00701838 /* PBXTargetDependency */,
                                F99B8EB20FEC220C00701838 /* PBXTargetDependency */,
+                               F96543A11E343601003C5540 /* PBXTargetDependency */,
                        );
                        name = dyld;
                        productName = dyld;
                        );
                        name = dyld;
                        productName = dyld;
                        buildPhases = (
                                F9ED4C9C0630A76B00DF4E74 /* Sources */,
                                F959621018849DF20003E4D4 /* add dyld symlink */,
                        buildPhases = (
                                F9ED4C9C0630A76B00DF4E74 /* Sources */,
                                F959621018849DF20003E4D4 /* add dyld symlink */,
+                               F98F1FBB1E4029CA00EF868D /* Headers */,
+                               F960A78C1E405E2300840176 /* expand dyld_priv.h macros */,
+                               F99006DF1E411C500013456D /* install dlfcn.h */,
                        );
                        buildRules = (
                                F921D31E070376F1000D1056 /* PBXBuildRule */,
                                F9574C4906C94DA700142BFA /* PBXBuildRule */,
                        );
                        dependencies = (
                        );
                        buildRules = (
                                F921D31E070376F1000D1056 /* PBXBuildRule */,
                                F9574C4906C94DA700142BFA /* PBXBuildRule */,
                        );
                        dependencies = (
+                               F922C81E1F33B96300D8F479 /* PBXTargetDependency */,
                        );
                        name = libdyld.dylib;
                        productName = libdyld;
                        );
                        name = libdyld.dylib;
                        productName = libdyld;
                F9ED4C8B0630A72300DF4E74 /* Project object */ = {
                        isa = PBXProject;
                        attributes = {
                F9ED4C8B0630A72300DF4E74 /* Project object */ = {
                        isa = PBXProject;
                        attributes = {
-                               LastUpgradeCheck = 0630;
+                               LastUpgradeCheck = 0900;
                                TargetAttributes = {
                                        37A0AD0A1C15FFF500731E50 = {
                                TargetAttributes = {
                                        37A0AD0A1C15FFF500731E50 = {
-                                               CreatedOnToolsVersion = 7.0;
+                                               CreatedOnToolsVersion = 8.0;
+                                       };
+                                       F922C8161F33B73800D8F479 = {
+                                               CreatedOnToolsVersion = 9.0;
+                                       };
+                                       F97C61A61DBAD1A900A84CD7 = {
+                                               CreatedOnToolsVersion = 8.0;
+                                               DevelopmentTeam = 59GAB85EFG;
+                                               ProvisioningStyle = Automatic;
                                        };
                                        F97FF3551C23638F000ACDD2 = {
                                        };
                                        F97FF3551C23638F000ACDD2 = {
-                                               CreatedOnToolsVersion = 7.1;
+                                               CreatedOnToolsVersion = 8.0;
+                                       };
+                                       F9AB02B71F329FAA00EE96C4 = {
+                                               CreatedOnToolsVersion = 9.0;
+                                       };
+                                       F9DDEDB11E2878CA00A753DC = {
+                                               CreatedOnToolsVersion = 8.2;
+                                               DevelopmentTeam = 59GAB85EFG;
+                                               ProvisioningStyle = Automatic;
                                        };
                                        F9F6F4271C1FB0A700BD8FED = {
                                        };
                                        F9F6F4271C1FB0A700BD8FED = {
-                                               CreatedOnToolsVersion = 7.1;
+                                               CreatedOnToolsVersion = 8.0;
                                        };
                                };
                        };
                                        };
                                };
                        };
                        targets = (
                                F9ED4C920630A73900DF4E74 /* all */,
                                37A0AD0A1C15FFF500731E50 /* update_dyld_shared_cache */,
                        targets = (
                                F9ED4C920630A73900DF4E74 /* all */,
                                37A0AD0A1C15FFF500731E50 /* update_dyld_shared_cache */,
-                               F9ED4C970630A76000DF4E74 /* dyld */,
                                F908134211D3ED0B00626CC1 /* libdyld */,
                                F908134211D3ED0B00626CC1 /* libdyld */,
+                               F9ED4C970630A76000DF4E74 /* dyld */,
+                               F9DDEDB11E2878CA00A753DC /* closured */,
                                F9ED4C9E0630A76B00DF4E74 /* libdyld.dylib */,
                                F93937310A94FAF700070A07 /* update_dyld_shared_cache_tool */,
                                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 */,
                                377685F21AC4B27D00026E6C /* multi_dyld_shared_cache_builder */,
                                3703A1111B38C1B300ADBA7F /* dyld_shared_cache_builder */,
+                               F963542F1DCD74A400895049 /* update_dyld_sim_shared_cache */,
+                               F99B8E550FEC10F600701838 /* dyld_shared_cache_util */,
+                               F97C61A61DBAD1A900A84CD7 /* dyld_closure_util */,
+                               F9F2A5580F7AEE9800B7C9EB /* libdsc */,
+                               F9D1001114D8D0BA00099D91 /* dsc_extractor */,
                                F9F6F4271C1FB0A700BD8FED /* dyld_tests */,
                                F97FF3551C23638F000ACDD2 /* nocr */,
                                F9F6F4271C1FB0A700BD8FED /* dyld_tests */,
                                F97FF3551C23638F000ACDD2 /* nocr */,
+                               F9AB02B71F329FAA00EE96C4 /* libclosured */,
+                               F922C8161F33B73800D8F479 /* libclosured-stub */,
                        );
                };
 /* End PBXProject section */
                        );
                };
 /* End PBXProject section */
                        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;
                };
                        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;
                };
+               F913C8511E93137700458AA3 /* Install sandbox profile */ = {
+                       isa = PBXShellScriptBuildPhase;
+                       buildActionMask = 8;
+                       files = (
+                       );
+                       inputPaths = (
+                               "$(SRCROOT)/dyld3/closured/com.apple.dyld.closured.sb",
+                       );
+                       name = "Install sandbox profile";
+                       outputPaths = (
+                               "${DSTROOT}/System/Library/Sandbox/Profiles/com.apple.dyld.closured.sb",
+                       );
+                       runOnlyForDeploymentPostprocessing = 1;
+                       shellPath = /bin/sh;
+                       shellScript = "if [ ${OS} = \"MACOS\" ]; then\n    mkdir -p ${DSTROOT}/System/Library/Sandbox/Profiles\n    cp ${SRCROOT}/dyld3/closured/com.apple.dyld.closured.sb ${DSTROOT}/System/Library/Sandbox/Profiles/com.apple.dyld.closured.sb\nfi";
+                       showEnvVarsInLog = 0;
+               };
                F9213B3F18BFC9CB001CB6E8 /* simulator entitlement */ = {
                        isa = PBXShellScriptBuildPhase;
                        buildActionMask = 8;
                F9213B3F18BFC9CB001CB6E8 /* simulator entitlement */ = {
                        isa = PBXShellScriptBuildPhase;
                        buildActionMask = 8;
                        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;
                };
                        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;
                };
+               F94182D61E60E74E00D8EF25 /* pre-platform builds */ = {
+                       isa = PBXShellScriptBuildPhase;
+                       buildActionMask = 8;
+                       files = (
+                       );
+                       inputPaths = (
+                       );
+                       name = "pre-platform builds";
+                       outputPaths = (
+                       );
+                       runOnlyForDeploymentPostprocessing = 1;
+                       shellPath = /bin/sh;
+                       shellScript = "xcodebuild install -target multi_dyld_shared_cache_builder SDKROOT=\"${SDKROOT}\" MACOSX_DEPLOYMENT_TARGET=${MACOSX_DEPLOYMENT_TARGET} OBJROOT=\"${OBJROOT}\" SRCROOT=\"${SRCROOT}\" DSTROOT=\"${DSTROOT}\" SYMROOT=\"${SYMROOT}\" RC_ProjectSourceVersion=\"${RC_ProjectSourceVersion}\"\n\nif [ \"${RC_PURPLE}\" = \"YES\" ]\nthen\n\txcodebuild install -target dyld_shared_cache_builder SDKROOT=\"${SDKROOT}\" MACOSX_DEPLOYMENT_TARGET=${MACOSX_DEPLOYMENT_TARGET} OBJROOT=\"${OBJROOT}\" SRCROOT=\"${SRCROOT}\" DSTROOT=\"${DSTROOT}\" SYMROOT=\"${SYMROOT}\" RC_ProjectSourceVersion=\"${RC_ProjectSourceVersion}\"\n\txcodebuild install -target update_dyld_sim_shared_cache SDKROOT=\"${SDKROOT}\" MACOSX_DEPLOYMENT_TARGET=${MACOSX_DEPLOYMENT_TARGET} OBJROOT=\"${OBJROOT}\" SRCROOT=\"${SRCROOT}\" DSTROOT=\"${DSTROOT}\" SYMROOT=\"${SYMROOT}\" RC_ProjectSourceVersion=\"${RC_ProjectSourceVersion}\"\nelse\n\txcodebuild install -target update_dyld_shared_cache_tool SDKROOT=\"${SDKROOT}\" MACOSX_DEPLOYMENT_TARGET=${MACOSX_DEPLOYMENT_TARGET} OBJROOT=\"${OBJROOT}\" SRCROOT=\"${SRCROOT}\" DSTROOT=\"${DSTROOT}\" SYMROOT=\"${SYMROOT}\" RC_ProjectSourceVersion=\"${RC_ProjectSourceVersion}\"\nfi";
+                       showEnvVarsInLog = 0;
+               };
+               F94942B01E6794650019AE08 /* installl plist */ = {
+                       isa = PBXShellScriptBuildPhase;
+                       buildActionMask = 8;
+                       files = (
+                       );
+                       inputPaths = (
+                               "$(SRCROOT)/dyld3/closured/com.apple.dyld.closured.plist",
+                       );
+                       name = "installl plist";
+                       outputPaths = (
+                               "${DSTROOT}/System/Library/LaunchDaemons/com.apple.dyld.closured.plist",
+                       );
+                       runOnlyForDeploymentPostprocessing = 1;
+                       shellPath = /bin/sh;
+                       shellScript = "mkdir -p ${DSTROOT}/System/Library/LaunchDaemons\nplutil -convert binary1 -o ${DSTROOT}/System/Library/LaunchDaemons/com.apple.dyld.closured.plist ${SRCROOT}/dyld3/closured/com.apple.dyld.closured.plist";
+                       showEnvVarsInLog = 0;
+               };
                F959621018849DF20003E4D4 /* add dyld symlink */ = {
                        isa = PBXShellScriptBuildPhase;
                        buildActionMask = 8;
                F959621018849DF20003E4D4 /* add dyld symlink */ = {
                        isa = PBXShellScriptBuildPhase;
                        buildActionMask = 8;
                        shellScript = "if [[ \"${PLATFORM_NAME}\"  == *simulator* ]]\nthen\n\tcd ${DSTROOT}/${INSTALL_PATH}\n\tln -s libdyld.dylib libdyld_sim.dylib\nfi\n";
                        showEnvVarsInLog = 0;
                };
                        shellScript = "if [[ \"${PLATFORM_NAME}\"  == *simulator* ]]\nthen\n\tcd ${DSTROOT}/${INSTALL_PATH}\n\tln -s libdyld.dylib libdyld_sim.dylib\nfi\n";
                        showEnvVarsInLog = 0;
                };
-               F96D19701D7F62C3007AF3CE /* Install dyld_priv.h */ = {
+               F960A78C1E405E2300840176 /* expand dyld_priv.h macros */ = {
                        isa = PBXShellScriptBuildPhase;
                        buildActionMask = 8;
                        files = (
                        );
                        inputPaths = (
                        isa = PBXShellScriptBuildPhase;
                        buildActionMask = 8;
                        files = (
                        );
                        inputPaths = (
+                               "${SRCROOT}/include/mach-o/dyld_priv.h",
                        );
                        );
-                       name = "Install dyld_priv.h";
+                       name = "expand dyld_priv.h macros";
                        outputPaths = (
                        outputPaths = (
+                               "${DSTROOT}/usr/local/include/mach-o/dyld_priv.h",
                        );
                        runOnlyForDeploymentPostprocessing = 1;
                        shellPath = /bin/sh;
                        );
                        runOnlyForDeploymentPostprocessing = 1;
                        shellPath = /bin/sh;
-                       shellScript = "${SRCROOT}/bin/expand.pl < ${SRCROOT}/include/mach-o/dyld_priv.h >  ${DSTROOT}/${INSTALL_PATH_PREFIX}/usr/local/include/mach-o/dyld_priv.h\n";
+                       shellScript = "${SRCROOT}/bin/expand.pl < ${SRCROOT}/include/mach-o/dyld_priv.h >  ${DSTROOT}/usr/local/include/mach-o/dyld_priv.h\n";
+                       showEnvVarsInLog = 0;
+               };
+               F96354301DCD74A400895049 /* 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;
                };
                F96D19A31D91D733007AF3CE /* make dyld_priv.h */ = {
                        showEnvVarsInLog = 0;
                };
                F96D19A31D91D733007AF3CE /* make dyld_priv.h */ = {
                        shellScript = "mkdir -p ${DERIVED_FILE_DIR}/mach-o\n${SRCROOT}/bin/expand.pl < ${SRCROOT}/include/mach-o/dyld_priv.h >  ${DERIVED_FILE_DIR}/mach-o/dyld_priv.h\n";
                        showEnvVarsInLog = 0;
                };
                        shellScript = "mkdir -p ${DERIVED_FILE_DIR}/mach-o\n${SRCROOT}/bin/expand.pl < ${SRCROOT}/include/mach-o/dyld_priv.h >  ${DERIVED_FILE_DIR}/mach-o/dyld_priv.h\n";
                        showEnvVarsInLog = 0;
                };
-               F991E3030FF1A4EC0082CCC9 /* do not install duplicates */ = {
+               F981C8C21F058F8200452F35 /* mkdir /var/db/dyld */ = {
                        isa = PBXShellScriptBuildPhase;
                        buildActionMask = 8;
                        files = (
                        );
                        inputPaths = (
                        );
                        isa = PBXShellScriptBuildPhase;
                        buildActionMask = 8;
                        files = (
                        );
                        inputPaths = (
                        );
-                       name = "do not install duplicates";
+                       name = "mkdir /var/db/dyld";
                        outputPaths = (
                        );
                        runOnlyForDeploymentPostprocessing = 1;
                        shellPath = /bin/sh;
                        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";
+                       shellScript = "mkdir -p ${DSTROOT}/private/var/db/dyld";
                        showEnvVarsInLog = 0;
                };
                        showEnvVarsInLog = 0;
                };
-               F99B8EB60FEC236500701838 /* suppress macosx dyld_shared_cache_util */ = {
+               F99006DF1E411C500013456D /* install dlfcn.h */ = {
                        isa = PBXShellScriptBuildPhase;
                        buildActionMask = 8;
                        files = (
                        );
                        inputPaths = (
                        );
                        isa = PBXShellScriptBuildPhase;
                        buildActionMask = 8;
                        files = (
                        );
                        inputPaths = (
                        );
-                       name = "suppress macosx dyld_shared_cache_util";
+                       name = "install dlfcn.h";
                        outputPaths = (
                        );
                        runOnlyForDeploymentPostprocessing = 1;
                        shellPath = /bin/sh;
                        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";
+                       shellScript = "# xcode only lets you install public headers to one directory\ncp ${SRCROOT}/include/dlfcn.h  ${DSTROOT}/usr/include/\n";
                        showEnvVarsInLog = 0;
                };
                        showEnvVarsInLog = 0;
                };
-               F9D050C811DD701A00FB0A29 /* configure archives */ = {
+               F991E3030FF1A4EC0082CCC9 /* do not install duplicates */ = {
                        isa = PBXShellScriptBuildPhase;
                        isa = PBXShellScriptBuildPhase;
-                       buildActionMask = 12;
+                       buildActionMask = 8;
                        files = (
                        );
                        inputPaths = (
                        );
                        files = (
                        );
                        inputPaths = (
                        );
-                       name = "configure archives";
+                       name = "do not install duplicates";
                        outputPaths = (
                        outputPaths = (
-                               "$(DERIVED_SOURCES_DIR)/archives.txt",
                        );
                        );
-                       runOnlyForDeploymentPostprocessing = 0;
+                       runOnlyForDeploymentPostprocessing = 1;
                        shellPath = /bin/sh;
                        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";
+                       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";
                        showEnvVarsInLog = 0;
                };
                        showEnvVarsInLog = 0;
                };
-               F9F6F42B1C1FB0AE00BD8FED /* build */ = {
+               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\n    mkdir -p ${DSTROOT}/AppleInternal/Library/Preferences/\n    cp dyld3/dyld-potential-framework-overrides ${DSTROOT}/AppleInternal/Library/Preferences/\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 = (
                        isa = PBXShellScriptBuildPhase;
                        buildActionMask = 12;
                        files = (
                        isa = PBXSourcesBuildPhase;
                        buildActionMask = 2147483647;
                        files = (
                        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 */,
+                               37554F521E3F78EB00407388 /* ImageProxy.cpp in Sources */,
+                               37554F421E3F169600407388 /* CacheBuilder.cpp in Sources */,
+                               37554F481E3F16BA00407388 /* OptimizerBranches.cpp in Sources */,
+                               37554F441E3F16A900407388 /* OptimizerObjC.cpp in Sources */,
+                               37554F581E3F7B6500407388 /* PathOverrides.cpp in Sources */,
+                               37554F561E3F7B4300407388 /* LaunchCacheWriter.cpp in Sources */,
+                               37908A301E3ADD15009613FA /* Diagnostics.cpp in Sources */,
+                               37554F491E3F76E400407388 /* DyldSharedCache.cpp in Sources */,
+                               37908A311E3EB585009613FA /* MachOParser.cpp in Sources */,
+                               F922AE581EF0D3C300926F9D /* DyldCacheParser.cpp in Sources */,
+                               37C5C2FE1E5CD154006B32C9 /* BuilderUtils.mm in Sources */,
+                               37908A2F1E3A864E009613FA /* dyld_shared_cache_builder.mm in Sources */,
+                               37554F541E3F7B1F00407388 /* LaunchCacheReader.cpp in Sources */,
+                               37554F461E3F16B600407388 /* OptimizerLinkedit.cpp in Sources */,
+                               37908A321E3ED667009613FA /* FileUtils.cpp in Sources */,
+                               37908A2E1E3A8632009613FA /* Manifest.mm in Sources */,
+                               37554F4B1E3F76E900407388 /* AdjustDylibSegments.cpp in Sources */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                        isa = PBXSourcesBuildPhase;
                        buildActionMask = 2147483647;
                        files = (
                        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 */,
+                               37554F511E3F78EB00407388 /* ImageProxy.cpp in Sources */,
+                               37554F3F1E3F165100407388 /* Diagnostics.cpp in Sources */,
+                               37554F471E3F16B900407388 /* OptimizerBranches.cpp in Sources */,
+                               37554F451E3F16B500407388 /* OptimizerLinkedit.cpp in Sources */,
+                               37554F571E3F7B6400407388 /* PathOverrides.cpp in Sources */,
+                               37554F551E3F7B4200407388 /* LaunchCacheWriter.cpp in Sources */,
+                               37554F401E3F167A00407388 /* MachOParser.cpp in Sources */,
+                               F981C8C11EF06A7800452F35 /* DyldCacheParser.cpp in Sources */,
+                               37554F411E3F169500407388 /* CacheBuilder.cpp in Sources */,
+                               37554F431E3F16A800407388 /* OptimizerObjC.cpp in Sources */,
+                               37C5C2FD1E5CD154006B32C9 /* BuilderUtils.mm in Sources */,
+                               37554F3B1E3F0FD200407388 /* Manifest.mm in Sources */,
+                               37554F531E3F7B1E00407388 /* LaunchCacheReader.cpp in Sources */,
+                               37554F3C1E3F0FD200407388 /* DyldSharedCache.cpp in Sources */,
+                               37554F3D1E3F0FD200407388 /* FileUtils.cpp in Sources */,
+                               37554F3E1E3F0FD200407388 /* multi_dyld_shared_cache_builder.mm in Sources */,
+                               37554F4A1E3F76E800407388 /* AdjustDylibSegments.cpp in Sources */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               F922C8131F33B73800D8F479 /* Sources */ = {
+                       isa = PBXSourcesBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               F922C81C1F33B88400D8F479 /* libclosured-stub.cpp in Sources */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                        isa = PBXSourcesBuildPhase;
                        buildActionMask = 2147483647;
                        files = (
                        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 */,
+                               F98692171DC3EFD500CBEDE6 /* update_dyld_shared_cache.cpp in Sources */,
+                               F98692181DC3EFD700CBEDE6 /* DyldSharedCache.cpp in Sources */,
+                               F981C8BD1EEF447500452F35 /* DyldCacheParser.cpp in Sources */,
+                               F986921F1DC3F98700CBEDE6 /* CacheBuilder.cpp in Sources */,
+                               F98692231DC403F900CBEDE6 /* AdjustDylibSegments.cpp in Sources */,
+                               F98692191DC3EFDA00CBEDE6 /* FileUtils.cpp in Sources */,
+                               F98692201DC3F99300CBEDE6 /* Diagnostics.cpp in Sources */,
+                               F9460DCD1E09FFFC00FEC613 /* PathOverrides.cpp in Sources */,
+                               F98692211DC401B900CBEDE6 /* MachOParser.cpp in Sources */,
+                               F9D862401DC57A27000A199A /* OptimizerObjC.cpp in Sources */,
+                               F94182D51E60A2F100D8EF25 /* OptimizerBranches.cpp in Sources */,
+                               F9A548B31DDBBC75002B4422 /* ImageProxy.cpp in Sources */,
+                               F9D862411DC65A4E000A199A /* LaunchCacheWriter.cpp in Sources */,
+                               F9D862421DC65A53000A199A /* LaunchCacheReader.cpp in Sources */,
+                               F9D8623F1DC41043000A199A /* OptimizerLinkedit.cpp in Sources */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               F96354311DCD74A400895049 /* Sources */ = {
+                       isa = PBXSourcesBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               F96354461DCD74BC00895049 /* update_dyld_sim_shared_cache.cpp in Sources */,
+                               F96354331DCD74A400895049 /* DyldSharedCache.cpp in Sources */,
+                               F981C8BF1EEF733C00452F35 /* DyldCacheParser.cpp in Sources */,
+                               F981C8BE1EEF733800452F35 /* ClosureBuffer.cpp in Sources */,
+                               F96354341DCD74A400895049 /* CacheBuilder.cpp in Sources */,
+                               F96354351DCD74A400895049 /* AdjustDylibSegments.cpp in Sources */,
+                               F963546C1DD8F38300895049 /* ImageProxy.cpp in Sources */,
+                               F96354361DCD74A400895049 /* FileUtils.cpp in Sources */,
+                               F96354371DCD74A400895049 /* Diagnostics.cpp in Sources */,
+                               F9460DCE1E0A000600FEC613 /* PathOverrides.cpp in Sources */,
+                               F96354381DCD74A400895049 /* MachOParser.cpp in Sources */,
+                               F96354391DCD74A400895049 /* OptimizerObjC.cpp in Sources */,
+                               F963543A1DCD74A400895049 /* LaunchCacheWriter.cpp in Sources */,
+                               37C5C2FF1E60D7DE006B32C9 /* OptimizerBranches.cpp in Sources */,
+                               F963543B1DCD74A400895049 /* LaunchCacheReader.cpp in Sources */,
+                               F963543C1DCD74A400895049 /* OptimizerLinkedit.cpp in Sources */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               F97C61A31DBAD1A900A84CD7 /* Sources */ = {
+                       isa = PBXSourcesBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               F9D862451DC975A5000A199A /* dyld_closure_util.cpp in Sources */,
+                               F97C61B31DBAE14200A84CD7 /* MachOParser.cpp in Sources */,
+                               F9D8624D1DC9783E000A199A /* FileUtils.cpp in Sources */,
+                               F9D862461DC975AA000A199A /* Diagnostics.cpp in Sources */,
+                               F926C0471DDBFB7A00941CB1 /* ImageProxy.cpp in Sources */,
+                               F9F76FB01E09CDF400828678 /* PathOverrides.cpp in Sources */,
+                               F9D8624C1DC97717000A199A /* DyldSharedCache.cpp in Sources */,
+                               F9D862471DC975B1000A199A /* LaunchCacheWriter.cpp in Sources */,
+                               F9B3CAEC1EEB5CFB00C9A48B /* DyldCacheParser.cpp in Sources */,
+                               F9D8624B1DC976E4000A199A /* LaunchCachePrinter.cpp in Sources */,
+                               F9D862481DC975B3000A199A /* LaunchCacheReader.cpp in Sources */,
+                               F9E5E2C61EB00A9F0013A0BB /* ClosureBuffer.cpp in Sources */,
+                               F9ABA06E1E289B72000F21B4 /* closuredProtocol.defs in Sources */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                        isa = PBXSourcesBuildPhase;
                        buildActionMask = 2147483647;
                        files = (
                        isa = PBXSourcesBuildPhase;
                        buildActionMask = 2147483647;
                        files = (
-                               F97FF3601C236408000ACDD2 /* execserver.defs in Sources */,
                                F97FF3611C23640C000ACDD2 /* nocr.c in Sources */,
                                F97FF3611C23640C000ACDD2 /* nocr.c in Sources */,
+                               F988F0BB1E2FDF5B003AED79 /* execserver.defs in Sources */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
+               F9AB02B41F329FAA00EE96C4 /* Sources */ = {
+                       isa = PBXSourcesBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               F9AB02CC1F32A33C00EE96C4 /* FileUtils.cpp in Sources */,
+                               F9AB02CB1F32A26700EE96C4 /* PathOverrides.cpp in Sources */,
+                               F9AB02CA1F32A25F00EE96C4 /* DyldCacheParser.cpp in Sources */,
+                               F9AB02C91F32A24B00EE96C4 /* ClosureBuffer.cpp in Sources */,
+                               F9AB02C81F32A23B00EE96C4 /* MachOParser.cpp in Sources */,
+                               F9AB02C71F32A22B00EE96C4 /* LaunchCacheReader.cpp in Sources */,
+                               F9AB02C61F32A1F400EE96C4 /* Diagnostics.cpp in Sources */,
+                               F9AB02C41F329FF400EE96C4 /* DyldSharedCache.cpp in Sources */,
+                               F9AB02C31F329FE000EE96C4 /* ImageProxy.cpp in Sources */,
+                               F9AB02C51F329FFE00EE96C4 /* LaunchCacheWriter.cpp in Sources */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
                F9D1000F14D8D0BA00099D91 /* Sources */ = {
                        isa = PBXSourcesBuildPhase;
                        buildActionMask = 2147483647;
                F9D1000F14D8D0BA00099D91 /* Sources */ = {
                        isa = PBXSourcesBuildPhase;
                        buildActionMask = 2147483647;
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
+               F9DDEDAE1E2878CA00A753DC /* Sources */ = {
+                       isa = PBXSourcesBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               F9DDEDB91E2878EC00A753DC /* closured.cpp in Sources */,
+                               F9DDEDBA1E2878F100A753DC /* closuredProtocol.defs in Sources */,
+                               F9DDEDBB1E287C9500A753DC /* DyldSharedCache.cpp in Sources */,
+                               F9DDEDBC1E287CA100A753DC /* Diagnostics.cpp in Sources */,
+                               F9DDEDC01E287CF600A753DC /* LaunchCacheReader.cpp in Sources */,
+                               F9DDEDBD1E287CB100A753DC /* LaunchCacheWriter.cpp in Sources */,
+                               F9DDEDC21E287D8A00A753DC /* PathOverrides.cpp in Sources */,
+                               F9DDEDC11E287D3C00A753DC /* MachOParser.cpp in Sources */,
+                               F981C8C01EEF7D4100452F35 /* DyldCacheParser.cpp in Sources */,
+                               F9DDEDBE1E287CF600A753DC /* FileUtils.cpp in Sources */,
+                               F9DDEDBF1E287CF600A753DC /* ImageProxy.cpp in Sources */,
+                               F9B3CAEA1EE20FE200C9A48B /* ClosureBuffer.cpp in Sources */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
                F9ED4C950630A76000DF4E74 /* Sources */ = {
                        isa = PBXSourcesBuildPhase;
                        buildActionMask = 2147483647;
                        files = (
                F9ED4C950630A76000DF4E74 /* Sources */ = {
                        isa = PBXSourcesBuildPhase;
                        buildActionMask = 2147483647;
                        files = (
+                               37D7DB001E96F0ED00D52CEA /* Tracing.cpp in Sources */,
                                F9ED4CDF0630A7F100DF4E74 /* dyldStartup.s in Sources */,
                                F9ED4CDB0630A7F100DF4E74 /* dyldInitialization.cpp in Sources */,
                                F9ED4CD70630A7F100DF4E74 /* dyld.cpp in Sources */,
                                F9ED4CDF0630A7F100DF4E74 /* dyldStartup.s in Sources */,
                                F9ED4CDB0630A7F100DF4E74 /* dyldInitialization.cpp in Sources */,
                                F9ED4CD70630A7F100DF4E74 /* dyld.cpp in Sources */,
                                F9ED4CDE0630A7F100DF4E74 /* dyldNew.cpp in Sources */,
                                F94DB9040F0A9B1700323715 /* ImageLoaderMachOClassic.cpp in Sources */,
                                F94DB9050F0A9B1700323715 /* ImageLoaderMachOCompressed.cpp in Sources */,
                                F9ED4CDE0630A7F100DF4E74 /* dyldNew.cpp in Sources */,
                                F94DB9040F0A9B1700323715 /* ImageLoaderMachOClassic.cpp in Sources */,
                                F94DB9050F0A9B1700323715 /* ImageLoaderMachOCompressed.cpp in Sources */,
+                               F9C2755B1DA73EA1007A5D8A /* Loading.cpp in Sources */,
+                               F9C275571DA5D67F007A5D8A /* MachOParser.cpp in Sources */,
+                               F922AE5A1EF0DC7200926F9D /* DyldCacheParser.cpp in Sources */,
+                               F9D8624F1DCBD318000A199A /* Diagnostics.cpp in Sources */,
+                               F9D862501DCBD31D000A199A /* LaunchCacheReader.cpp in Sources */,
+                               F9C73AC21E2992730025C89E /* closuredProtocol.defs in Sources */,
+                               F977DDCB1E53BF5500609230 /* SharedCacheRuntime.cpp in Sources */,
+                               F9D862511DCBD330000A199A /* DyldSharedCache.cpp in Sources */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                        isa = PBXSourcesBuildPhase;
                        buildActionMask = 2147483647;
                        files = (
                        isa = PBXSourcesBuildPhase;
                        buildActionMask = 2147483647;
                        files = (
+                               F9D49CCC1458B95200F86ADD /* start_glue.s in Sources */,
+                               37D7DB011E96F3EB00D52CEA /* Tracing.cpp in Sources */,
                                F9F256360639DBCC00A7427D /* dyldLock.cpp in Sources */,
                                F9BA514B0ECE4F4200D1D62E /* dyld_stub_binder.s in Sources */,
                                F9A221E70F3A6D7C00D15F73 /* dyldLibSystemGlue.c in Sources */,
                                F9F256360639DBCC00A7427D /* dyldLock.cpp in Sources */,
                                F9BA514B0ECE4F4200D1D62E /* dyld_stub_binder.s in Sources */,
                                F9A221E70F3A6D7C00D15F73 /* dyldLibSystemGlue.c in Sources */,
                                F9A6D70C116FBBD10051CC16 /* threadLocalHelpers.s in Sources */,
                                F95090E51C5AD1E80031F81D /* dyld_process_info.cpp in Sources */,
                                F958D4771C7FCE6700A0B199 /* dyld_process_info_notify.cpp 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 */,
+                               F96D19BF1D94A6DC007AF3CE /* MachOParser.cpp in Sources */,
+                               F922AE591EF0DBA500926F9D /* DyldCacheParser.cpp in Sources */,
+                               F9D8624E1DCBD06A000A199A /* Diagnostics.cpp in Sources */,
+                               F92015711DE3F3B000816A4A /* DyldSharedCache.cpp in Sources */,
+                               F96D19C01D94BFCE007AF3CE /* AllImages.cpp in Sources */,
+                               F96D19A81D93661A007AF3CE /* APIs.cpp in Sources */,
+                               F9C15A4A1E1F7DAC0006E570 /* APIs_macOS.cpp in Sources */,
+                               F97C61A21D9CAE3500A84CD7 /* Logging.cpp in Sources */,
+                               F9C2755A1DA71CE8007A5D8A /* Loading.cpp in Sources */,
+                               F90108611E2AD96000870568 /* PathOverrides.cpp in Sources */,
+                               F9D862431DC90A4F000A199A /* LaunchCacheReader.cpp in Sources */,
+                               F9E5E2C71EB00AAA0013A0BB /* ClosureBuffer.cpp in Sources */,
+                               F9C86B651E2B16C600FD8669 /* closuredProtocol.defs in Sources */,
+                               F97C619F1D9829AA00A84CD7 /* libdyldEntryVector.cpp in Sources */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                        target = 37A0AD0A1C15FFF500731E50 /* update_dyld_shared_cache */;
                        targetProxy = 37A0AD0E1C16000F00731E50 /* PBXContainerItemProxy */;
                };
                        target = 37A0AD0A1C15FFF500731E50 /* update_dyld_shared_cache */;
                        targetProxy = 37A0AD0E1C16000F00731E50 /* PBXContainerItemProxy */;
                };
-               37A0AD111C16003600731E50 /* PBXTargetDependency */ = {
+               D8668AD01ECE335F005E7D31 /* PBXTargetDependency */ = {
                        isa = PBXTargetDependency;
                        isa = PBXTargetDependency;
-                       target = F93937310A94FAF700070A07 /* update_dyld_shared_cache_tool */;
-                       targetProxy = 37A0AD101C16003600731E50 /* PBXContainerItemProxy */;
+                       target = F97C61A61DBAD1A900A84CD7 /* dyld_closure_util */;
+                       targetProxy = D8668ACF1ECE335F005E7D31 /* PBXContainerItemProxy */;
                };
                };
-               37A0AD131C16003600731E50 /* PBXTargetDependency */ = {
+               F908134811D3ED1A00626CC1 /* PBXTargetDependency */ = {
                        isa = PBXTargetDependency;
                        isa = PBXTargetDependency;
-                       target = F9F2A5580F7AEE9800B7C9EB /* libdsc */;
-                       targetProxy = 37A0AD121C16003600731E50 /* PBXContainerItemProxy */;
+                       target = F9ED4C9E0630A76B00DF4E74 /* libdyld.dylib */;
+                       targetProxy = F908134711D3ED1A00626CC1 /* PBXContainerItemProxy */;
                };
                };
-               37A0AD151C16003600731E50 /* PBXTargetDependency */ = {
+               F922C8121F33B62700D8F479 /* PBXTargetDependency */ = {
                        isa = PBXTargetDependency;
                        isa = PBXTargetDependency;
-                       target = F9D1001114D8D0BA00099D91 /* dsc_extractor */;
-                       targetProxy = 37A0AD141C16003600731E50 /* PBXContainerItemProxy */;
+                       target = F9AB02B71F329FAA00EE96C4 /* libclosured */;
+                       targetProxy = F922C8111F33B62700D8F479 /* PBXContainerItemProxy */;
                };
                };
-               37A0AD171C16003600731E50 /* PBXTargetDependency */ = {
+               F922C81E1F33B96300D8F479 /* PBXTargetDependency */ = {
                        isa = PBXTargetDependency;
                        isa = PBXTargetDependency;
-                       target = 377685F21AC4B27D00026E6C /* multi_dyld_shared_cache_builder */;
-                       targetProxy = 37A0AD161C16003600731E50 /* PBXContainerItemProxy */;
+                       target = F922C8161F33B73800D8F479 /* libclosured-stub */;
+                       targetProxy = F922C81D1F33B96300D8F479 /* PBXContainerItemProxy */;
                };
                };
-               37A0AD191C16003600731E50 /* PBXTargetDependency */ = {
+               F94182D81E60F0BE00D8EF25 /* PBXTargetDependency */ = {
                        isa = PBXTargetDependency;
                        isa = PBXTargetDependency;
-                       target = 3703A1111B38C1B300ADBA7F /* dyld_shared_cache_builder */;
-                       targetProxy = 37A0AD181C16003600731E50 /* PBXContainerItemProxy */;
+                       target = F99B8E550FEC10F600701838 /* dyld_shared_cache_util */;
+                       targetProxy = F94182D71E60F0BE00D8EF25 /* PBXContainerItemProxy */;
                };
                };
-               37A0AD1B1C16004600731E50 /* PBXTargetDependency */ = {
+               F94182DA1E60F0C000D8EF25 /* PBXTargetDependency */ = {
                        isa = PBXTargetDependency;
                        isa = PBXTargetDependency;
-                       target = F99B8E550FEC10F600701838 /* dyld_shared_cache_util */;
-                       targetProxy = 37A0AD1A1C16004600731E50 /* PBXContainerItemProxy */;
+                       target = F9F2A5580F7AEE9800B7C9EB /* libdsc */;
+                       targetProxy = F94182D91E60F0C000D8EF25 /* PBXContainerItemProxy */;
                };
                };
-               F908134811D3ED1A00626CC1 /* PBXTargetDependency */ = {
+               F94182DC1E60F16900D8EF25 /* PBXTargetDependency */ = {
                        isa = PBXTargetDependency;
                        isa = PBXTargetDependency;
-                       target = F9ED4C9E0630A76B00DF4E74 /* libdyld.dylib */;
-                       targetProxy = F908134711D3ED1A00626CC1 /* PBXContainerItemProxy */;
+                       target = F9D1001114D8D0BA00099D91 /* dsc_extractor */;
+                       targetProxy = F94182DB1E60F16900D8EF25 /* PBXContainerItemProxy */;
+               };
+               F96543A11E343601003C5540 /* PBXTargetDependency */ = {
+                       isa = PBXTargetDependency;
+                       target = F97C61A61DBAD1A900A84CD7 /* dyld_closure_util */;
+                       targetProxy = F96543A01E343601003C5540 /* PBXContainerItemProxy */;
                };
                F97FF3661C237F97000ACDD2 /* PBXTargetDependency */ = {
                        isa = PBXTargetDependency;
                };
                F97FF3661C237F97000ACDD2 /* PBXTargetDependency */ = {
                        isa = PBXTargetDependency;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INT_CONVERSION = YES;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INT_CONVERSION = YES;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+                               CLANG_WARN_SUSPICIOUS_MOVE = YES;
                                CLANG_WARN_UNREACHABLE_CODE = YES;
                                CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
                                COPY_PHASE_STRIP = NO;
                                CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)";
                                CLANG_WARN_UNREACHABLE_CODE = YES;
                                CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
                                COPY_PHASE_STRIP = NO;
                                CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)";
+                               DEAD_CODE_STRIPPING = YES;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                FRAMEWORK_SEARCH_PATHS = (
                                        "$(inherited)",
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                FRAMEWORK_SEARCH_PATHS = (
                                        "$(inherited)",
                                GCC_PREPROCESSOR_DEFINITIONS = (
                                        "DEBUG=1",
                                        "$(inherited)",
                                GCC_PREPROCESSOR_DEFINITIONS = (
                                        "DEBUG=1",
                                        "$(inherited)",
+                                       "BUILDING_CACHE_BUILDER=1",
                                );
                                GCC_SYMBOLS_PRIVATE_EXTERN = NO;
                                GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
                                );
                                GCC_SYMBOLS_PRIVATE_EXTERN = NO;
                                GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
                                GCC_WARN_UNUSED_FUNCTION = YES;
                                GCC_WARN_UNUSED_VARIABLE = YES;
                                INSTALL_PATH = "$(INSTALL_LOCATION)/usr/local/bin";
                                GCC_WARN_UNUSED_FUNCTION = YES;
                                GCC_WARN_UNUSED_VARIABLE = YES;
                                INSTALL_PATH = "$(INSTALL_LOCATION)/usr/local/bin";
+                               MACOSX_DEPLOYMENT_TARGET = 10.11;
                                MTL_ENABLE_DEBUG_INFO = YES;
                                ONLY_ACTIVE_ARCH = YES;
                                OTHER_CFLAGS = "-DBOM_SUPPORT=1";
                                PRODUCT_NAME = "$(TARGET_NAME)";
                                SDKROOT = macosx.internal;
                                MTL_ENABLE_DEBUG_INFO = YES;
                                ONLY_ACTIVE_ARCH = YES;
                                OTHER_CFLAGS = "-DBOM_SUPPORT=1";
                                PRODUCT_NAME = "$(TARGET_NAME)";
                                SDKROOT = macosx.internal;
+                               SUPPORTED_PLATFORMS = macosx;
+                               TOOLCHAINS = default;
                                USER_HEADER_SEARCH_PATHS = "../launch-cache/";
                                USER_HEADER_SEARCH_PATHS = "../launch-cache/";
-                               VALID_ARCHS = "armv6 armv7 x86_64 x86_64h";
+                               VALID_ARCHS = "x86_64 x86_64h";
                        };
                        name = Debug;
                };
                        };
                        name = Debug;
                };
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INT_CONVERSION = YES;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INT_CONVERSION = YES;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+                               CLANG_WARN_SUSPICIOUS_MOVE = YES;
                                CLANG_WARN_UNREACHABLE_CODE = YES;
                                CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
                                COPY_PHASE_STRIP = NO;
                                CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)";
                                CLANG_WARN_UNREACHABLE_CODE = YES;
                                CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
                                COPY_PHASE_STRIP = NO;
                                CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)";
+                               DEAD_CODE_STRIPPING = YES;
                                DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
                                ENABLE_NS_ASSERTIONS = NO;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
                                ENABLE_NS_ASSERTIONS = NO;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                        "$(SDKROOT)$(APPLE_INTERNAL_LIBRARY_DIR)/Frameworks",
                                );
                                GCC_C_LANGUAGE_STANDARD = gnu99;
                                        "$(SDKROOT)$(APPLE_INTERNAL_LIBRARY_DIR)/Frameworks",
                                );
                                GCC_C_LANGUAGE_STANDARD = gnu99;
+                               GCC_PREPROCESSOR_DEFINITIONS = "BUILDING_CACHE_BUILDER=1";
                                GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
                                GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
                                GCC_WARN_UNDECLARED_SELECTOR = YES;
                                GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
                                GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
                                GCC_WARN_UNDECLARED_SELECTOR = YES;
                                GCC_WARN_UNUSED_FUNCTION = YES;
                                GCC_WARN_UNUSED_VARIABLE = YES;
                                INSTALL_PATH = "$(INSTALL_LOCATION)/usr/local/bin";
                                GCC_WARN_UNUSED_FUNCTION = YES;
                                GCC_WARN_UNUSED_VARIABLE = YES;
                                INSTALL_PATH = "$(INSTALL_LOCATION)/usr/local/bin";
+                               MACOSX_DEPLOYMENT_TARGET = 10.11;
                                MTL_ENABLE_DEBUG_INFO = NO;
                                OTHER_CFLAGS = "-DBOM_SUPPORT=1";
                                PRODUCT_NAME = "$(TARGET_NAME)";
                                SDKROOT = macosx.internal;
                                MTL_ENABLE_DEBUG_INFO = NO;
                                OTHER_CFLAGS = "-DBOM_SUPPORT=1";
                                PRODUCT_NAME = "$(TARGET_NAME)";
                                SDKROOT = macosx.internal;
+                               SUPPORTED_PLATFORMS = macosx;
+                               TOOLCHAINS = default;
                                USER_HEADER_SEARCH_PATHS = "../launch-cache/";
                                USER_HEADER_SEARCH_PATHS = "../launch-cache/";
-                               VALID_ARCHS = "armv6 armv7 x86_64 x86_64h";
+                               VALID_ARCHS = "x86_64 x86_64h";
                                VERSIONING_SYSTEM = "apple-generic";
                        };
                        name = Release;
                                VERSIONING_SYSTEM = "apple-generic";
                        };
                        name = Release;
                                CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
                                COPY_PHASE_STRIP = NO;
                                CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)";
                                CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
                                COPY_PHASE_STRIP = NO;
                                CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)";
+                               DEAD_CODE_STRIPPING = YES;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                FRAMEWORK_SEARCH_PATHS = (
                                        "$(inherited)",
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                FRAMEWORK_SEARCH_PATHS = (
                                        "$(inherited)",
                                GCC_PREPROCESSOR_DEFINITIONS = (
                                        "DEBUG=1",
                                        "$(inherited)",
                                GCC_PREPROCESSOR_DEFINITIONS = (
                                        "DEBUG=1",
                                        "$(inherited)",
+                                       "BUILDING_CACHE_BUILDER=1",
                                );
                                GCC_SYMBOLS_PRIVATE_EXTERN = NO;
                                GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
                                );
                                GCC_SYMBOLS_PRIVATE_EXTERN = NO;
                                GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
                                GCC_WARN_UNUSED_FUNCTION = YES;
                                GCC_WARN_UNUSED_VARIABLE = YES;
                                INSTALL_PATH = "$(INSTALL_LOCATION)/usr/local/bin";
                                GCC_WARN_UNUSED_FUNCTION = YES;
                                GCC_WARN_UNUSED_VARIABLE = YES;
                                INSTALL_PATH = "$(INSTALL_LOCATION)/usr/local/bin";
+                               MACOSX_DEPLOYMENT_TARGET = 10.11;
                                MTL_ENABLE_DEBUG_INFO = YES;
                                ONLY_ACTIVE_ARCH = YES;
                                OTHER_CFLAGS = "-DBOM_SUPPORT=1";
                                MTL_ENABLE_DEBUG_INFO = YES;
                                ONLY_ACTIVE_ARCH = YES;
                                OTHER_CFLAGS = "-DBOM_SUPPORT=1";
                                SDKROOT = macosx.internal;
                                STRIP_INSTALLED_PRODUCT = NO;
                                USER_HEADER_SEARCH_PATHS = "../launch-cache/";
                                SDKROOT = macosx.internal;
                                STRIP_INSTALLED_PRODUCT = NO;
                                USER_HEADER_SEARCH_PATHS = "../launch-cache/";
-                               VALID_ARCHS = "armv6 armv7 x86_64 x86_64h";
+                               VALID_ARCHS = "x86_64 x86_64h";
                        };
                        name = Debug;
                };
                        };
                        name = Debug;
                };
                                CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
                                COPY_PHASE_STRIP = NO;
                                CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)";
                                CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
                                COPY_PHASE_STRIP = NO;
                                CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)";
+                               DEAD_CODE_STRIPPING = YES;
                                DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
                                ENABLE_NS_ASSERTIONS = NO;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
                                ENABLE_NS_ASSERTIONS = NO;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                        "$(SDKROOT)/AppleInternal/Library/Frameworks",
                                );
                                GCC_C_LANGUAGE_STANDARD = gnu99;
                                        "$(SDKROOT)/AppleInternal/Library/Frameworks",
                                );
                                GCC_C_LANGUAGE_STANDARD = gnu99;
+                               GCC_PREPROCESSOR_DEFINITIONS = "BUILDING_CACHE_BUILDER=1";
                                GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
                                GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
                                GCC_WARN_UNDECLARED_SELECTOR = YES;
                                GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
                                GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
                                GCC_WARN_UNDECLARED_SELECTOR = YES;
                                GCC_WARN_UNUSED_FUNCTION = YES;
                                GCC_WARN_UNUSED_VARIABLE = YES;
                                INSTALL_PATH = "$(INSTALL_LOCATION)/usr/local/bin";
                                GCC_WARN_UNUSED_FUNCTION = YES;
                                GCC_WARN_UNUSED_VARIABLE = YES;
                                INSTALL_PATH = "$(INSTALL_LOCATION)/usr/local/bin";
+                               MACOSX_DEPLOYMENT_TARGET = 10.11;
                                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/";
                                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";
+                               VALID_ARCHS = "x86_64 x86_64h";
                                VERSIONING_SYSTEM = "apple-generic";
                        };
                        name = Release;
                                VERSIONING_SYSTEM = "apple-generic";
                        };
                        name = Release;
                        };
                        name = Release;
                };
                        };
                        name = Release;
                };
+               F922C8191F33B73800D8F479 /* Debug */ = {
+                       isa = XCBuildConfiguration;
+                       buildSettings = {
+                               ALWAYS_SEARCH_USER_PATHS = NO;
+                               CLANG_ANALYZER_NONNULL = YES;
+                               CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+                               CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+                               CLANG_CXX_LIBRARY = "libc++";
+                               CLANG_ENABLE_MODULES = YES;
+                               CLANG_ENABLE_OBJC_ARC = YES;
+                               CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+                               CLANG_WARN_COMMA = YES;
+                               CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+                               CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+                               CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+                               CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+                               CLANG_WARN_STRICT_PROTOTYPES = YES;
+                               CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+                               CODE_SIGN_IDENTITY = "-";
+                               COPY_PHASE_STRIP = NO;
+                               DEBUG_INFORMATION_FORMAT = dwarf;
+                               DYLIB_COMPATIBILITY_VERSION = 1;
+                               DYLIB_CURRENT_VERSION = 1;
+                               ENABLE_TESTABILITY = YES;
+                               EXECUTABLE_PREFIX = lib;
+                               GCC_C_LANGUAGE_STANDARD = gnu11;
+                               GCC_DYNAMIC_NO_PIC = NO;
+                               GCC_OPTIMIZATION_LEVEL = 0;
+                               GCC_PREPROCESSOR_DEFINITIONS = (
+                                       "DEBUG=1",
+                                       "$(inherited)",
+                               );
+                               GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+                               GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+                               LD_DYLIB_INSTALL_NAME = /usr/lib/closure/libclosured.dylib;
+                               MACOSX_DEPLOYMENT_TARGET = 10.13;
+                               MTL_ENABLE_DEBUG_INFO = YES;
+                               OTHER_LDFLAGS = "-nostdlib";
+                               PRODUCT_NAME = closured;
+                               SDKROOT = macosx;
+                               SKIP_INSTALL = YES;
+                       };
+                       name = Debug;
+               };
+               F922C81A1F33B73800D8F479 /* Release */ = {
+                       isa = XCBuildConfiguration;
+                       buildSettings = {
+                               ALWAYS_SEARCH_USER_PATHS = NO;
+                               CLANG_ANALYZER_NONNULL = YES;
+                               CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+                               CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+                               CLANG_CXX_LIBRARY = "libc++";
+                               CLANG_ENABLE_MODULES = YES;
+                               CLANG_ENABLE_OBJC_ARC = YES;
+                               CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+                               CLANG_WARN_COMMA = YES;
+                               CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+                               CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+                               CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+                               CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+                               CLANG_WARN_STRICT_PROTOTYPES = YES;
+                               CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+                               CODE_SIGN_IDENTITY = "-";
+                               COPY_PHASE_STRIP = NO;
+                               DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+                               DYLIB_COMPATIBILITY_VERSION = 1;
+                               DYLIB_CURRENT_VERSION = 1;
+                               ENABLE_NS_ASSERTIONS = NO;
+                               EXECUTABLE_PREFIX = lib;
+                               GCC_C_LANGUAGE_STANDARD = gnu11;
+                               GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+                               GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+                               LD_DYLIB_INSTALL_NAME = /usr/lib/closure/libclosured.dylib;
+                               MACOSX_DEPLOYMENT_TARGET = 10.13;
+                               MTL_ENABLE_DEBUG_INFO = NO;
+                               OTHER_LDFLAGS = "-nostdlib";
+                               PRODUCT_NAME = closured;
+                               SDKROOT = macosx;
+                               SKIP_INSTALL = YES;
+                       };
+                       name = Release;
+               };
                F93937350A94FB2900070A07 /* Debug */ = {
                        isa = XCBuildConfiguration;
                        buildSettings = {
                F93937350A94FB2900070A07 /* Debug */ = {
                        isa = XCBuildConfiguration;
                        buildSettings = {
                                CLANG_CXX_LIBRARY = "libc++";
                                CLANG_ENABLE_OBJC_ARC = YES;
                                COPY_PHASE_STRIP = NO;
                                CLANG_CXX_LIBRARY = "libc++";
                                CLANG_ENABLE_OBJC_ARC = YES;
                                COPY_PHASE_STRIP = NO;
+                               DEAD_CODE_STRIPPING = YES;
                                DEBUG_INFORMATION_FORMAT = dwarf;
                                FRAMEWORK_SEARCH_PATHS = (
                                        "$(inherited)",
                                DEBUG_INFORMATION_FORMAT = dwarf;
                                FRAMEWORK_SEARCH_PATHS = (
                                        "$(inherited)",
                                GCC_OPTIMIZATION_LEVEL = 0;
                                GCC_THREADSAFE_STATICS = NO;
                                GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
                                GCC_OPTIMIZATION_LEVEL = 0;
                                GCC_THREADSAFE_STATICS = NO;
                                GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
+                               GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;
                                GCC_WARN_CHECK_SWITCH_STATEMENTS = YES;
                                GCC_WARN_EFFECTIVE_CPLUSPLUS_VIOLATIONS = NO;
                                GCC_WARN_CHECK_SWITCH_STATEMENTS = YES;
                                GCC_WARN_EFFECTIVE_CPLUSPLUS_VIOLATIONS = NO;
+                               GCC_WARN_HIDDEN_VIRTUAL_FUNCTIONS = YES;
                                GCC_WARN_MISSING_PARENTHESES = YES;
                                GCC_WARN_MISSING_PARENTHESES = YES;
+                               GCC_WARN_NON_VIRTUAL_DESTRUCTOR = YES;
                                GCC_WARN_SHADOW = YES;
                                GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES;
                                GCC_WARN_UNUSED_FUNCTION = YES;
                                GCC_WARN_UNUSED_VARIABLE = 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";
+                               HEADER_SEARCH_PATHS = (
+                                       "$(SRCROOT)/include",
+                                       "$(SRCROOT)/dyld3",
+                                       "$(SRCROOT)/dyld3/shared-cache",
+                               );
+                               INSTALL_PATH = /usr/bin;
                                OTHER_CPLUSPLUSFLAGS = "$(OTHER_CFLAGS)";
                                OTHER_LDFLAGS = "-stdlib=libc++";
                                PRODUCT_NAME = update_dyld_shared_cache;
                                SDKROOT = macosx.internal;
                                OTHER_CPLUSPLUSFLAGS = "$(OTHER_CFLAGS)";
                                OTHER_LDFLAGS = "-stdlib=libc++";
                                PRODUCT_NAME = update_dyld_shared_cache;
                                SDKROOT = macosx.internal;
+                               USE_HEADERMAP = NO;
                                VALID_ARCHS = x86_64;
                        };
                        name = Debug;
                                VALID_ARCHS = x86_64;
                        };
                        name = Debug;
                                CLANG_CXX_LANGUAGE_STANDARD = "c++14";
                                CLANG_CXX_LIBRARY = "libc++";
                                CLANG_ENABLE_OBJC_ARC = YES;
                                CLANG_CXX_LANGUAGE_STANDARD = "c++14";
                                CLANG_CXX_LIBRARY = "libc++";
                                CLANG_ENABLE_OBJC_ARC = YES;
+                               CODE_SIGN_IDENTITY = "-";
                                COPY_PHASE_STRIP = NO;
                                CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)";
                                COPY_PHASE_STRIP = NO;
                                CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)";
+                               DEAD_CODE_STRIPPING = YES;
                                DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
                                FRAMEWORK_SEARCH_PATHS = (
                                        "$(inherited)",
                                DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
                                FRAMEWORK_SEARCH_PATHS = (
                                        "$(inherited)",
                                GCC_OPTIMIZATION_LEVEL = s;
                                GCC_THREADSAFE_STATICS = NO;
                                GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
                                GCC_OPTIMIZATION_LEVEL = s;
                                GCC_THREADSAFE_STATICS = NO;
                                GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
+                               GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;
+                               GCC_WARN_HIDDEN_VIRTUAL_FUNCTIONS = YES;
+                               GCC_WARN_NON_VIRTUAL_DESTRUCTOR = YES;
+                               GCC_WARN_SHADOW = YES;
                                GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES;
                                GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES;
-                               HEADER_SEARCH_PATHS = "$(SRCROOT)/include";
+                               HEADER_SEARCH_PATHS = (
+                                       "$(SRCROOT)/include",
+                                       "$(SRCROOT)/dyld3",
+                                       "$(SRCROOT)/dyld3/shared-cache",
+                               );
+                               INSTALL_PATH = /usr/bin;
                                OTHER_CPLUSPLUSFLAGS = "$(OTHER_CFLAGS)";
                                OTHER_LDFLAGS = "-stdlib=libc++";
                                PRODUCT_NAME = update_dyld_shared_cache;
                                SDKROOT = macosx.internal;
                                STRIP_INSTALLED_PRODUCT = YES;
                                STRIP_STYLE = debugging;
                                OTHER_CPLUSPLUSFLAGS = "$(OTHER_CFLAGS)";
                                OTHER_LDFLAGS = "-stdlib=libc++";
                                PRODUCT_NAME = update_dyld_shared_cache;
                                SDKROOT = macosx.internal;
                                STRIP_INSTALLED_PRODUCT = YES;
                                STRIP_STYLE = debugging;
+                               USE_HEADERMAP = NO;
                                VALID_ARCHS = x86_64;
                                VERSIONING_SYSTEM = "apple-generic";
                        };
                        name = Release;
                };
                                VALID_ARCHS = x86_64;
                                VERSIONING_SYSTEM = "apple-generic";
                        };
                        name = Release;
                };
+               F96354431DCD74A400895049 /* 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_ABOUT_MISSING_PROTOTYPES = YES;
+                               GCC_WARN_CHECK_SWITCH_STATEMENTS = YES;
+                               GCC_WARN_EFFECTIVE_CPLUSPLUS_VIOLATIONS = NO;
+                               GCC_WARN_HIDDEN_VIRTUAL_FUNCTIONS = YES;
+                               GCC_WARN_MISSING_PARENTHESES = YES;
+                               GCC_WARN_NON_VIRTUAL_DESTRUCTOR = 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",
+                                       "$(SRCROOT)/dyld3",
+                                       "$(SRCROOT)/dyld3/shared-cache",
+                               );
+                               INSTALL_PATH = "$(DEVICE_PLATFORM_INSTALL_DIR)/Developer/Library/CoreSimulator/Profiles/Runtimes/$(SIMULATOR_DIR_NAME).simruntime/Contents/Resources";
+                               MACOSX_DEPLOYMENT_TARGET = 10.11;
+                               OTHER_CPLUSPLUSFLAGS = "$(OTHER_CFLAGS)";
+                               OTHER_LDFLAGS = "-stdlib=libc++";
+                               PRODUCT_NAME = "$(TARGET_NAME)";
+                               SDKROOT = macosx.internal;
+                               SIMULATOR_DIR_NAME = "$(PLATFORM_FAMILY_NAME_$(DEVICE_PLATFORM_NAME)) $(IPHONE_SDK_MARKETING_VERSION)";
+                               USE_HEADERMAP = NO;
+                               VALID_ARCHS = x86_64;
+                       };
+                       name = Debug;
+               };
+               F96354441DCD74A400895049 /* Release */ = {
+                       isa = XCBuildConfiguration;
+                       baseConfigurationReference = F94182DE1E60FFDC00D8EF25 /* update_dyld_sim_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_ABOUT_MISSING_PROTOTYPES = YES;
+                               GCC_WARN_HIDDEN_VIRTUAL_FUNCTIONS = YES;
+                               GCC_WARN_NON_VIRTUAL_DESTRUCTOR = YES;
+                               GCC_WARN_SHADOW = YES;
+                               GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES;
+                               HEADER_SEARCH_PATHS = (
+                                       "$(SRCROOT)/include",
+                                       "$(SRCROOT)/dyld3",
+                                       "$(SRCROOT)/dyld3/shared-cache",
+                               );
+                               INSTALL_PATH = "$(DEVICE_PLATFORM_INSTALL_DIR)/Developer/Library/CoreSimulator/Profiles/Runtimes/$(SIMULATOR_DIR_NAME).simruntime/Contents/Resources";
+                               MACOSX_DEPLOYMENT_TARGET = 10.11;
+                               OTHER_CPLUSPLUSFLAGS = "$(OTHER_CFLAGS)";
+                               OTHER_LDFLAGS = "-stdlib=libc++";
+                               PRODUCT_NAME = "$(TARGET_NAME)";
+                               SDKROOT = macosx.internal;
+                               SIMULATOR_DIR_NAME = "$(PLATFORM_FAMILY_NAME_$(DEVICE_PLATFORM_NAME)) $(IPHONE_SDK_MARKETING_VERSION)";
+                               STRIP_INSTALLED_PRODUCT = YES;
+                               STRIP_STYLE = debugging;
+                               USE_HEADERMAP = NO;
+                               VALID_ARCHS = x86_64;
+                               VERSIONING_SYSTEM = "apple-generic";
+                       };
+                       name = Release;
+               };
+               F97C61AC1DBAD1A900A84CD7 /* Debug */ = {
+                       isa = XCBuildConfiguration;
+                       buildSettings = {
+                               ALWAYS_SEARCH_USER_PATHS = NO;
+                               CLANG_ANALYZER_NONNULL = YES;
+                               CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+                               CLANG_CXX_LIBRARY = "libc++";
+                               CLANG_ENABLE_MODULES = YES;
+                               CLANG_ENABLE_OBJC_ARC = YES;
+                               CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+                               CLANG_WARN_INFINITE_RECURSION = YES;
+                               CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+                               CLANG_WARN_SUSPICIOUS_MOVES = YES;
+                               CODE_SIGN_IDENTITY = "-";
+                               COPY_PHASE_STRIP = NO;
+                               DEAD_CODE_STRIPPING = YES;
+                               DEBUG_INFORMATION_FORMAT = dwarf;
+                               DEVELOPMENT_TEAM = 59GAB85EFG;
+                               ENABLE_TESTABILITY = YES;
+                               GCC_C_LANGUAGE_STANDARD = gnu99;
+                               GCC_DYNAMIC_NO_PIC = NO;
+                               GCC_OPTIMIZATION_LEVEL = 0;
+                               GCC_PREPROCESSOR_DEFINITIONS = (
+                                       "DEBUG=1",
+                                       "$(inherited)",
+                               );
+                               GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+                               GCC_WARN_SHADOW = YES;
+                               GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+                               INSTALL_PATH = "$(INSTALL_LOCATION)/usr/local/bin";
+                               MTL_ENABLE_DEBUG_INFO = YES;
+                               ONLY_ACTIVE_ARCH = YES;
+                               PRODUCT_NAME = "$(TARGET_NAME)";
+                               SDKROOT = macosx.internal;
+                       };
+                       name = Debug;
+               };
+               F97C61AD1DBAD1A900A84CD7 /* Release */ = {
+                       isa = XCBuildConfiguration;
+                       buildSettings = {
+                               ALWAYS_SEARCH_USER_PATHS = NO;
+                               CLANG_ANALYZER_NONNULL = YES;
+                               CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+                               CLANG_CXX_LIBRARY = "libc++";
+                               CLANG_ENABLE_MODULES = YES;
+                               CLANG_ENABLE_OBJC_ARC = YES;
+                               CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+                               CLANG_WARN_INFINITE_RECURSION = YES;
+                               CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+                               CLANG_WARN_SUSPICIOUS_MOVES = YES;
+                               CODE_SIGN_IDENTITY = "-";
+                               COPY_PHASE_STRIP = NO;
+                               DEAD_CODE_STRIPPING = YES;
+                               DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+                               DEVELOPMENT_TEAM = 59GAB85EFG;
+                               ENABLE_NS_ASSERTIONS = NO;
+                               GCC_C_LANGUAGE_STANDARD = gnu99;
+                               GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+                               GCC_WARN_SHADOW = YES;
+                               GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+                               INSTALL_PATH = "$(INSTALL_LOCATION)/usr/local/bin";
+                               MTL_ENABLE_DEBUG_INFO = NO;
+                               PRODUCT_NAME = "$(TARGET_NAME)";
+                               SDKROOT = macosx.internal;
+                       };
+                       name = Release;
+               };
                F97FF35A1C23638F000ACDD2 /* Debug */ = {
                        isa = XCBuildConfiguration;
                        buildSettings = {
                F97FF35A1C23638F000ACDD2 /* Debug */ = {
                        isa = XCBuildConfiguration;
                        buildSettings = {
                                CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
                                CODE_SIGN_IDENTITY = "-";
                                COPY_PHASE_STRIP = NO;
                                CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
                                CODE_SIGN_IDENTITY = "-";
                                COPY_PHASE_STRIP = NO;
+                               CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)";
                                DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
                                ENABLE_NS_ASSERTIONS = NO;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
                                ENABLE_NS_ASSERTIONS = NO;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                MTL_ENABLE_DEBUG_INFO = NO;
                                PRODUCT_NAME = "$(TARGET_NAME)";
                                SDKROOT = macosx.internal;
                                MTL_ENABLE_DEBUG_INFO = NO;
                                PRODUCT_NAME = "$(TARGET_NAME)";
                                SDKROOT = macosx.internal;
+                               VERSIONING_SYSTEM = "apple-generic";
                        };
                        name = Release;
                };
                        };
                        name = Release;
                };
                                );
                                INSTALL_PATH = "$(INSTALL_LOCATION)/usr/local/bin";
                                PRODUCT_NAME = dyld_shared_cache_util;
                                );
                                INSTALL_PATH = "$(INSTALL_LOCATION)/usr/local/bin";
                                PRODUCT_NAME = dyld_shared_cache_util;
+                               SDKROOT = macosx.internal;
+                               SUPPORTED_PLATFORMS = macosx;
                        };
                        name = Debug;
                };
                        };
                        name = Debug;
                };
                                );
                                INSTALL_PATH = "$(INSTALL_LOCATION)/usr/local/bin";
                                PRODUCT_NAME = dyld_shared_cache_util;
                                );
                                INSTALL_PATH = "$(INSTALL_LOCATION)/usr/local/bin";
                                PRODUCT_NAME = dyld_shared_cache_util;
+                               SDKROOT = macosx.internal;
                                SKIP_INSTALL = NO;
                                SKIP_INSTALL = NO;
+                               SUPPORTED_PLATFORMS = macosx;
+                       };
+                       name = Release;
+               };
+               F9AB02C01F329FAA00EE96C4 /* Debug */ = {
+                       isa = XCBuildConfiguration;
+                       buildSettings = {
+                               ALWAYS_SEARCH_USER_PATHS = NO;
+                               CLANG_ANALYZER_NONNULL = YES;
+                               CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+                               CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+                               CLANG_CXX_LIBRARY = "libc++";
+                               CLANG_ENABLE_MODULES = YES;
+                               CLANG_ENABLE_OBJC_ARC = YES;
+                               CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+                               CLANG_WARN_COMMA = YES;
+                               CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+                               CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+                               CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+                               CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+                               CLANG_WARN_STRICT_PROTOTYPES = YES;
+                               CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+                               CODE_SIGN_IDENTITY = "-";
+                               COPY_PHASE_STRIP = NO;
+                               DEAD_CODE_STRIPPING = YES;
+                               DEBUG_INFORMATION_FORMAT = dwarf;
+                               DYLIB_COMPATIBILITY_VERSION = 1;
+                               DYLIB_CURRENT_VERSION = 1;
+                               ENABLE_TESTABILITY = YES;
+                               EXECUTABLE_PREFIX = lib;
+                               GCC_C_LANGUAGE_STANDARD = gnu11;
+                               GCC_DYNAMIC_NO_PIC = NO;
+                               GCC_ENABLE_CPP_EXCEPTIONS = YES;
+                               GCC_ENABLE_CPP_RTTI = YES;
+                               GCC_OPTIMIZATION_LEVEL = 0;
+                               GCC_PREPROCESSOR_DEFINITIONS = (
+                                       "DYLD_IN_PROCESS=0",
+                                       "DEBUG=1",
+                                       "$(inherited)",
+                               );
+                               GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+                               GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+                               INSTALL_PATH = /usr/lib/closure;
+                               MACOSX_DEPLOYMENT_TARGET = 10.12;
+                               MTL_ENABLE_DEBUG_INFO = YES;
+                               OTHER_LDFLAGS = (
+                                       "-Wl,-exported_symbol,__ZN5dyld325closured_CreateImageGroupERKNS_13ClosureBufferE",
+                                       "-Wl,-no_inits",
+                               );
+                               PRODUCT_NAME = closured;
+                               SDKROOT = macosx.internal;
+                       };
+                       name = Debug;
+               };
+               F9AB02C11F329FAA00EE96C4 /* Release */ = {
+                       isa = XCBuildConfiguration;
+                       buildSettings = {
+                               ALWAYS_SEARCH_USER_PATHS = NO;
+                               CLANG_ANALYZER_NONNULL = YES;
+                               CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+                               CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+                               CLANG_CXX_LIBRARY = "libc++";
+                               CLANG_ENABLE_MODULES = YES;
+                               CLANG_ENABLE_OBJC_ARC = YES;
+                               CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+                               CLANG_WARN_COMMA = YES;
+                               CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+                               CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+                               CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+                               CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+                               CLANG_WARN_STRICT_PROTOTYPES = YES;
+                               CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+                               CODE_SIGN_IDENTITY = "-";
+                               COPY_PHASE_STRIP = NO;
+                               DEAD_CODE_STRIPPING = YES;
+                               DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+                               DYLIB_COMPATIBILITY_VERSION = 1;
+                               DYLIB_CURRENT_VERSION = 1;
+                               ENABLE_NS_ASSERTIONS = NO;
+                               EXECUTABLE_PREFIX = lib;
+                               GCC_C_LANGUAGE_STANDARD = gnu11;
+                               GCC_ENABLE_CPP_EXCEPTIONS = YES;
+                               GCC_ENABLE_CPP_RTTI = YES;
+                               GCC_PREPROCESSOR_DEFINITIONS = "DYLD_IN_PROCESS=0";
+                               GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+                               GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+                               INSTALL_PATH = /usr/lib/closure;
+                               MACOSX_DEPLOYMENT_TARGET = 10.12;
+                               MTL_ENABLE_DEBUG_INFO = NO;
+                               OTHER_LDFLAGS = (
+                                       "-Wl,-exported_symbol,__ZN5dyld325closured_CreateImageGroupERKNS_13ClosureBufferE",
+                                       "-Wl,-no_inits",
+                               );
+                               PRODUCT_NAME = closured;
+                               SDKROOT = macosx.internal;
                        };
                        name = Release;
                };
                        };
                        name = Release;
                };
                        baseConfigurationReference = F971DD141A4A0E0700BBDD52 /* dyld.xcconfig */;
                        buildSettings = {
                                ALWAYS_SEARCH_USER_PATHS = NO;
                        baseConfigurationReference = F971DD141A4A0E0700BBDD52 /* dyld.xcconfig */;
                        buildSettings = {
                                ALWAYS_SEARCH_USER_PATHS = NO;
-                               ARCHS = "$(ARCHS_STANDARD_32_64_BIT)";
+                               CLANG_CXX_LANGUAGE_STANDARD = "c++0x";
                                CLANG_CXX_LIBRARY = "libc++";
                                CLANG_WARN_EMPTY_BODY = YES;
                                CODE_SIGN_IDENTITY = "-";
                                CLANG_CXX_LIBRARY = "libc++";
                                CLANG_WARN_EMPTY_BODY = YES;
                                CODE_SIGN_IDENTITY = "-";
                                GCC_DYNAMIC_NO_PIC = NO;
                                GCC_ENABLE_BUILTIN_FUNCTIONS = NO;
                                GCC_OPTIMIZATION_LEVEL = 0;
                                GCC_DYNAMIC_NO_PIC = NO;
                                GCC_ENABLE_BUILTIN_FUNCTIONS = NO;
                                GCC_OPTIMIZATION_LEVEL = 0;
-                               GCC_PREPROCESSOR_DEFINITIONS = "DYLD_VERSION=$(RC_ProjectSourceVersion)";
+                               GCC_PREPROCESSOR_DEFINITIONS = (
+                                       "DYLD_VERSION=$(RC_ProjectSourceVersion)",
+                                       "_LIBCPP_NO_EXCEPTIONS=1",
+                                       "$(inherited)",
+                               );
                                GCC_SYMBOLS_PRIVATE_EXTERN = NO;
                                GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO;
                                GCC_WARN_ALLOW_INCOMPLETE_PROTOCOL = NO;
                                GCC_SYMBOLS_PRIVATE_EXTERN = NO;
                                GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO;
                                GCC_WARN_ALLOW_INCOMPLETE_PROTOCOL = NO;
                                        "$(ALIGNMENT)",
                                        "$(ENTRY)",
                                );
                                        "$(ALIGNMENT)",
                                        "$(ENTRY)",
                                );
-                               SDKROOT = macosx.internal;
                                STRIPFLAGS = "-S";
                                UNSTRIPPED_PRODUCT = NO;
                                VERSIONING_SYSTEM = "apple-generic";
                                STRIPFLAGS = "-S";
                                UNSTRIPPED_PRODUCT = NO;
                                VERSIONING_SYSTEM = "apple-generic";
                        isa = XCBuildConfiguration;
                        baseConfigurationReference = F971DD141A4A0E0700BBDD52 /* dyld.xcconfig */;
                        buildSettings = {
                        isa = XCBuildConfiguration;
                        baseConfigurationReference = F971DD141A4A0E0700BBDD52 /* dyld.xcconfig */;
                        buildSettings = {
-                               ARCHS = "$(ARCHS_STANDARD_32_64_BIT)";
+                               CLANG_CXX_LANGUAGE_STANDARD = "c++0x";
                                CLANG_CXX_LIBRARY = "libc++";
                                CLANG_WARN_EMPTY_BODY = YES;
                                CODE_SIGN_IDENTITY = "-";
                                CLANG_CXX_LIBRARY = "libc++";
                                CLANG_WARN_EMPTY_BODY = YES;
                                CODE_SIGN_IDENTITY = "-";
                                GCC_DYNAMIC_NO_PIC = NO;
                                GCC_ENABLE_CPP_RTTI = NO;
                                GCC_OPTIMIZATION_LEVEL = s;
                                GCC_DYNAMIC_NO_PIC = NO;
                                GCC_ENABLE_CPP_RTTI = NO;
                                GCC_OPTIMIZATION_LEVEL = s;
-                               GCC_PREPROCESSOR_DEFINITIONS = "DYLD_VERSION=$(RC_ProjectSourceVersion)";
+                               GCC_PREPROCESSOR_DEFINITIONS = (
+                                       "DYLD_VERSION=$(RC_ProjectSourceVersion)",
+                                       "_LIBCPP_NO_EXCEPTIONS=1",
+                                       "$(inherited)",
+                               );
                                GCC_SYMBOLS_PRIVATE_EXTERN = NO;
                                GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO;
                                GCC_WARN_SHADOW = YES;
                                GCC_SYMBOLS_PRIVATE_EXTERN = NO;
                                GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO;
                                GCC_WARN_SHADOW = YES;
                                        "$(ALIGNMENT)",
                                        "$(ENTRY)",
                                        "-Wl,-no_data_const",
                                        "$(ALIGNMENT)",
                                        "$(ENTRY)",
                                        "-Wl,-no_data_const",
-                                       "-Wl,-section_order,__DATA,__all_image_info:__nl_symbol_ptr:__got:__const:__crash_info:__data:__common:__bss",
+                                       "-Wl,-section_order,__DATA,__all_image_info:__nl_symbol_ptr:__got:__const:__crash_info:__data:__bss:__common",
                                );
                                );
-                               SDKROOT = macosx.internal;
                                STRIPFLAGS = "-S";
                                UNSTRIPPED_PRODUCT = NO;
                                VERSIONING_SYSTEM = "apple-generic";
                                STRIPFLAGS = "-S";
                                UNSTRIPPED_PRODUCT = NO;
                                VERSIONING_SYSTEM = "apple-generic";
                        isa = XCBuildConfiguration;
                        baseConfigurationReference = F971DD151A4A0E0700BBDD52 /* libdyld.xcconfig */;
                        buildSettings = {
                        isa = XCBuildConfiguration;
                        baseConfigurationReference = F971DD151A4A0E0700BBDD52 /* libdyld.xcconfig */;
                        buildSettings = {
-                               ARCHS = "$(ARCHS_STANDARD_32_64_BIT)";
                                CLANG_CXX_LANGUAGE_STANDARD = "c++0x";
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_CXX_LANGUAGE_STANDARD = "c++0x";
                                CLANG_WARN_EMPTY_BODY = YES;
+                               CODE_SIGN_IDENTITY = "-";
                                COMBINE_HIDPI_IMAGES = YES;
                                COMBINE_HIDPI_IMAGES = YES;
+                               CURRENT_PROJECT_VERSION = 500;
                                DEAD_CODE_STRIPPING = YES;
                                EXECUTABLE_PREFIX = lib;
                                GCC_C_LANGUAGE_STANDARD = gnu99;
                                DEAD_CODE_STRIPPING = YES;
                                EXECUTABLE_PREFIX = lib;
                                GCC_C_LANGUAGE_STANDARD = gnu99;
                                GCC_WARN_ABOUT_RETURN_TYPE = YES;
                                GCC_WARN_SHADOW = YES;
                                GCC_WARN_UNINITIALIZED_AUTOS = YES;
                                GCC_WARN_ABOUT_RETURN_TYPE = YES;
                                GCC_WARN_SHADOW = YES;
                                GCC_WARN_UNINITIALIZED_AUTOS = YES;
+                               GENERATE_TEXT_BASED_STUBS = YES;
                                HEADER_SEARCH_PATHS = "$(SRCROOT)/include";
                                INSTALLHDRS_COPY_PHASE = YES;
                                HEADER_SEARCH_PATHS = "$(SRCROOT)/include";
                                INSTALLHDRS_COPY_PHASE = YES;
+                               INSTALLHDRS_SCRIPT_PHASE = YES;
+                               ONLY_ACTIVE_ARCH = NO;
                                OTHER_CFLAGS = "";
                                OTHER_CFLAGS = "";
+                               OTHER_CPLUSPLUSFLAGS = (
+                                       "$(OTHER_CFLAGS)",
+                                       "-Wglobal-constructors",
+                               );
                                OTHER_LDFLAGS = (
                                OTHER_LDFLAGS = (
+                                       "-Wl,-no_inits",
                                        "-nostdlib",
                                        "$(LIBSYSTEM_LIBS)",
                                        "-umbrella",
                                        System,
                                        "-L$(SDKROOT)/usr/lib/system",
                                );
                                        "-nostdlib",
                                        "$(LIBSYSTEM_LIBS)",
                                        "-umbrella",
                                        System,
                                        "-L$(SDKROOT)/usr/lib/system",
                                );
+                               OTHER_TAPI_FLAGS = "-extra-public-header ./include/dlfcn.h -extra-private-header ./dyld3/libdyldEntryVector.h -ObjC++ -std=c++11 ";
+                               PRIVATE_HEADERS_FOLDER_PATH = "/usr/local/include/mach-o";
                                PRODUCT_NAME = dyld;
                                PRODUCT_NAME = dyld;
+                               PUBLIC_HEADERS_FOLDER_PATH = "/usr//include/mach-o";
                                SKIP_INSTALL = NO;
                                SKIP_INSTALL = NO;
+                               SUPPORTS_TEXT_BASED_API = YES;
+                               TAPI_VERIFY_MODE = Pedantic;
+                               VERSIONING_SYSTEM = "apple-generic";
+                               VERSION_INFO_EXPORT_DECL = "__attribute__((visibility(\"default\")))";
                                WARNING_CFLAGS = (
                                        "-Wmost",
                                        "-Wno-four-char-constants",
                                WARNING_CFLAGS = (
                                        "-Wmost",
                                        "-Wno-four-char-constants",
                        isa = XCBuildConfiguration;
                        baseConfigurationReference = F971DD151A4A0E0700BBDD52 /* libdyld.xcconfig */;
                        buildSettings = {
                        isa = XCBuildConfiguration;
                        baseConfigurationReference = F971DD151A4A0E0700BBDD52 /* libdyld.xcconfig */;
                        buildSettings = {
-                               ARCHS = "$(ARCHS_STANDARD_32_64_BIT)";
                                CLANG_CXX_LANGUAGE_STANDARD = "c++0x";
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_CXX_LANGUAGE_STANDARD = "c++0x";
                                CLANG_WARN_EMPTY_BODY = YES;
+                               CODE_SIGN_IDENTITY = "-";
                                COMBINE_HIDPI_IMAGES = YES;
                                COPY_PHASE_STRIP = YES;
                                CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)";
                                COMBINE_HIDPI_IMAGES = YES;
                                COPY_PHASE_STRIP = YES;
                                CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)";
                                GCC_WARN_ABOUT_RETURN_TYPE = YES;
                                GCC_WARN_SHADOW = YES;
                                GCC_WARN_UNINITIALIZED_AUTOS = YES;
                                GCC_WARN_ABOUT_RETURN_TYPE = YES;
                                GCC_WARN_SHADOW = YES;
                                GCC_WARN_UNINITIALIZED_AUTOS = YES;
+                               GENERATE_TEXT_BASED_STUBS = YES;
                                HEADER_SEARCH_PATHS = "$(SRCROOT)/include";
                                INSTALLHDRS_COPY_PHASE = YES;
                                HEADER_SEARCH_PATHS = "$(SRCROOT)/include";
                                INSTALLHDRS_COPY_PHASE = YES;
+                               INSTALLHDRS_SCRIPT_PHASE = YES;
                                OTHER_CFLAGS = "";
                                OTHER_CPLUSPLUSFLAGS = (
                                        "-fno-exceptions",
                                        "$(OTHER_CFLAGS)",
                                );
                                OTHER_LDFLAGS = (
                                OTHER_CFLAGS = "";
                                OTHER_CPLUSPLUSFLAGS = (
                                        "-fno-exceptions",
                                        "$(OTHER_CFLAGS)",
                                );
                                OTHER_LDFLAGS = (
+                                       "-Wl,-no_inits",
                                        "-nostdlib",
                                        "$(LIBSYSTEM_LIBS)",
                                        "-umbrella",
                                        System,
                                        "-L$(SDKROOT)/usr/lib/system",
                                        "-nostdlib",
                                        "$(LIBSYSTEM_LIBS)",
                                        "-umbrella",
                                        System,
                                        "-L$(SDKROOT)/usr/lib/system",
+                                       "-Wl,-no_inits",
                                );
                                "OTHER_LDFLAGS[sdk=iphoneos*]" = (
                                );
                                "OTHER_LDFLAGS[sdk=iphoneos*]" = (
+                                       "-Wl,-no_inits",
                                        "-nostdlib",
                                        "$(LIBSYSTEM_LIBS)",
                                        "-umbrella",
                                        "-nostdlib",
                                        "$(LIBSYSTEM_LIBS)",
                                        "-umbrella",
                                        "-Wl,-dirty_data_list,$(SRCROOT)/src/libdyld_data_symbols.dirty",
                                );
                                "OTHER_LDFLAGS[sdk=macosx*]" = (
                                        "-Wl,-dirty_data_list,$(SRCROOT)/src/libdyld_data_symbols.dirty",
                                );
                                "OTHER_LDFLAGS[sdk=macosx*]" = (
+                                       "-Wl,-no_inits",
                                        "-nostdlib",
                                        "$(LIBSYSTEM_LIBS)",
                                        "-umbrella",
                                        System,
                                        "-L$(SDKROOT)/usr/lib/system",
                                );
                                        "-nostdlib",
                                        "$(LIBSYSTEM_LIBS)",
                                        "-umbrella",
                                        System,
                                        "-L$(SDKROOT)/usr/lib/system",
                                );
+                               OTHER_TAPI_FLAGS = "-extra-public-header ./include/dlfcn.h -extra-private-header ./dyld3/libdyldEntryVector.h -ObjC++ -std=c++11 ";
+                               PRIVATE_HEADERS_FOLDER_PATH = "/usr/local/include/mach-o";
                                PRODUCT_NAME = dyld;
                                PRODUCT_NAME = dyld;
+                               PUBLIC_HEADERS_FOLDER_PATH = "/usr//include/mach-o";
                                SEPARATE_STRIP = YES;
                                SKIP_INSTALL = NO;
                                STRIP_INSTALLED_PRODUCT = YES;
                                SEPARATE_STRIP = YES;
                                SKIP_INSTALL = NO;
                                STRIP_INSTALLED_PRODUCT = YES;
+                               SUPPORTS_TEXT_BASED_API = YES;
+                               TAPI_VERIFY_MODE = Pedantic;
                                VERSIONING_SYSTEM = "apple-generic";
                                VERSIONING_SYSTEM = "apple-generic";
+                               VERSION_INFO_EXPORT_DECL = "__attribute__((visibility(\"default\")))";
                                WARNING_CFLAGS = (
                                        "-Wmost",
                                        "-Wno-four-char-constants",
                                WARNING_CFLAGS = (
                                        "-Wmost",
                                        "-Wno-four-char-constants",
                        isa = XCBuildConfiguration;
                        baseConfigurationReference = F971DD131A4A0E0700BBDD52 /* base.xcconfig */;
                        buildSettings = {
                        isa = XCBuildConfiguration;
                        baseConfigurationReference = F971DD131A4A0E0700BBDD52 /* base.xcconfig */;
                        buildSettings = {
+                               CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
                                CLANG_CXX_LIBRARY = "compiler-default";
                                CLANG_CXX_LIBRARY = "compiler-default";
-                               ONLY_ACTIVE_ARCH = NO;
+                               CLANG_WARN_BOOL_CONVERSION = YES;
+                               CLANG_WARN_CONSTANT_CONVERSION = YES;
+                               CLANG_WARN_EMPTY_BODY = YES;
+                               CLANG_WARN_ENUM_CONVERSION = YES;
+                               CLANG_WARN_INFINITE_RECURSION = YES;
+                               CLANG_WARN_INT_CONVERSION = YES;
+                               CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+                               CLANG_WARN_SUSPICIOUS_MOVE = YES;
+                               CLANG_WARN_UNREACHABLE_CODE = YES;
+                               CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+                               ENABLE_STRICT_OBJC_MSGSEND = YES;
+                               ENABLE_TESTABILITY = NO;
+                               EXCLUDED_INSTALLSRC_SUBDIRECTORY_PATTERNS = "$(inherited) build DerivedData closure-tests";
+                               GCC_NO_COMMON_BLOCKS = YES;
+                               GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+                               GCC_WARN_ABOUT_RETURN_TYPE = YES;
+                               GCC_WARN_UNDECLARED_SELECTOR = YES;
+                               GCC_WARN_UNINITIALIZED_AUTOS = YES;
+                               GCC_WARN_UNUSED_FUNCTION = YES;
+                               GCC_WARN_UNUSED_VARIABLE = YES;
+                               ONLY_ACTIVE_ARCH = YES;
                                SDKROOT = macosx.internal;
                                SDKROOT = macosx.internal;
-                               SUPPORTED_PLATFORMS = "iphonesimulator iphoneos macosx watchos appletvos";
                        };
                        name = Debug;
                };
                        };
                        name = Debug;
                };
                        isa = XCBuildConfiguration;
                        baseConfigurationReference = F971DD131A4A0E0700BBDD52 /* base.xcconfig */;
                        buildSettings = {
                        isa = XCBuildConfiguration;
                        baseConfigurationReference = F971DD131A4A0E0700BBDD52 /* base.xcconfig */;
                        buildSettings = {
+                               CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
                                CLANG_CXX_LIBRARY = "compiler-default";
                                CLANG_CXX_LIBRARY = "compiler-default";
+                               CLANG_WARN_BOOL_CONVERSION = YES;
+                               CLANG_WARN_CONSTANT_CONVERSION = YES;
+                               CLANG_WARN_EMPTY_BODY = YES;
+                               CLANG_WARN_ENUM_CONVERSION = YES;
+                               CLANG_WARN_INFINITE_RECURSION = YES;
+                               CLANG_WARN_INT_CONVERSION = YES;
+                               CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+                               CLANG_WARN_SUSPICIOUS_MOVE = YES;
+                               CLANG_WARN_UNREACHABLE_CODE = YES;
+                               CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+                               ENABLE_STRICT_OBJC_MSGSEND = YES;
+                               ENABLE_TESTABILITY = NO;
+                               EXCLUDED_INSTALLSRC_SUBDIRECTORY_PATTERNS = "$(inherited) build DerivedData closure-tests";
+                               GCC_NO_COMMON_BLOCKS = YES;
+                               GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+                               GCC_WARN_ABOUT_RETURN_TYPE = YES;
+                               GCC_WARN_UNDECLARED_SELECTOR = YES;
+                               GCC_WARN_UNINITIALIZED_AUTOS = YES;
+                               GCC_WARN_UNUSED_FUNCTION = YES;
+                               GCC_WARN_UNUSED_VARIABLE = YES;
                                SDKROOT = macosx.internal;
                                SDKROOT = macosx.internal;
-                               SUPPORTED_PLATFORMS = "iphonesimulator iphoneos macosx watchos appletvos";
+                       };
+                       name = Release;
+               };
+               F9DDEDB71E2878CB00A753DC /* Debug */ = {
+                       isa = XCBuildConfiguration;
+                       baseConfigurationReference = F9EDC0A01F0481B400B030F4 /* closured.xcconfig */;
+                       buildSettings = {
+                               ALWAYS_SEARCH_USER_PATHS = NO;
+                               CLANG_ANALYZER_NONNULL = YES;
+                               CLANG_CXX_LANGUAGE_STANDARD = "c++14";
+                               CLANG_CXX_LIBRARY = "libc++";
+                               CLANG_ENABLE_MODULES = YES;
+                               CLANG_ENABLE_OBJC_ARC = YES;
+                               CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+                               CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+                               CODE_SIGN_IDENTITY = "-";
+                               COPY_PHASE_STRIP = NO;
+                               DEAD_CODE_STRIPPING = YES;
+                               DEBUG_INFORMATION_FORMAT = dwarf;
+                               DEVELOPMENT_TEAM = 59GAB85EFG;
+                               ENABLE_TESTABILITY = YES;
+                               GCC_C_LANGUAGE_STANDARD = gnu99;
+                               GCC_DYNAMIC_NO_PIC = NO;
+                               GCC_OPTIMIZATION_LEVEL = 0;
+                               GCC_PREPROCESSOR_DEFINITIONS = (
+                                       "BUILDING_CLOSURED=1",
+                                       "DEBUG=1",
+                                       "$(inherited)",
+                               );
+                               GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+                               GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+                               HEADER_SEARCH_PATHS = "$(SRCROOT)/include";
+                               INSTALL_PATH = /usr/libexec/;
+                               MACOSX_DEPLOYMENT_TARGET = 10.13;
+                               MTL_ENABLE_DEBUG_INFO = YES;
+                               PLIST_FILE_OUTPUT_FORMAT = binary;
+                               PRODUCT_NAME = "$(TARGET_NAME)";
+                               STRIPFLAGS = "-S";
+                               VERSIONING_SYSTEM = "";
+                       };
+                       name = Debug;
+               };
+               F9DDEDB81E2878CB00A753DC /* Release */ = {
+                       isa = XCBuildConfiguration;
+                       baseConfigurationReference = F9EDC0A01F0481B400B030F4 /* closured.xcconfig */;
+                       buildSettings = {
+                               ALWAYS_SEARCH_USER_PATHS = NO;
+                               CLANG_ANALYZER_NONNULL = YES;
+                               CLANG_CXX_LANGUAGE_STANDARD = "c++14";
+                               CLANG_CXX_LIBRARY = "libc++";
+                               CLANG_ENABLE_MODULES = YES;
+                               CLANG_ENABLE_OBJC_ARC = YES;
+                               CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+                               CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+                               CODE_SIGN_IDENTITY = "-";
+                               COPY_PHASE_STRIP = NO;
+                               CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)";
+                               DEAD_CODE_STRIPPING = YES;
+                               DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+                               DEVELOPMENT_TEAM = 59GAB85EFG;
+                               ENABLE_NS_ASSERTIONS = NO;
+                               GCC_C_LANGUAGE_STANDARD = gnu99;
+                               GCC_PREPROCESSOR_DEFINITIONS = "BUILDING_CLOSURED=1";
+                               GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+                               GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+                               HEADER_SEARCH_PATHS = "$(SRCROOT)/include";
+                               INSTALL_PATH = /usr/libexec/;
+                               MACOSX_DEPLOYMENT_TARGET = 10.13;
+                               MTL_ENABLE_DEBUG_INFO = NO;
+                               PLIST_FILE_OUTPUT_FORMAT = binary;
+                               PRODUCT_NAME = "$(TARGET_NAME)";
+                               STRIPFLAGS = "-S";
+                               VERSIONING_SYSTEM = "apple-generic";
                        };
                        name = Release;
                };
                        };
                        name = Release;
                };
                        defaultConfigurationIsVisible = 0;
                        defaultConfigurationName = Release;
                };
                        defaultConfigurationIsVisible = 0;
                        defaultConfigurationName = Release;
                };
+               F922C8181F33B73800D8F479 /* Build configuration list for PBXNativeTarget "libclosured-stub" */ = {
+                       isa = XCConfigurationList;
+                       buildConfigurations = (
+                               F922C8191F33B73800D8F479 /* Debug */,
+                               F922C81A1F33B73800D8F479 /* Release */,
+                       );
+                       defaultConfigurationIsVisible = 0;
+                       defaultConfigurationName = Release;
+               };
                F93937340A94FB2900070A07 /* Build configuration list for PBXNativeTarget "update_dyld_shared_cache_tool" */ = {
                        isa = XCConfigurationList;
                        buildConfigurations = (
                F93937340A94FB2900070A07 /* Build configuration list for PBXNativeTarget "update_dyld_shared_cache_tool" */ = {
                        isa = XCConfigurationList;
                        buildConfigurations = (
                        defaultConfigurationIsVisible = 0;
                        defaultConfigurationName = Release;
                };
                        defaultConfigurationIsVisible = 0;
                        defaultConfigurationName = Release;
                };
+               F96354421DCD74A400895049 /* Build configuration list for PBXNativeTarget "update_dyld_sim_shared_cache" */ = {
+                       isa = XCConfigurationList;
+                       buildConfigurations = (
+                               F96354431DCD74A400895049 /* Debug */,
+                               F96354441DCD74A400895049 /* Release */,
+                       );
+                       defaultConfigurationIsVisible = 0;
+                       defaultConfigurationName = Release;
+               };
+               F97C61AB1DBAD1A900A84CD7 /* Build configuration list for PBXNativeTarget "dyld_closure_util" */ = {
+                       isa = XCConfigurationList;
+                       buildConfigurations = (
+                               F97C61AC1DBAD1A900A84CD7 /* Debug */,
+                               F97C61AD1DBAD1A900A84CD7 /* Release */,
+                       );
+                       defaultConfigurationIsVisible = 0;
+                       defaultConfigurationName = Release;
+               };
                F97FF35C1C23638F000ACDD2 /* Build configuration list for PBXNativeTarget "nocr" */ = {
                        isa = XCConfigurationList;
                        buildConfigurations = (
                F97FF35C1C23638F000ACDD2 /* Build configuration list for PBXNativeTarget "nocr" */ = {
                        isa = XCConfigurationList;
                        buildConfigurations = (
                        defaultConfigurationIsVisible = 0;
                        defaultConfigurationName = Release;
                };
                        defaultConfigurationIsVisible = 0;
                        defaultConfigurationName = Release;
                };
+               F9AB02C21F329FAA00EE96C4 /* Build configuration list for PBXNativeTarget "libclosured" */ = {
+                       isa = XCConfigurationList;
+                       buildConfigurations = (
+                               F9AB02C01F329FAA00EE96C4 /* Debug */,
+                               F9AB02C11F329FAA00EE96C4 /* Release */,
+                       );
+                       defaultConfigurationIsVisible = 0;
+                       defaultConfigurationName = Release;
+               };
                F9D1001714D8D0F100099D91 /* Build configuration list for PBXNativeTarget "dsc_extractor" */ = {
                        isa = XCConfigurationList;
                        buildConfigurations = (
                F9D1001714D8D0F100099D91 /* Build configuration list for PBXNativeTarget "dsc_extractor" */ = {
                        isa = XCConfigurationList;
                        buildConfigurations = (
                        defaultConfigurationIsVisible = 0;
                        defaultConfigurationName = Release;
                };
                        defaultConfigurationIsVisible = 0;
                        defaultConfigurationName = Release;
                };
+               F9DDEDB61E2878CB00A753DC /* Build configuration list for PBXNativeTarget "closured" */ = {
+                       isa = XCConfigurationList;
+                       buildConfigurations = (
+                               F9DDEDB71E2878CB00A753DC /* Debug */,
+                               F9DDEDB81E2878CB00A753DC /* Release */,
+                       );
+                       defaultConfigurationIsVisible = 0;
+                       defaultConfigurationName = Release;
+               };
                F9F2A56B0F7AEEB100B7C9EB /* Build configuration list for PBXNativeTarget "libdsc" */ = {
                        isa = XCConfigurationList;
                        buildConfigurations = (
                F9F2A56B0F7AEEB100B7C9EB /* Build configuration list for PBXNativeTarget "libdsc" */ = {
                        isa = XCConfigurationList;
                        buildConfigurations = (
diff --git a/dyld3/APIs.cpp b/dyld3/APIs.cpp
new file mode 100644 (file)
index 0000000..360a1cd
--- /dev/null
@@ -0,0 +1,1474 @@
+/*
+ * Copyright (c) 2017 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 <string.h>
+#include <stdint.h>
+#include <_simple.h>
+#include <sys/errno.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <TargetConditionals.h>
+#include <CommonCrypto/CommonDigest.h>
+#include <dispatch/dispatch.h>
+
+#include <algorithm>
+
+#include "dlfcn.h"
+#include "dyld_priv.h"
+
+#include "AllImages.h"
+#include "MachOParser.h"
+#include "Loading.h"
+#include "Logging.h"
+#include "Diagnostics.h"
+#include "DyldSharedCache.h"
+#include "PathOverrides.h"
+#include "APIs.h"
+#include "StringUtils.h"
+
+
+
+extern "C" {
+    #include "closuredProtocol.h"
+}
+
+
+namespace dyld {
+    extern dyld_all_image_infos dyld_all_image_infos;
+}
+
+
+namespace dyld3 {
+
+
+uint32_t _dyld_image_count(void)
+{
+    log_apis("_dyld_image_count()\n");
+
+    return gAllImages.count();
+}
+
+const mach_header* _dyld_get_image_header(uint32_t imageIndex)
+{
+    log_apis("_dyld_get_image_header(%d)\n", imageIndex);
+
+    const mach_header* loadAddress;
+    launch_cache::Image image = gAllImages.findByLoadOrder(imageIndex, &loadAddress);
+    if ( image.valid() )
+        return loadAddress;
+    return nullptr;
+}
+
+intptr_t _dyld_get_image_slide(const mach_header* mh)
+{
+    log_apis("_dyld_get_image_slide(%p)\n", mh);
+
+    MachOParser parser(mh);
+    return parser.getSlide();
+}
+
+intptr_t _dyld_get_image_vmaddr_slide(uint32_t imageIndex)
+{
+    log_apis("_dyld_get_image_vmaddr_slide(%d)\n", imageIndex);
+
+    const mach_header* mh = _dyld_get_image_header(imageIndex);
+    if ( mh != nullptr )
+        return dyld3::_dyld_get_image_slide(mh);
+    return 0;
+}
+
+const char* _dyld_get_image_name(uint32_t imageIndex)
+{
+    log_apis("_dyld_get_image_name(%d)\n", imageIndex);
+
+    const mach_header* loadAddress;
+    launch_cache::Image image = gAllImages.findByLoadOrder(imageIndex, &loadAddress);
+    if ( image.valid() )
+        return gAllImages.imagePath(image.binaryData());
+    return nullptr;
+}
+
+
+static bool nameMatch(const char* installName, const char* libraryName)
+{
+    const char* leafName = strrchr(installName, '/');
+    if ( leafName == NULL )
+        leafName = installName;
+    else
+        leafName++;
+
+    // -framework case is exact match of leaf name
+    if ( strcmp(leafName, libraryName) == 0 )
+        return true;
+
+    // -lxxx case: leafName must match "lib" <libraryName> ["." ?] ".dylib"
+    size_t leafNameLen = strlen(leafName);
+    size_t libraryNameLen = strlen(libraryName);
+    if ( leafNameLen < (libraryNameLen+9) )
+        return false;
+    if ( strncmp(leafName, "lib", 3) != 0 )
+        return false;
+    if ( strcmp(&leafName[leafNameLen-6], ".dylib") != 0 )
+        return false;
+    if ( strncmp(&leafName[3], libraryName, libraryNameLen) != 0 )
+        return false;
+    return (leafName[libraryNameLen+3] == '.');
+}
+
+
+//
+// BETTER, USE: dyld_get_program_sdk_version()
+//
+// Scans the main executable and returns the version of the specified dylib the program was built against.
+//
+// The library to find is the leaf name that would have been passed to linker tool
+// (e.g. -lfoo or -framework foo would use "foo").
+//
+// Returns -1 if the main executable did not link against the specified library, or is malformed.
+//
+int32_t NSVersionOfLinkTimeLibrary(const char* libraryName)
+{
+    log_apis("NSVersionOfLinkTimeLibrary(\"%s\")\n", libraryName);
+
+    __block int32_t result = -1;
+    MachOParser parser(gAllImages.mainExecutable());
+    parser.forEachDependentDylib(^(const char* loadPath, bool, bool, bool, uint32_t compatVersion, uint32_t currentVersion, bool& stop) {
+        if ( nameMatch(loadPath, libraryName) )
+            result = currentVersion;
+    });
+    log_apis("   NSVersionOfLinkTimeLibrary() => 0x%08X\n", result);
+    return result;
+}
+
+
+//
+// Searches loaded images for the requested dylib and returns its current version.
+//
+// The library to find is the leaf name that would have been passed to linker tool
+// (e.g. -lfoo or -framework foo would use "foo").
+//
+// If the specified library is not loaded, -1 is returned.
+//
+int32_t NSVersionOfRunTimeLibrary(const char* libraryName)
+{
+    log_apis("NSVersionOfRunTimeLibrary(\"%s\")\n", libraryName);
+
+    uint32_t count = gAllImages.count();
+    for (uint32_t i=0; i < count; ++i) {
+        const mach_header* loadAddress;
+        launch_cache::Image image = gAllImages.findByLoadOrder(i, &loadAddress);
+        if ( image.valid() ) {
+            MachOParser parser(loadAddress);
+            const char* installName;
+            uint32_t currentVersion;
+            uint32_t compatVersion;
+            if ( parser.getDylibInstallName(&installName, &compatVersion, &currentVersion) && nameMatch(installName, libraryName) ) {
+                log_apis("   NSVersionOfRunTimeLibrary() => 0x%08X\n", currentVersion);
+                return currentVersion;
+            }
+        }
+    }
+    log_apis("   NSVersionOfRunTimeLibrary() => -1\n");
+    return -1;
+}
+
+
+#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()
+{
+    log_apis("dyld_get_program_sdk_watch_os_version()\n");
+
+    Platform platform;
+    uint32_t minOS;
+    uint32_t sdk;
+
+    MachOParser parser(gAllImages.mainExecutable());
+    if ( parser.getPlatformAndVersion(&platform, &minOS, &sdk) ) {
+        if ( platform == Platform::watchOS )
+            return sdk;
+    }
+    return 0;
+}
+
+uint32_t dyld_get_program_min_watch_os_version()
+{
+    log_apis("dyld_get_program_min_watch_os_version()\n");
+
+    Platform platform;
+    uint32_t minOS;
+    uint32_t sdk;
+
+    MachOParser parser(gAllImages.mainExecutable());
+    if ( parser.getPlatformAndVersion(&platform, &minOS, &sdk) ) {
+        if ( platform == Platform::watchOS )
+            return minOS;  // return raw minOS (not mapped to iOS version)
+    }
+    return 0;
+}
+#endif
+
+
+#if TARGET_OS_BRIDGE
+
+static uint32_t bridgeVersToIOSVers(uint32_t vers)
+{
+    return vers + 0x00090000;
+}
+
+uint32_t dyld_get_program_sdk_bridge_os_version()
+{
+    log_apis("dyld_get_program_sdk_bridge_os_version()\n");
+
+    Platform platform;
+    uint32_t minOS;
+    uint32_t sdk;
+
+    MachOParser parser(gAllImages.mainExecutable());
+    if ( parser.getPlatformAndVersion(&platform, &minOS, &sdk) ) {
+        if ( platform == Platform::bridgeOS )
+            return sdk;
+    }
+    return 0;
+}
+
+uint32_t dyld_get_program_min_bridge_os_version()
+{
+    log_apis("dyld_get_program_min_bridge_os_version()\n");
+
+    Platform platform;
+    uint32_t minOS;
+    uint32_t sdk;
+
+    MachOParser parser(gAllImages.mainExecutable());
+    if ( parser.getPlatformAndVersion(&platform, &minOS, &sdk) ) {
+        if ( platform == Platform::bridgeOS )
+            return minOS;  // return raw minOS (not mapped to iOS version)
+    }
+    return 0;
+}
+
+#endif
+
+
+#if !__WATCH_OS_VERSION_MIN_REQUIRED && !__TV_OS_VERSION_MIN_REQUIRED && !TARGET_OS_BRIDGE
+
+#define PACKED_VERSION(major, minor, tiny) ((((major) & 0xffff) << 16) | (((minor) & 0xff) << 8) | ((tiny) & 0xff))
+
+static uint32_t deriveSDKVersFromDylibs(const mach_header* mh)
+{
+    __block uint32_t foundationVers = 0;
+    __block uint32_t libSystemVers = 0;
+    MachOParser parser(mh);
+    parser.forEachDependentDylib(^(const char* loadPath, bool, bool, bool, uint32_t compatVersion, uint32_t currentVersion, bool& stop) {
+        if ( strcmp(loadPath, "/System/Library/Frameworks/Foundation.framework/Versions/C/Foundation") == 0 )
+            foundationVers = currentVersion;
+        else if ( strcmp(loadPath, "/usr/lib/libSystem.B.dylib") == 0 )
+            libSystemVers = currentVersion;
+    });
+
+    struct DylibToOSMapping {
+        uint32_t dylibVersion;
+        uint32_t osVersion;
+    };
+    
+  #if __IPHONE_OS_VERSION_MIN_REQUIRED
+    static const DylibToOSMapping foundationMapping[] = {
+        { PACKED_VERSION(678,24,0), 0x00020000 },
+        { PACKED_VERSION(678,26,0), 0x00020100 },
+        { PACKED_VERSION(678,29,0), 0x00020200 },
+        { PACKED_VERSION(678,47,0), 0x00030000 },
+        { PACKED_VERSION(678,51,0), 0x00030100 },
+        { PACKED_VERSION(678,60,0), 0x00030200 },
+        { PACKED_VERSION(751,32,0), 0x00040000 },
+        { PACKED_VERSION(751,37,0), 0x00040100 },
+        { PACKED_VERSION(751,49,0), 0x00040200 },
+        { PACKED_VERSION(751,58,0), 0x00040300 },
+        { PACKED_VERSION(881,0,0),  0x00050000 },
+        { PACKED_VERSION(890,1,0),  0x00050100 },
+        { PACKED_VERSION(992,0,0),  0x00060000 },
+        { PACKED_VERSION(993,0,0),  0x00060100 },
+        { PACKED_VERSION(1038,14,0),0x00070000 },
+        { PACKED_VERSION(0,0,0),    0x00070000 }
+        // 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),   0x000A0400 },
+        { PACKED_VERSION(111,0,0),  0x000A0500 },
+        { PACKED_VERSION(123,0,0),  0x000A0600 },
+        { PACKED_VERSION(159,0,0),  0x000A0700 },
+        { PACKED_VERSION(169,3,0),  0x000A0800 },
+        { PACKED_VERSION(1197,0,0), 0x000A0900 },
+        { PACKED_VERSION(0,0,0),    0x000A0900 }
+        // 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
+
+
+//
+// Returns the sdk version (encode as nibble XXXX.YY.ZZ) that 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)
+{
+    log_apis("dyld_get_sdk_version(%p)\n", mh);
+
+    Platform platform;
+    uint32_t minOS;
+    uint32_t sdk;
+
+    if ( !MachOParser::wellFormedMachHeaderAndLoadCommands(mh) )
+        return 0;
+    MachOParser parser(mh);
+    if ( parser.getPlatformAndVersion(&platform, &minOS, &sdk) ) {
+        switch (platform) {
+#if TARGET_OS_BRIDGE
+            case Platform::bridgeOS:
+                // new binary. sdk version looks like "2.0" but API wants "11.0"
+                return bridgeVersToIOSVers(sdk);
+            case Platform::iOS:
+                // old binary. sdk matches API semantics so can return directly.
+                return sdk;
+#elif __WATCH_OS_VERSION_MIN_REQUIRED
+            case Platform::watchOS:
+                // new binary. sdk version looks like "2.0" but API wants "9.0"
+                return watchVersToIOSVers(sdk);
+            case Platform::iOS:
+                // old binary. sdk matches API semantics so can return directly.
+                return sdk;
+#elif __TV_OS_VERSION_MIN_REQUIRED
+            case Platform::tvOS:
+            case Platform::iOS:
+                return sdk;
+#elif __IPHONE_OS_VERSION_MIN_REQUIRED
+            case Platform::iOS:
+                if ( sdk != 0 )    // old binaries might not have SDK set
+                    return sdk;
+                break;
+#else
+            case Platform::macOS:
+                if ( sdk != 0 )    // old binaries might not have SDK set
+                    return sdk;
+                break;
+#endif
+            default:
+                // wrong binary for this platform
+                break;
+        }
+    }
+
+#if __WATCH_OS_VERSION_MIN_REQUIRED ||__TV_OS_VERSION_MIN_REQUIRED || TARGET_OS_BRIDGE
+    // All watchOS and tvOS 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()
+{
+     log_apis("dyld_get_program_sdk_version()\n");
+
+    return dyld3::dyld_get_sdk_version(gAllImages.mainExecutable());
+}
+
+uint32_t dyld_get_min_os_version(const mach_header* mh)
+{
+    log_apis("dyld_get_min_os_version(%p)\n", mh);
+
+    Platform platform;
+    uint32_t minOS;
+    uint32_t sdk;
+
+    if ( !MachOParser::wellFormedMachHeaderAndLoadCommands(mh) )
+        return 0;
+    MachOParser parser(mh);
+    if ( parser.getPlatformAndVersion(&platform, &minOS, &sdk) ) {
+        switch (platform) {
+#if TARGET_OS_BRIDGE
+            case Platform::bridgeOS:
+                // new binary. sdk version looks like "2.0" but API wants "11.0"
+                return bridgeVersToIOSVers(minOS);
+            case Platform::iOS:
+                // old binary. sdk matches API semantics so can return directly.
+                return minOS;
+#elif __WATCH_OS_VERSION_MIN_REQUIRED
+            case Platform::watchOS:
+                // new binary. OS version looks like "2.0" but API wants "9.0"
+                return watchVersToIOSVers(minOS);
+            case Platform::iOS:
+                // old binary. OS matches API semantics so can return directly.
+                return minOS;
+#elif __TV_OS_VERSION_MIN_REQUIRED
+            case Platform::tvOS:
+            case Platform::iOS:
+                return minOS;
+#elif __IPHONE_OS_VERSION_MIN_REQUIRED
+            case Platform::iOS:
+                return minOS;
+#else
+            case Platform::macOS:
+                return minOS;
+#endif
+            default:
+                // wrong binary for this platform
+                break;
+        }
+    }
+    return 0;
+}
+
+
+uint32_t dyld_get_program_min_os_version()
+{
+     log_apis("dyld_get_program_min_os_version()\n");
+
+    return dyld3::dyld_get_min_os_version(gAllImages.mainExecutable());
+}
+
+
+bool _dyld_get_image_uuid(const mach_header* mh, uuid_t uuid)
+{
+     log_apis("_dyld_get_image_uuid(%p, %p)\n", mh, uuid);
+
+    if ( !MachOParser::wellFormedMachHeaderAndLoadCommands(mh) )
+        return false;
+    MachOParser parser(mh);
+    return parser.getUuid(uuid);
+}
+
+//
+// _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.
+//
+int _NSGetExecutablePath(char* buf, uint32_t* bufsize)
+{
+     log_apis("_NSGetExecutablePath(%p, %p)\n", buf, bufsize);
+
+   launch_cache::Image image = gAllImages.mainExecutableImage();
+    if ( image.valid() ) {
+        const char* path = gAllImages.imagePath(image.binaryData());
+        size_t pathSize = strlen(path) + 1;
+        if ( *bufsize >= pathSize ) {
+            strcpy(buf, path);
+            return 0;
+        }
+        *bufsize = (uint32_t)pathSize;
+    }
+    return -1;
+}
+
+void _dyld_register_func_for_add_image(void (*func)(const mach_header *mh, intptr_t vmaddr_slide))
+{
+    log_apis("_dyld_register_func_for_add_image(%p)\n", func);
+
+    gAllImages.addLoadNotifier(func);
+}
+
+void _dyld_register_func_for_remove_image(void (*func)(const mach_header *mh, intptr_t vmaddr_slide))
+{
+    log_apis("_dyld_register_func_for_remove_image(%p)\n", func);
+
+    gAllImages.addUnloadNotifier(func);
+}
+
+void _dyld_objc_notify_register(_dyld_objc_notify_mapped    mapped,
+                                _dyld_objc_notify_init      init,
+                                _dyld_objc_notify_unmapped  unmapped)
+{
+    log_apis("_dyld_objc_notify_register(%p, %p, %p)\n", mapped, init, unmapped);
+
+    gAllImages.setObjCNotifiers(mapped, init, unmapped);
+}
+
+
+const mach_header* dyld_image_header_containing_address(const void* addr)
+{
+    log_apis("dyld_image_header_containing_address(%p)\n", addr);
+
+    const mach_header* loadAddress;
+    launch_cache::Image image = gAllImages.findByOwnedAddress(addr, &loadAddress);
+    if ( image.valid() )
+        return loadAddress;
+    return nullptr;
+}
+
+
+const char* dyld_image_path_containing_address(const void* addr)
+{
+    log_apis("dyld_image_path_containing_address(%p)\n", addr);
+
+    const mach_header* loadAddress;
+    launch_cache::Image image = gAllImages.findByOwnedAddress(addr, &loadAddress);
+    if ( image.valid() ) {
+        const char* path = gAllImages.imagePath(image.binaryData());
+        log_apis("   dyld_image_path_containing_address() => %s\n", path);
+        return path;
+    }
+    log_apis("   dyld_image_path_containing_address() => NULL\n");
+    return nullptr;
+}
+
+bool _dyld_is_memory_immutable(const void* addr, size_t length)
+{
+    uintptr_t checkStart = (uintptr_t)addr;
+    uintptr_t checkEnd   = checkStart + length;
+
+    // quick check to see if in r/o region of shared cache.  If so return true.
+    const DyldSharedCache* cache = (DyldSharedCache*)gAllImages.cacheLoadAddress();
+    if ( cache != nullptr ) {
+        __block bool firstVMAddr = 0;
+        __block bool isReadOnlyInCache = false;
+        __block bool isInCache = false;
+        cache->forEachRegion(^(const void* content, uint64_t vmAddr, uint64_t size, uint32_t permissions) {
+            if ( firstVMAddr == 0 )
+                firstVMAddr = vmAddr;
+            uintptr_t regionStart = (uintptr_t)cache + (uintptr_t)(vmAddr - firstVMAddr);
+            uintptr_t regionEnd   = regionStart + (uintptr_t)size;
+            if ( (regionStart < checkStart) && (checkEnd < regionEnd) ) {
+                isInCache = true;
+                isReadOnlyInCache = ((permissions & VM_PROT_WRITE) != 0);
+            }
+        });
+        if ( isInCache )
+            return isReadOnlyInCache;
+    }
+
+    // go slow route of looking at each image's segments
+    const mach_header* loadAddress;
+    uint8_t permissions;
+    launch_cache::Image image = gAllImages.findByOwnedAddress(addr, &loadAddress, &permissions);
+    if ( !image.valid() )
+        return false;
+    if ( (permissions & VM_PROT_WRITE) != 0 )
+        return false;
+    return !gAllImages.imageUnloadable(image, loadAddress);
+}
+
+
+int dladdr(const void* addr, Dl_info* info)
+{
+    log_apis("dladdr(%p, %p)\n", addr, info);
+
+    const mach_header* loadAddress;
+    launch_cache::Image image = gAllImages.findByOwnedAddress(addr, &loadAddress);
+    if ( !image.valid() ) {
+        log_apis("   dladdr() => 0\n");
+        return 0;
+    }
+    MachOParser parser(loadAddress);
+    info->dli_fname = gAllImages.imagePath(image.binaryData());
+    info->dli_fbase = (void*)(loadAddress);
+    if ( addr == info->dli_fbase ) {
+        // special case lookup of header
+        info->dli_sname = "__dso_handle";
+        info->dli_saddr = info->dli_fbase;
+    }
+    else if ( parser.findClosestSymbol(addr, &(info->dli_sname), (const void**)&(info->dli_saddr)) ) {
+        // never return the mach_header symbol
+        if ( info->dli_saddr == info->dli_fbase ) {
+            info->dli_sname = nullptr;
+            info->dli_saddr = nullptr;
+        }
+        // strip off leading underscore
+        else if ( (info->dli_sname != nullptr) && (info->dli_sname[0] == '_') ) {
+            info->dli_sname = info->dli_sname + 1;
+        }
+    }
+    else {
+        info->dli_sname = nullptr;
+        info->dli_saddr = nullptr;
+    }
+    log_apis("   dladdr() => 1, { \"%s\", %p, \"%s\", %p }\n", info->dli_fname, info->dli_fbase, info->dli_sname, info->dli_saddr);
+    return 1;
+}
+
+
+struct PerThreadErrorMessage
+{
+    size_t      sizeAllocated;
+    bool        valid;
+    char        message[1];
+};
+
+static pthread_key_t dlerror_perThreadKey()
+{
+    static dispatch_once_t  onceToken;
+    static pthread_key_t    dlerrorPThreadKey;
+    dispatch_once(&onceToken, ^{
+        pthread_key_create(&dlerrorPThreadKey, &free);
+    });
+    return dlerrorPThreadKey;
+}
+
+static void clearErrorString()
+{
+    PerThreadErrorMessage* errorBuffer = (PerThreadErrorMessage*)pthread_getspecific(dlerror_perThreadKey());
+    if ( errorBuffer != nullptr )
+        errorBuffer->valid = false;
+}
+
+__attribute__((format(printf, 1, 2)))
+static void setErrorString(const char* format, ...)
+{
+    _SIMPLE_STRING buf = _simple_salloc();
+    if ( buf != nullptr ) {
+        va_list    list;
+        va_start(list, format);
+        _simple_vsprintf(buf, format, list);
+        va_end(list);
+        size_t strLen = strlen(_simple_string(buf)) + 1;
+        size_t sizeNeeded = sizeof(PerThreadErrorMessage) + strLen;
+        PerThreadErrorMessage* errorBuffer = (PerThreadErrorMessage*)pthread_getspecific(dlerror_perThreadKey());
+        if ( errorBuffer != nullptr ) {
+            if ( errorBuffer->sizeAllocated < sizeNeeded ) {
+                free(errorBuffer);
+                errorBuffer = nullptr;
+            }
+        }
+        if ( errorBuffer == nullptr ) {
+            size_t allocSize = std::max(sizeNeeded, (size_t)256);
+            PerThreadErrorMessage* p = (PerThreadErrorMessage*)malloc(allocSize);
+            p->sizeAllocated = allocSize;
+            p->valid = false;
+            pthread_setspecific(dlerror_perThreadKey(), p);
+            errorBuffer = p;
+        }
+        strcpy(errorBuffer->message, _simple_string(buf));
+        errorBuffer->valid = true;
+        _simple_sfree(buf);
+    }
+}
+
+char* dlerror()
+{
+    log_apis("dlerror()\n");
+
+    PerThreadErrorMessage* errorBuffer = (PerThreadErrorMessage*)pthread_getspecific(dlerror_perThreadKey());
+    if ( errorBuffer != nullptr ) {
+        if ( errorBuffer->valid ) {
+            // you can only call dlerror() once, then the message is cleared
+            errorBuffer->valid = false;
+            return errorBuffer->message;
+        }
+    }
+    return nullptr;
+}
+
+#if __arm64__
+    #define CURRENT_CPU_TYPE CPU_TYPE_ARM64
+#elif __arm__
+    #define CURRENT_CPU_TYPE CPU_TYPE_ARM
+#endif
+
+
+class VIS_HIDDEN RecursiveAutoLock
+{
+public:
+    RecursiveAutoLock() {
+        pthread_mutex_lock(&_sMutex);
+    }
+    ~RecursiveAutoLock() {
+        pthread_mutex_unlock(&_sMutex);
+    }
+private:
+    static pthread_mutex_t _sMutex;
+};
+
+pthread_mutex_t RecursiveAutoLock::_sMutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER;
+
+static void* makeDlHandle(const mach_header* mh, bool dontContinue)
+{
+    uintptr_t flags = (dontContinue ? 1 : 0);
+    return (void*)((((uintptr_t)mh) >> 5) | flags);
+}
+
+VIS_HIDDEN
+void parseDlHandle(void* h, const mach_header** mh, bool* dontContinue)
+{
+    *dontContinue = (((uintptr_t)h) & 1);
+    *mh           = (const mach_header*)((((uintptr_t)h) & (-2)) << 5);
+}
+
+int dlclose(void* handle)
+{
+    log_apis("dlclose(%p)\n", handle);
+
+    // silently accept magic handles for main executable
+    if ( handle == RTLD_MAIN_ONLY )
+        return 0;
+    if ( handle == RTLD_DEFAULT )
+        return 0;
+    
+   // from here on, serialize all dlopen()s
+    RecursiveAutoLock dlopenSerializer;
+
+    const mach_header*  mh;
+    bool                dontContinue;
+    parseDlHandle(handle, &mh, &dontContinue);
+    launch_cache::Image image = gAllImages.findByLoadAddress(mh);
+    if ( image.valid() ) {
+        // removes image if reference count went to zero
+        if ( !image.neverUnload() )
+            gAllImages.decRefCount(mh);
+        clearErrorString();
+        return 0;
+    }
+    else {
+        setErrorString("invalid handle passed to dlclose()");
+        return -1;
+    }
+}
+
+
+
+VIS_HIDDEN
+const mach_header* loadImageAndDependents(Diagnostics& diag, const launch_cache::binary_format::Image* imageToLoad, bool bumpDlopenCount)
+{
+    launch_cache::Image topImage(imageToLoad);
+    uint32_t maxLoad = topImage.maxLoadCount();
+    // first construct array of all BinImage* objects that dlopen'ed image depends on
+    const dyld3::launch_cache::binary_format::Image*    fullImageList[maxLoad];
+    dyld3::launch_cache::SlowLoadSet imageSet(&fullImageList[0], &fullImageList[maxLoad]);
+    imageSet.add(imageToLoad);
+    STACK_ALLOC_DYNARRAY(const launch_cache::BinaryImageGroupData*, gAllImages.currentGroupsCount(), currentGroupsList);
+    gAllImages.copyCurrentGroups(currentGroupsList);
+    if ( !topImage.recurseAllDependentImages(currentGroupsList, imageSet, nullptr) ) {
+        diag.error("unexpected > %d images loaded", maxLoad);
+        return nullptr;
+    }
+
+    // build array of BinImage* that are not already loaded
+    const dyld3::launch_cache::binary_format::Image*    toLoadImageList[maxLoad];
+    const dyld3::launch_cache::binary_format::Image**   toLoadImageArray = toLoadImageList;
+    __block int needToLoadCount = 0;
+    imageSet.forEach(^(const dyld3::launch_cache::binary_format::Image* aBinImage) {
+        if ( gAllImages.findLoadAddressByImage(aBinImage) == nullptr )
+            toLoadImageArray[needToLoadCount++] = aBinImage;
+    });
+    assert(needToLoadCount > 0);
+
+    // build one array of all existing and to-be-loaded images
+    uint32_t alreadyLoadImageCount = gAllImages.count();
+    STACK_ALLOC_DYNARRAY(loader::ImageInfo, alreadyLoadImageCount + needToLoadCount, allImages);
+    loader::ImageInfo* allImagesArray = &allImages[0];
+    gAllImages.forEachImage(^(uint32_t imageIndex, const mach_header* loadAddress, const launch_cache::Image image, bool& stop) {
+        launch_cache::ImageGroup grp = image.group();
+        loader::ImageInfo&       info= allImagesArray[imageIndex];
+        info.imageData               = image.binaryData();
+        info.loadAddress             = loadAddress;
+        info.groupNum                = grp.groupNum();
+        info.indexInGroup            = grp.indexInGroup(info.imageData);
+        info.previouslyFixedUp       = true;
+        info.justMapped              = false;
+        info.justUsedFromDyldCache   = false;
+        info.neverUnload             = false;
+    });
+    for (int i=0; i < needToLoadCount; ++i) {
+        launch_cache::Image      img(toLoadImageArray[i]);
+        launch_cache::ImageGroup grp = img.group();
+        loader::ImageInfo&       info= allImages[alreadyLoadImageCount+i];
+        info.imageData               = toLoadImageArray[i];
+        info.loadAddress             = nullptr;
+        info.groupNum                = grp.groupNum();
+        info.indexInGroup            = grp.indexInGroup(img.binaryData());
+        info.previouslyFixedUp       = false;
+        info.justMapped              = false;
+        info.justUsedFromDyldCache   = false;
+        info.neverUnload             = false;
+    }
+
+    // map new images and apply all fixups
+    mapAndFixupImages(diag, allImages, (const uint8_t*)gAllImages.cacheLoadAddress(), &dyld3::log_loads, &dyld3::log_segments, &dyld3::log_fixups, &dyld3::log_dofs);
+    if ( diag.hasError() )
+         return nullptr;
+    const mach_header* topLoadAddress = allImages[alreadyLoadImageCount].loadAddress;
+
+    // bump dlopen refcount of image directly loaded
+    if ( bumpDlopenCount )
+        gAllImages.incRefCount(topLoadAddress);
+
+    // tell gAllImages about new images
+    dyld3::launch_cache::DynArray<loader::ImageInfo> newImages(needToLoadCount, &allImages[alreadyLoadImageCount]);
+    gAllImages.addImages(newImages);
+
+    // tell gAllImages about any old images which now have never unload set
+    for (int i=0; i < alreadyLoadImageCount; ++i) {
+        if (allImages[i].neverUnload && !allImages[i].imageData->neverUnload)
+            gAllImages.setNeverUnload(allImages[i]);
+    }
+
+    // run initializers
+    gAllImages.runInitialzersBottomUp(topLoadAddress);
+
+    return topLoadAddress;
+}
+
+
+void* dlopen(const char* path, int mode)
+{    
+    log_apis("dlopen(\"%s\", 0x%08X)\n", ((path==NULL) ? "NULL" : path), mode);
+
+    clearErrorString();
+
+    // 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;
+    }
+
+    // from here on, serialize all dlopen()s
+    RecursiveAutoLock dlopenSerializer;
+
+    const char* leafName = strrchr(path, '/');
+    if ( leafName != nullptr )
+        ++leafName;
+    else
+        leafName = path;
+
+    // RTLD_FIRST means when dlsym() is called with handle, only search the image and not those loaded after it
+    bool dontContinue = (mode & RTLD_FIRST);
+    bool bumpRefCount = true;
+
+    // check if dylib with same inode/mtime is already loaded
+    __block const mach_header* alreadyLoadMH = nullptr;
+    struct stat statBuf;
+    if ( stat(path, &statBuf) == 0 ) {
+        alreadyLoadMH = gAllImages.alreadyLoaded(statBuf.st_ino, statBuf.st_mtime, bumpRefCount);
+        if ( alreadyLoadMH != nullptr) {
+            log_apis("   dlopen: path inode/mtime matches already loaded image\n");
+            void* result = makeDlHandle(alreadyLoadMH, dontContinue);
+            log_apis("   dlopen(%s) => %p\n", leafName, result);
+            return result;
+        }
+    }
+
+    // check if already loaded, and if so, just bump ref-count
+    gPathOverrides.forEachPathVariant(path, ^(const char* possiblePath, bool& stop) {
+        alreadyLoadMH = gAllImages.alreadyLoaded(possiblePath, bumpRefCount);
+        if ( alreadyLoadMH != nullptr ) {
+            log_apis("   dlopen: matches already loaded image %s\n", possiblePath);
+            stop = true;
+        }
+    });
+    if ( alreadyLoadMH != nullptr) {
+        void* result = makeDlHandle(alreadyLoadMH, dontContinue);
+        log_apis("   dlopen(%s) => %p\n", leafName, result);
+        return result;
+    }
+
+    // it may be that the path supplied is a symlink to something already loaded
+    char resolvedPath[PATH_MAX];
+    const char* realPathResult = realpath(path, resolvedPath);
+    // If realpath() resolves to a path which does not exist on disk, errno is set to ENOENT
+    bool checkRealPathToo = ((realPathResult != nullptr) || (errno == ENOENT)) && (strcmp(path, resolvedPath) != 0);
+    if ( checkRealPathToo ) {
+        alreadyLoadMH = gAllImages.alreadyLoaded(resolvedPath, bumpRefCount);
+        log_apis("   dlopen: real path=%s\n", resolvedPath);
+        if ( alreadyLoadMH != nullptr) {
+            void* result = makeDlHandle(alreadyLoadMH, dontContinue);
+            log_apis("   dlopen(%s) => %p\n", leafName, result);
+            return result;
+        }
+    }
+
+    // check if image is in a known ImageGroup
+    __block const launch_cache::binary_format::Image* imageToLoad = nullptr;
+    gPathOverrides.forEachPathVariant(path, ^(const char* possiblePath, bool& stop) {
+        log_apis("   dlopen: checking for pre-built closure for path: %s\n", possiblePath);
+        imageToLoad = gAllImages.findImageInKnownGroups(possiblePath);
+        if ( imageToLoad != nullptr )
+            stop = true;
+    });
+    if ( (imageToLoad == nullptr) && checkRealPathToo ) {
+        gPathOverrides.forEachPathVariant(resolvedPath, ^(const char* possiblePath, bool& stop) {
+            log_apis("   dlopen: checking for pre-built closure for real path: %s\n", possiblePath);
+            imageToLoad = gAllImages.findImageInKnownGroups(possiblePath);
+            if ( imageToLoad != nullptr )
+                stop = true;
+        });
+    }
+
+    // check if image from a known ImageGroup is already loaded (via a different path)
+    if ( imageToLoad != nullptr ) {
+        alreadyLoadMH = gAllImages.alreadyLoaded(imageToLoad, bumpRefCount);
+        if ( alreadyLoadMH != nullptr) {
+            void* result = makeDlHandle(alreadyLoadMH, dontContinue);
+            log_apis("   dlopen(%s) => %p\n", leafName, result);
+            return result;
+        }
+    }
+
+    // RTLD_NOLOAD means do nothing if image not already loaded
+    if ( mode & RTLD_NOLOAD ) {
+        log_apis("   dlopen(%s) => NULL\n", leafName);
+        return nullptr;
+    }
+
+    // if we have a closure, optimistically use it.  If out of date, it will fail
+    if ( imageToLoad != nullptr ) {
+        log_apis("   dlopen: trying existing closure image=%p\n", imageToLoad);
+        Diagnostics diag;
+        const mach_header* topLoadAddress = loadImageAndDependents(diag, imageToLoad, true);
+        if ( diag.noError() ) {
+            void* result = makeDlHandle(topLoadAddress, dontContinue);
+            log_apis("   dlopen(%s) => %p\n", leafName, result);
+            return result;
+        }
+        // image is no longer valid, will need to build one
+        imageToLoad = nullptr;
+        log_apis("   dlopen: existing closure no longer valid\n");
+    }
+
+    // if no existing closure, RPC to closured to create one
+    const char* closuredErrorMessages[3];
+    int closuredErrorMessagesCount = 0;
+    if ( imageToLoad == nullptr ) {
+        imageToLoad = gAllImages.messageClosured(path, "dlopen", closuredErrorMessages, closuredErrorMessagesCount);
+    }
+
+    // load images using new closure
+    if ( imageToLoad != nullptr ) {
+        log_apis("   dlopen: using closured built image=%p\n", imageToLoad);
+        Diagnostics diag;
+        const mach_header* topLoadAddress = loadImageAndDependents(diag, imageToLoad, true);
+        if ( diag.noError() ) {
+            void* result = makeDlHandle(topLoadAddress, dontContinue);
+            log_apis("   dlopen(%s) => %p\n", leafName, result);
+            return result;
+        }
+        if ( closuredErrorMessagesCount < 3 ) {
+            closuredErrorMessages[closuredErrorMessagesCount++] = strdup(diag.errorMessage());
+        }
+    }
+
+    // otherwise, closured failed to build needed load info
+    switch ( closuredErrorMessagesCount ) {
+        case 0:
+            setErrorString("dlopen(%s, 0x%04X): closured error", path, mode);
+            log_apis("   dlopen: closured error\n");
+            break;
+        case 1:
+            setErrorString("dlopen(%s, 0x%04X): %s", path, mode, closuredErrorMessages[0]);
+            log_apis("   dlopen: closured error: %s\n", closuredErrorMessages[0]);
+            break;
+        case 2:
+            setErrorString("dlopen(%s, 0x%04X): %s %s", path, mode, closuredErrorMessages[0], closuredErrorMessages[1]);
+            log_apis("   dlopen: closured error: %s %s\n", closuredErrorMessages[0], closuredErrorMessages[1]);
+            break;
+        case 3:
+            setErrorString("dlopen(%s, 0x%04X): %s %s %s", path, mode, closuredErrorMessages[0], closuredErrorMessages[1], closuredErrorMessages[2]);
+            log_apis("   dlopen: closured error: %s %s %s\n", closuredErrorMessages[0], closuredErrorMessages[1], closuredErrorMessages[2]);
+            break;
+    }
+    for (int i=0; i < closuredErrorMessagesCount;++i)
+        free((void*)closuredErrorMessages[i]);
+
+    log_apis("   dlopen(%s) => NULL\n", leafName);
+
+    return nullptr;
+}
+
+bool dlopen_preflight(const char* path)
+{
+    log_apis("dlopen_preflight(%s)\n", path);
+
+    if ( gAllImages.alreadyLoaded(path, false) != nullptr )
+        return true;
+
+    if ( gAllImages.findImageInKnownGroups(path) != nullptr )
+        return true;
+
+    // map whole file
+    struct stat statBuf;
+    if ( ::stat(path, &statBuf) != 0 )
+        return false;
+    int fd = ::open(path, O_RDONLY);
+    if ( fd < 0 )
+        return false;
+    const void* fileBuffer = ::mmap(NULL, (size_t)statBuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+    ::close(fd);
+    if ( fileBuffer == MAP_FAILED )
+        return false;
+    size_t mappedSize = (size_t)statBuf.st_size;
+
+    // check if it is current arch mach-o or fat with slice for current arch
+    __block bool result = false;
+    __block Diagnostics diag;
+    if ( MachOParser::isMachO(diag, fileBuffer, mappedSize) ) {
+        result = true;
+    }
+    else {
+        if ( FatUtil::isFatFile(fileBuffer) ) {
+            FatUtil::forEachSlice(diag, fileBuffer, mappedSize, ^(uint32_t sliceCpuType, uint32_t sliceCpuSubType, const void* sliceStart, size_t sliceSz, bool& stop) {
+                if ( MachOParser::isMachO(diag, sliceStart, sliceSz) ) {
+                    result = true;
+                    stop = true;
+                }
+            });
+        }
+    }
+    ::munmap((void*)fileBuffer, mappedSize);
+
+    // FIXME: may be symlink to something in dyld cache
+
+    // FIXME: maybe ask closured
+
+    return result;
+}
+
+static void* dlsym_search(const char* symName, const mach_header* startImageLoadAddress, const launch_cache::Image& startImage, bool searchStartImage, MachOParser::DependentFinder reExportFollower)
+{
+    // construct array of all BinImage* objects that dlopen'ed image depends on
+    uint32_t maxLoad = startImage.maxLoadCount();
+    const dyld3::launch_cache::binary_format::Image*    fullImageList[maxLoad];
+    dyld3::launch_cache::SlowLoadSet imageSet(&fullImageList[0], &fullImageList[maxLoad]);
+    imageSet.add(startImage.binaryData());
+    STACK_ALLOC_DYNARRAY(const launch_cache::BinaryImageGroupData*, gAllImages.currentGroupsCount(), currentGroupsList);
+    gAllImages.copyCurrentGroups(currentGroupsList);
+
+    __block void* result = nullptr;
+    auto handler = ^(const dyld3::launch_cache::binary_format::Image* aBinImage, bool& stop) {
+        const mach_header* loadAddress = gAllImages.findLoadAddressByImage(aBinImage);
+        if ( !searchStartImage && (loadAddress == startImageLoadAddress) )
+            return;
+        if ( loadAddress != nullptr ) {
+            MachOParser parser(loadAddress);
+            if ( parser.hasExportedSymbol(symName, reExportFollower, &result) ) {
+                stop = true;
+            }
+        }
+    };
+
+    bool stop = false;
+    handler(startImage.binaryData(), stop);
+    if (stop)
+        return result;
+
+    // check each dependent image for symbol
+    if ( !startImage.recurseAllDependentImages(currentGroupsList, imageSet, handler) ) {
+        setErrorString("unexpected > %d images loaded", maxLoad);
+        return nullptr;
+    }
+    return result;
+}
+
+void* dlsym(void* handle, const char* symbolName)
+{
+    log_apis("dlsym(%p, \"%s\")\n", handle, symbolName);
+
+    clearErrorString();
+
+    // 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);
+
+    // this block is only used if hasExportedSymbol() needs to trace re-exported dylibs to find a symbol
+    MachOParser::DependentFinder reExportFollower = ^(uint32_t targetDepIndex, const char* depLoadPath, void* extra, const mach_header** foundMH, void** foundExtra) {
+        if ( (strncmp(depLoadPath, "@rpath/", 7) == 0) && (extra != nullptr) ) {
+            const mach_header* parentMH = (mach_header*)extra;
+            launch_cache::Image parentImage = gAllImages.findByLoadAddress(parentMH);
+            if ( parentImage.valid() ) {
+                STACK_ALLOC_DYNARRAY(const launch_cache::BinaryImageGroupData*, gAllImages.currentGroupsCount(), currentGroupsList);
+                gAllImages.copyCurrentGroups(currentGroupsList);
+                parentImage.forEachDependentImage(currentGroupsList, ^(uint32_t parentDepIndex, dyld3::launch_cache::Image parentDepImage, dyld3::launch_cache::Image::LinkKind kind, bool &stop) {
+                    if ( parentDepIndex != targetDepIndex )
+                        return;
+                    const mach_header* parentDepMH = gAllImages.findLoadAddressByImage(parentDepImage.binaryData());
+                    if ( parentDepMH != nullptr ) {
+                        *foundMH = parentDepMH;
+                        stop = true;
+                    }
+                });
+            }
+        }
+        else {
+            *foundMH = gAllImages.alreadyLoaded(depLoadPath, false);
+        }
+        return (*foundMH != nullptr);
+    };
+
+    if ( handle == RTLD_DEFAULT ) {
+        // magic "search all in load order" handle
+        for (uint32_t index=0; index < gAllImages.count(); ++index) {
+            const mach_header* loadAddress;
+            launch_cache::Image image = gAllImages.findByLoadOrder(index, &loadAddress);
+            if ( image.valid() ) {
+                MachOParser parser(loadAddress);
+                void* result;
+                //log_apis("   dlsym(): index=%d, loadAddress=%p\n", index, loadAddress);
+                if ( parser.hasExportedSymbol(underscoredName, reExportFollower, &result) ) {
+                    log_apis("   dlsym() => %p\n", result);
+                    return result;
+                }
+            }
+        }
+        setErrorString("dlsym(RTLD_DEFAULT, %s): symbol not found", symbolName);
+        log_apis("   dlsym() => NULL\n");
+        return nullptr;
+    }
+    else if ( handle == RTLD_MAIN_ONLY ) {
+        // magic "search only main executable" handle
+        MachOParser parser(gAllImages.mainExecutable());
+        //log_apis("   dlsym(): index=%d, loadAddress=%p\n", index, loadAddress);
+        void* result;
+        if ( parser.hasExportedSymbol(underscoredName, reExportFollower, &result) ) {
+            log_apis("   dlsym() => %p\n", result);
+            return result;
+        }
+        setErrorString("dlsym(RTLD_MAIN_ONLY, %s): symbol not found", symbolName);
+        log_apis("   dlsym() => NULL\n");
+        return nullptr;
+    }
+
+    // rest of cases search in dependency order
+    const mach_header* startImageLoadAddress;
+    launch_cache::Image startImage(nullptr);
+    void* result = nullptr;
+    if ( handle == RTLD_NEXT ) {
+        // magic "search what I would see" handle
+        void* callerAddress = __builtin_return_address(0);
+        startImage = gAllImages.findByOwnedAddress(callerAddress, &startImageLoadAddress);
+        if ( ! startImage.valid() ) {
+            setErrorString("dlsym(RTLD_NEXT, %s): called by unknown image (caller=%p)", symbolName, callerAddress);
+            return nullptr;
+        }
+        result = dlsym_search(underscoredName, startImageLoadAddress, startImage, false, reExportFollower);
+    }
+    else if ( handle == RTLD_SELF ) {
+        // magic "search me, then what I would see" handle
+        void* callerAddress = __builtin_return_address(0);
+        startImage = gAllImages.findByOwnedAddress(callerAddress, &startImageLoadAddress);
+        if ( ! startImage.valid() ) {
+            setErrorString("dlsym(RTLD_SELF, %s): called by unknown image (caller=%p)", symbolName, callerAddress);
+            return nullptr;
+        }
+        result = dlsym_search(underscoredName, startImageLoadAddress, startImage, true, reExportFollower);
+    }
+    else {
+        // handle value was something returned by dlopen()
+        bool dontContinue;
+        parseDlHandle(handle, &startImageLoadAddress, &dontContinue);
+        startImage = gAllImages.findByLoadAddress(startImageLoadAddress);
+        if ( !startImage.valid() ) {
+            setErrorString("dlsym(%p, %s): invalid handle", handle, symbolName);
+            log_apis("   dlsym() => NULL\n");
+            return nullptr;
+        }
+        if ( dontContinue ) {
+            // RTLD_FIRST only searches one place
+            MachOParser parser(startImageLoadAddress);
+           parser.hasExportedSymbol(underscoredName, reExportFollower, &result);
+        }
+        else {
+            result = dlsym_search(underscoredName, startImageLoadAddress, startImage, true, reExportFollower);
+        }
+    }
+
+    if ( result != nullptr ) {
+        log_apis("   dlsym() => %p\n", result);
+        return result;
+    }
+
+    setErrorString("dlsym(%p, %s): symbol not found", handle, symbolName);
+    log_apis("   dlsym() => NULL\n");
+    return nullptr;
+}
+
+
+const struct dyld_all_image_infos* _dyld_get_all_image_infos()
+{
+    return gAllImages.oldAllImageInfo();
+}
+
+bool dyld_shared_cache_some_image_overridden()
+{
+    log_apis("dyld_shared_cache_some_image_overridden()\n");
+
+    assert(0 && "not implemented yet");
+}
+
+bool _dyld_get_shared_cache_uuid(uuid_t uuid)
+{
+    log_apis("_dyld_get_shared_cache_uuid()\n");
+
+    if ( gAllImages.oldAllImageInfo() != nullptr ) {
+        memcpy(uuid, gAllImages.oldAllImageInfo()->sharedCacheUUID, sizeof(uuid_t));
+        return true;
+    }
+    return false;
+}
+
+const void* _dyld_get_shared_cache_range(size_t* mappedSize)
+{
+    log_apis("_dyld_get_shared_cache_range()\n");
+
+    const DyldSharedCache* sharedCache = (DyldSharedCache*)gAllImages.cacheLoadAddress();
+    if ( sharedCache != nullptr ) {
+        *mappedSize = (size_t)sharedCache->mappedSize();
+        return sharedCache;
+    }
+    *mappedSize = 0;
+    return NULL;
+}
+
+bool _dyld_find_unwind_sections(void* addr, dyld_unwind_sections* info)
+{
+    log_apis("_dyld_find_unwind_sections(%p, %p)\n", addr, info);
+
+    const mach_header* mh = dyld_image_header_containing_address(addr);
+    if ( mh == nullptr )
+        return false;
+
+    info->mh                            = mh;
+    info->dwarf_section                 = nullptr;
+    info->dwarf_section_length          = 0;
+    info->compact_unwind_section        = nullptr;
+    info->compact_unwind_section_length = 0;
+
+    MachOParser parser(mh);
+    parser.forEachSection(^(const char* segName, const char* sectName, uint32_t flags, const void* content, size_t sectSize, bool illegalSectionSize, bool& stop) {
+        if ( strcmp(segName, "__TEXT") == 0 ) {
+            if ( strcmp(sectName, "__eh_frame") == 0 ) {
+                info->dwarf_section         = content;
+                info->dwarf_section_length  = sectSize;
+            }
+            else if ( strcmp(sectName, "__unwind_info") == 0 ) {
+                info->compact_unwind_section         = content;
+                info->compact_unwind_section_length  = sectSize;
+            }
+        }
+    });
+
+    return true;
+}
+
+
+bool dyld_process_is_restricted()
+{
+    log_apis("dyld_process_is_restricted()\n");
+
+    launch_cache::Closure closure(gAllImages.mainClosure());
+    return closure.isRestricted();
+}
+
+
+const char* dyld_shared_cache_file_path()
+{
+    log_apis("dyld_shared_cache_file_path()\n");
+
+    return gAllImages.dyldCachePath();
+}
+
+
+void dyld_dynamic_interpose(const mach_header* mh, const dyld_interpose_tuple array[], size_t count)
+{
+    log_apis("dyld_dynamic_interpose(%p, %p, %lu)\n", mh, array, count);
+    // FIXME
+}
+
+
+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 DyldSharedCache* findCacheInDirAndMap(const uuid_t cacheUuid, const char* dirPath, size_t& sizeMapped)
+{
+    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 DyldSharedCache* cache = (DyldSharedCache*)mapStartOfCache(cachePath, 0x00100000) ) {
+                uuid_t foundUuid;
+                cache->getUUID(foundUuid);
+                if ( ::memcmp(foundUuid, cacheUuid, 16) != 0 ) {
+                    // wrong uuid, unmap and keep looking
+                    ::munmap((void*)cache, 0x00100000);
+                }
+                else {
+                    // found cache
+                    closedir(dirp);
+                    sizeMapped = 0x00100000;
+                    return cache;
+                }
+            }
+        }
+        closedir(dirp);
+    }
+    return nullptr;
+}
+
+int dyld_shared_cache_find_iterate_text(const uuid_t cacheUuid, const char* extraSearchDirs[], void (^callback)(const dyld_shared_cache_dylib_text_info* info))
+{
+    log_apis("dyld_shared_cache_find_iterate_text()\n");
+
+    // see if requested cache is the active one in this process
+    size_t sizeMapped = 0;
+    const DyldSharedCache* sharedCache = (DyldSharedCache*)gAllImages.cacheLoadAddress();
+    if ( sharedCache != nullptr ) {
+        uuid_t runningUuid;
+        sharedCache->getUUID(runningUuid);
+        if ( ::memcmp(runningUuid, cacheUuid, 16) != 0 )
+            sharedCache = nullptr;
+    }
+    if ( sharedCache == nullptr ) {
+         // if not, look in 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
+        sharedCache = findCacheInDirAndMap(cacheUuid, defaultSearchDir, sizeMapped);
+        // if not there, look in extra search locations
+        if ( sharedCache == nullptr ) {
+            for (const char** p = extraSearchDirs; *p != nullptr; ++p) {
+                sharedCache = findCacheInDirAndMap(cacheUuid, *p, sizeMapped);
+                if ( sharedCache != nullptr )
+                    break;
+            }
+        }
+    }
+    if ( sharedCache == nullptr )
+        return -1;
+
+    // get base address of cache
+    __block uint64_t cacheUnslidBaseAddress = 0;
+    sharedCache->forEachRegion(^(const void *content, uint64_t vmAddr, uint64_t size, uint32_t permissions) {
+        if ( cacheUnslidBaseAddress == 0 )
+            cacheUnslidBaseAddress = vmAddr;
+    });
+
+    // iterate all images
+    sharedCache->forEachImageTextSegment(^(uint64_t loadAddressUnslid, uint64_t textSegmentSize, const uuid_t dylibUUID, const char* installName) {
+        dyld_shared_cache_dylib_text_info dylibTextInfo;
+        dylibTextInfo.version              = 2;
+        dylibTextInfo.loadAddressUnslid    = loadAddressUnslid;
+        dylibTextInfo.textSegmentSize      = textSegmentSize;
+        dylibTextInfo.path                 = installName;
+        ::memcpy(dylibTextInfo.dylibUuid, dylibUUID, 16);
+        dylibTextInfo.textSegmentOffset    = loadAddressUnslid - cacheUnslidBaseAddress;
+        callback(&dylibTextInfo);
+    });
+
+    if ( sizeMapped != 0 )
+        ::munmap((void*)sharedCache, sizeMapped);
+
+    return 0;
+}
+
+int dyld_shared_cache_iterate_text(const uuid_t cacheUuid, void (^callback)(const dyld_shared_cache_dylib_text_info* info))
+{
+    log_apis("dyld_shared_cache_iterate_text()\n");
+
+    const char* extraSearchDirs[] = { NULL };
+    return dyld3::dyld_shared_cache_find_iterate_text(cacheUuid, extraSearchDirs, callback);
+}
+
+
+
+} // namespace dyld3
+
diff --git a/dyld3/APIs.h b/dyld3/APIs.h
new file mode 100644 (file)
index 0000000..544f090
--- /dev/null
@@ -0,0 +1,179 @@
+/*
+ * Copyright (c) 2017 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_APIS_H__
+#define __DYLD_APIS_H__
+
+#include <string.h>
+#include <stdint.h>
+
+#include "dlfcn.h"
+#include "dyld_priv.h"
+
+
+#define TEMP_HIDDEN __attribute__((visibility("hidden")))
+
+namespace dyld3 {
+
+
+uint32_t _dyld_image_count() TEMP_HIDDEN;
+
+const mach_header* _dyld_get_image_header(uint32_t imageIndex) TEMP_HIDDEN;
+
+intptr_t _dyld_get_image_slide(const mach_header* mh) TEMP_HIDDEN;
+
+intptr_t _dyld_get_image_vmaddr_slide(uint32_t imageIndex) TEMP_HIDDEN;
+
+const char* _dyld_get_image_name(uint32_t imageIndex) TEMP_HIDDEN;
+
+int32_t NSVersionOfLinkTimeLibrary(const char* libraryName) TEMP_HIDDEN;
+
+int32_t NSVersionOfRunTimeLibrary(const char* libraryName) TEMP_HIDDEN;
+
+#if __WATCH_OS_VERSION_MIN_REQUIRED
+uint32_t dyld_get_program_sdk_watch_os_version() TEMP_HIDDEN;
+
+uint32_t dyld_get_program_min_watch_os_version() TEMP_HIDDEN;
+#endif
+
+#if TARGET_OS_BRIDGE
+uint32_t dyld_get_program_sdk_bridge_os_version() TEMP_HIDDEN;
+
+uint32_t dyld_get_program_min_bridge_os_version() TEMP_HIDDEN;
+#endif
+
+
+uint32_t dyld_get_sdk_version(const mach_header* mh) TEMP_HIDDEN;
+
+
+uint32_t dyld_get_program_sdk_version() TEMP_HIDDEN;
+uint32_t dyld_get_min_os_version(const mach_header* mh) TEMP_HIDDEN;
+
+uint32_t dyld_get_program_min_os_version() TEMP_HIDDEN;
+
+
+bool _dyld_get_image_uuid(const mach_header* mh, uuid_t uuid) TEMP_HIDDEN;
+
+int _NSGetExecutablePath(char* buf, uint32_t* bufsize) TEMP_HIDDEN;
+
+void _dyld_register_func_for_add_image(void (*func)(const mach_header *mh, intptr_t vmaddr_slide)) TEMP_HIDDEN;
+
+void _dyld_register_func_for_remove_image(void (*func)(const mach_header *mh, intptr_t vmaddr_slide)) TEMP_HIDDEN;
+
+void _dyld_objc_notify_register(_dyld_objc_notify_mapped    mapped,
+                                _dyld_objc_notify_init      init,
+                                _dyld_objc_notify_unmapped  unmapped) TEMP_HIDDEN;
+
+const mach_header* dyld_image_header_containing_address(const void* addr) TEMP_HIDDEN;
+
+const mach_header* _dyld_get_image_header_containing_address(const void* address) TEMP_HIDDEN;
+
+bool _dyld_image_containing_address(const void* address) TEMP_HIDDEN;
+
+const char* dyld_image_path_containing_address(const void* addr) TEMP_HIDDEN;
+
+bool _dyld_is_memory_immutable(const void* addr, size_t length) TEMP_HIDDEN;
+
+
+int dladdr(const void* addr, Dl_info* info) TEMP_HIDDEN;
+
+char* dlerror() TEMP_HIDDEN;
+
+int dlclose(void* handle) TEMP_HIDDEN;
+
+void* dlopen(const char* path, int mode) TEMP_HIDDEN;
+
+bool dlopen_preflight(const char* path) TEMP_HIDDEN;
+
+void* dlsym(void* handle, const char* symbolName) TEMP_HIDDEN;
+
+const struct dyld_all_image_infos* _dyld_get_all_image_infos() TEMP_HIDDEN;
+
+bool dyld_shared_cache_some_image_overridden() TEMP_HIDDEN;
+
+bool _dyld_get_shared_cache_uuid(uuid_t uuid) TEMP_HIDDEN;
+
+const void* _dyld_get_shared_cache_range(size_t* length) TEMP_HIDDEN;
+
+bool _dyld_find_unwind_sections(void* addr, dyld_unwind_sections* info) TEMP_HIDDEN;
+
+bool dyld_process_is_restricted() TEMP_HIDDEN;
+
+const char* dyld_shared_cache_file_path() TEMP_HIDDEN;
+
+void dyld_dynamic_interpose(const mach_header* mh, const dyld_interpose_tuple array[], size_t count) TEMP_HIDDEN;
+
+int dyld_shared_cache_find_iterate_text(const uuid_t cacheUuid, const char* extraSearchDirs[], void (^callback)(const dyld_shared_cache_dylib_text_info* info)) TEMP_HIDDEN;
+
+int dyld_shared_cache_iterate_text(const uuid_t cacheUuid, void (^callback)(const dyld_shared_cache_dylib_text_info* info)) TEMP_HIDDEN;
+
+void _dyld_fork_child() TEMP_HIDDEN;
+
+// only in macOS and deprecated 
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
+NSObjectFileImageReturnCode NSCreateObjectFileImageFromFile(const char* pathName, NSObjectFileImage *objectFileImage) TEMP_HIDDEN;
+NSObjectFileImageReturnCode NSCreateObjectFileImageFromMemory(const void *address, size_t size, NSObjectFileImage *objectFileImage) TEMP_HIDDEN;
+bool NSDestroyObjectFileImage(NSObjectFileImage objectFileImage) TEMP_HIDDEN;
+uint32_t NSSymbolDefinitionCountInObjectFileImage(NSObjectFileImage objectFileImage) TEMP_HIDDEN;
+const char*  NSSymbolDefinitionNameInObjectFileImage(NSObjectFileImage objectFileImage, uint32_t ordinal) TEMP_HIDDEN;
+uint32_t NSSymbolReferenceCountInObjectFileImage(NSObjectFileImage objectFileImage) TEMP_HIDDEN;
+const char*  NSSymbolReferenceNameInObjectFileImage(NSObjectFileImage objectFileImage, uint32_t ordinal, bool *tentative_definition) TEMP_HIDDEN;
+bool NSIsSymbolDefinedInObjectFileImage(NSObjectFileImage objectFileImage, const char* symbolName) TEMP_HIDDEN;
+void* NSGetSectionDataInObjectFileImage(NSObjectFileImage objectFileImage, const char* segmentName, const char* sectionName, size_t *size) TEMP_HIDDEN;
+const char* NSNameOfModule(NSModule m) TEMP_HIDDEN;
+const char* NSLibraryNameForModule(NSModule m) TEMP_HIDDEN;
+NSModule NSLinkModule(NSObjectFileImage objectFileImage, const char* moduleName, uint32_t options) TEMP_HIDDEN;
+bool NSUnLinkModule(NSModule module, uint32_t options) TEMP_HIDDEN;
+bool NSIsSymbolNameDefined(const char* symbolName) TEMP_HIDDEN;
+bool NSIsSymbolNameDefinedWithHint(const char* symbolName, const char* libraryNameHint) TEMP_HIDDEN;
+bool NSIsSymbolNameDefinedInImage(const struct mach_header* image, const char* symbolName) TEMP_HIDDEN;
+NSSymbol NSLookupAndBindSymbol(const char* symbolName) TEMP_HIDDEN;
+NSSymbol NSLookupAndBindSymbolWithHint(const char* symbolName, const char* libraryNameHint) TEMP_HIDDEN;
+NSSymbol NSLookupSymbolInModule(NSModule module, const char* symbolName) TEMP_HIDDEN;
+NSSymbol NSLookupSymbolInImage(const struct mach_header* image, const char* symbolName, uint32_t options) TEMP_HIDDEN;
+const char*  NSNameOfSymbol(NSSymbol symbol) TEMP_HIDDEN;
+void* NSAddressOfSymbol(NSSymbol symbol) TEMP_HIDDEN;
+NSModule NSModuleForSymbol(NSSymbol symbol) TEMP_HIDDEN;
+void NSLinkEditError(NSLinkEditErrors *c, int *errorNumber, const char** fileName, const char** errorString) TEMP_HIDDEN;
+bool  NSAddLibrary(const char* pathName) TEMP_HIDDEN;
+bool NSAddLibraryWithSearching(const char* pathName) TEMP_HIDDEN;
+const struct mach_header* NSAddImage(const char* image_name, uint32_t options) TEMP_HIDDEN;
+void NSInstallLinkEditErrorHandlers(const NSLinkEditErrorHandlers *handlers) TEMP_HIDDEN;
+bool _dyld_present(void) TEMP_HIDDEN;
+bool _dyld_launched_prebound(void)   TEMP_HIDDEN;
+bool _dyld_all_twolevel_modules_prebound(void) TEMP_HIDDEN;
+bool _dyld_bind_fully_image_containing_address(const void* address)  TEMP_HIDDEN;
+bool _dyld_image_containing_address(const void* address) TEMP_HIDDEN;
+void _dyld_lookup_and_bind(const char* symbol_name, void **address, NSModule* module) TEMP_HIDDEN;
+void _dyld_lookup_and_bind_with_hint(const char* symbol_name, const char* library_name_hint, void** address, NSModule* module) TEMP_HIDDEN;
+void _dyld_lookup_and_bind_fully(const char* symbol_name, void** address, NSModule* module) TEMP_HIDDEN;
+const struct mach_header*  _dyld_get_image_header_containing_address(const void* address) TEMP_HIDDEN;
+#endif
+
+} // namespace dyld3
+
+#endif // __DYLD_APIS_H__
+
+
diff --git a/dyld3/APIs_macOS.cpp b/dyld3/APIs_macOS.cpp
new file mode 100644 (file)
index 0000000..41fdff3
--- /dev/null
@@ -0,0 +1,617 @@
+/*
+ * Copyright (c) 2017 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 <string.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <_simple.h>
+#include <sys/errno.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <TargetConditionals.h>
+#include <malloc/malloc.h>
+
+#include <algorithm>
+
+#include "dlfcn.h"
+#include "dyld_priv.h"
+
+#include "AllImages.h"
+#include "MachOParser.h"
+#include "Loading.h"
+#include "Logging.h"
+#include "Diagnostics.h"
+#include "DyldSharedCache.h"
+#include "APIs.h"
+
+
+
+typedef dyld3::launch_cache::binary_format::Image BinaryImage;
+
+
+namespace dyld3 {
+
+// from APIs.cpp
+void                                        parseDlHandle(void* h, const mach_header** mh, bool* dontContinue);
+const mach_header*                          loadImageAndDependents(Diagnostics& diag, const launch_cache::binary_format::Image* imageToLoad, bool bumpDlopenCount);
+
+
+// only in macOS and deprecated 
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
+
+// macOS needs to support an old API that only works with fileype==MH_BUNDLE.
+// In this deprecated API (unlike dlopen), loading and linking are separate steps.
+// NSCreateObjectFileImageFrom*() just maps in the bundle mach-o file.
+// NSLinkModule() does the load of dependent modules and rebasing/binding.
+// To unload one of these, you must call NSUnLinkModule() and NSDestroyObjectFileImage() in any order!
+//
+
+NSObjectFileImageReturnCode NSCreateObjectFileImageFromFile(const char* path, NSObjectFileImage* ofi)
+{
+    log_apis("NSCreateObjectFileImageFromFile(\"%s\", %p)\n", path, ofi);
+
+    // verify path exists
+     struct stat statbuf;
+    if ( ::stat(path, &statbuf) == -1 )
+        return NSObjectFileImageFailure;
+
+    // create ofi that just contains path. NSLinkModule does all the work
+    __NSObjectFileImage* result = gAllImages.addNSObjectFileImage();
+    result->path        = strdup(path);
+    result->memSource   = nullptr;
+    result->memLength   = 0;
+    result->loadAddress = nullptr;
+    result->binImage    = nullptr;
+    *ofi = result;
+
+    log_apis("NSCreateObjectFileImageFromFile() => %p\n", result);
+
+    return NSObjectFileImageSuccess;
+}
+
+NSObjectFileImageReturnCode NSCreateObjectFileImageFromMemory(const void* memImage, size_t memImageSize, NSObjectFileImage *ofi)
+{
+    log_apis("NSCreateObjectFileImageFromMemory(%p, 0x%0lX, %p)\n", memImage, memImageSize, ofi);
+
+    // sanity check the buffer is a mach-o file
+    __block Diagnostics diag;
+    __block const mach_header* foundMH = nullptr;
+    if ( MachOParser::isMachO(diag, memImage, memImageSize) ) {
+        foundMH = (mach_header*)memImage;
+    }
+    else {
+        FatUtil::forEachSlice(diag, memImage, memImageSize, ^(uint32_t sliceCpuType, uint32_t sliceCpuSubType, const void* sliceStart, size_t sliceSize, bool& stop) {
+            if ( MachOParser::isMachO(diag, sliceStart, sliceSize) ) {
+                foundMH = (mach_header*)sliceStart;
+                stop = true;
+            }
+        });
+    }
+    if ( foundMH == nullptr ) {
+        log_apis("NSCreateObjectFileImageFromMemory() not mach-o\n");
+        return NSObjectFileImageFailure;
+    }
+
+    // this API can only be used with bundles
+    if ( foundMH->filetype != MH_BUNDLE ) {
+        log_apis("NSCreateObjectFileImageFromMemory() not a bundle, filetype=%d\n", foundMH->filetype);
+        return NSObjectFileImageInappropriateFile;
+    }
+
+    // allocate ofi that just lists the memory range
+    __NSObjectFileImage* result = gAllImages.addNSObjectFileImage();
+    result->path        = nullptr;
+    result->memSource   = memImage;
+    result->memLength   = memImageSize;
+    result->loadAddress = nullptr;
+    result->binImage    = nullptr;
+    *ofi = result;
+
+    log_apis("NSCreateObjectFileImageFromMemory() => %p\n", result);
+
+    return NSObjectFileImageSuccess;
+}
+
+NSModule NSLinkModule(NSObjectFileImage ofi, const char* moduleName, uint32_t options)
+{
+    log_apis("NSLinkModule(%p, \"%s\", 0x%08X)\n", ofi, moduleName, options);
+
+    // ofi is invalid if not in list
+    if ( !gAllImages.hasNSObjectFileImage(ofi) ) {
+        log_apis("NSLinkModule() => NULL (invalid NSObjectFileImage)\n");
+        return nullptr;
+    }
+
+    // if this is memory based image, write to temp file, then use file based loading
+    const BinaryImage* imageToLoad = nullptr;
+    if ( ofi->memSource != nullptr ) {
+        // make temp file with content of memory buffer
+        bool successfullyWritten = false;
+        ofi->path = ::tempnam(nullptr, "NSCreateObjectFileImageFromMemory-");
+        if ( ofi->path != nullptr ) {
+            int fd = ::open(ofi->path, O_WRONLY | O_CREAT | O_EXCL, 0644);
+            if ( fd != -1 ) {
+                ssize_t writtenSize = ::pwrite(fd, ofi->memSource, ofi->memLength, 0);
+                if ( writtenSize == ofi->memLength )
+                    successfullyWritten = true;
+                ::close(fd);
+            }
+        }
+        if ( !successfullyWritten ) {
+            if ( ofi->path != nullptr ) {
+                free((void*)ofi->path);
+                ofi->path = nullptr;
+            }
+            log_apis("NSLinkModule() => NULL (could not save memory image to temp file)\n");
+            return nullptr;
+        }
+    }
+    else {
+        // check if image is in a known ImageGroup, but not loaded. if so, load using existing closure info
+        log_apis("   NSLinkModule: checking for pre-built closure for path: %s\n", ofi->path);
+        imageToLoad = gAllImages.findImageInKnownGroups(ofi->path);
+        // TODO: check symlinks, realpath
+    }
+
+    // if no existing closure, RPC to closured to create one
+    if ( imageToLoad == nullptr ) {
+        const char* closuredErrorMessages[3];
+        int closuredErrorMessagesCount = 0;
+        if ( imageToLoad == nullptr ) {
+            imageToLoad = gAllImages.messageClosured(ofi->path, "NSLinkModule", closuredErrorMessages, closuredErrorMessagesCount);
+        }
+        for (int i=0; i < closuredErrorMessagesCount; ++i) {
+            log_apis("   NSLinkModule: failed: %s\n", closuredErrorMessages[i]);
+            free((void*)closuredErrorMessages[i]);
+        }
+    }
+
+    // use Image info to load and fixup image and all its dependents
+    if ( imageToLoad != nullptr ) {
+        Diagnostics diag;
+        ofi->loadAddress = loadImageAndDependents(diag, imageToLoad, true);
+        if ( diag.hasError() )
+            log_apis("   NSLinkModule: failed: %s\n", diag.errorMessage());
+   }
+
+    // if memory based load, delete temp file
+    if ( ofi->memSource != nullptr ) {
+        log_apis("   NSLinkModule: delete temp file: %s\n", ofi->path);
+        ::unlink(ofi->path);
+    }
+
+    log_apis("NSLinkModule() => %p\n", ofi->loadAddress);
+    return (NSModule)ofi->loadAddress;
+}
+
+// NSUnLinkModule unmaps the image, but does not release the NSObjectFileImage
+bool NSUnLinkModule(NSModule module, uint32_t options)
+{
+    log_apis("NSUnLinkModule(%p, 0x%08X)\n", module, options);
+
+    bool result = false;
+    const mach_header*  mh = (mach_header*)module;
+    launch_cache::Image image = gAllImages.findByLoadAddress(mh);
+    if ( image.valid() ) {
+        // removes image if reference count went to zero
+        gAllImages.decRefCount(mh);
+        result = true;
+    }
+
+    log_apis("NSUnLinkModule() => %d\n", result);
+
+    return result;
+}
+
+// NSDestroyObjectFileImage releases the NSObjectFileImage, but the mapped image may remain in use
+bool NSDestroyObjectFileImage(NSObjectFileImage ofi)
+{
+    log_apis("NSDestroyObjectFileImage(%p)\n", ofi);
+
+    // ofi is invalid if not in list
+    if ( !gAllImages.hasNSObjectFileImage(ofi) )
+        return false;
+
+    // keep copy of info
+    const void* memSource = ofi->memSource;
+    size_t      memLength = ofi->memLength;
+    const char* path      = ofi->path;
+
+    // remove from list
+    gAllImages.removeNSObjectFileImage(ofi);
+
+    // if object was created from a memory, release that memory
+    // NOTE: this is the way dyld has always done this. NSCreateObjectFileImageFromMemory() hands ownership of the memory to dyld
+    if ( memSource != nullptr ) {
+        // we don't know if memory came from malloc or vm_allocate, so ask malloc
+        if ( malloc_size(memSource) != 0 )
+            free((void*)(memSource));
+        else
+            vm_deallocate(mach_task_self(), (vm_address_t)memSource, memLength);
+    }
+    free((void*)path);
+
+    return true;
+}
+
+uint32_t NSSymbolDefinitionCountInObjectFileImage(NSObjectFileImage objectFileImage)
+{
+    halt("NSSymbolDefinitionCountInObjectFileImage() is obsolete");
+}
+
+const char* NSSymbolDefinitionNameInObjectFileImage(NSObjectFileImage objectFileImage, uint32_t ordinal)
+{
+    halt("NSSymbolDefinitionNameInObjectFileImage() is obsolete");
+}
+
+uint32_t NSSymbolReferenceCountInObjectFileImage(NSObjectFileImage objectFileImage)
+{
+    halt("NSSymbolReferenceCountInObjectFileImage() is obsolete");
+}
+
+const char* NSSymbolReferenceNameInObjectFileImage(NSObjectFileImage objectFileImage, uint32_t ordinal, bool *tentative_definition)
+{
+    halt("NSSymbolReferenceNameInObjectFileImage() is obsolete");
+}
+
+bool NSIsSymbolDefinedInObjectFileImage(NSObjectFileImage ofi, const char* symbolName)
+{
+    log_apis("NSIsSymbolDefinedInObjectFileImage(%p, %s)\n", ofi, symbolName);
+
+    // ofi is invalid if not in list
+    if ( !gAllImages.hasNSObjectFileImage(ofi) )
+        return false;
+
+    void* addr;
+    MachOParser parser(ofi->loadAddress);
+    return parser.hasExportedSymbol(symbolName, ^(uint32_t , const char*, void*, const mach_header**, void**) {
+        return false;
+    }, &addr);
+}
+
+void* NSGetSectionDataInObjectFileImage(NSObjectFileImage ofi, const char* segmentName, const char* sectionName, size_t* size)
+{
+    // ofi is invalid if not in list
+    if ( !gAllImages.hasNSObjectFileImage(ofi) )
+        return nullptr;
+
+    __block void* result = nullptr;
+    MachOParser parser(ofi->loadAddress);
+    parser.forEachSection(^(const char* aSegName, const char* aSectName, uint32_t flags, const void* content, size_t aSize, bool illegalSectionSize, bool& stop) {
+        if ( (strcmp(sectionName, aSectName) == 0) && (strcmp(segmentName, aSegName) == 0) ) {
+            result = (void*)content;
+            if ( size != nullptr )
+                *size = aSize;
+            stop = true;
+        }
+    });
+    return result;
+}
+
+const char* NSNameOfModule(NSModule m)
+{
+    log_apis("NSNameOfModule(%p)\n", m);
+
+    const mach_header* foundInLoadAddress;
+    launch_cache::Image image = gAllImages.findByOwnedAddress(m, &foundInLoadAddress);
+    if ( image.valid() ) {
+        return gAllImages.imagePath(image.binaryData());
+    }
+    return nullptr;
+}
+
+const char* NSLibraryNameForModule(NSModule m)
+{
+    log_apis("NSLibraryNameForModule(%p)\n", m);
+
+    const mach_header* foundInLoadAddress;
+    launch_cache::Image image = gAllImages.findByOwnedAddress(m, &foundInLoadAddress);
+    if ( image.valid() ) {
+        return gAllImages.imagePath(image.binaryData());
+    }
+    return nullptr;
+}
+
+
+static bool flatFindSymbol(const char* symbolName, void** symbolAddress, const mach_header** foundInImageAtLoadAddress)
+{
+    for (uint32_t index=0; index < gAllImages.count(); ++index) {
+        const mach_header* loadAddress;
+        launch_cache::Image image = gAllImages.findByLoadOrder(index, &loadAddress);
+        if ( image.valid() ) {
+            MachOParser parser(loadAddress);
+            if ( parser.hasExportedSymbol(symbolName, ^(uint32_t , const char* , void* , const mach_header** , void**) { return false; }, symbolAddress) ) {
+                *foundInImageAtLoadAddress = loadAddress;
+                return true;
+            }
+        }
+    }
+    return false;
+}
+
+bool NSIsSymbolNameDefined(const char* symbolName)
+{
+    log_apis("NSIsSymbolNameDefined(%s)\n", symbolName);
+
+    const mach_header* foundInImageAtLoadAddress;
+    void* address;
+    return flatFindSymbol(symbolName, &address, &foundInImageAtLoadAddress);
+}
+
+bool NSIsSymbolNameDefinedWithHint(const char* symbolName, const char* libraryNameHint)
+{
+    log_apis("NSIsSymbolNameDefinedWithHint(%s, %s)\n", symbolName, libraryNameHint);
+
+    const mach_header* foundInImageAtLoadAddress;
+    void* address;
+    return flatFindSymbol(symbolName, &address, &foundInImageAtLoadAddress);
+}
+
+bool NSIsSymbolNameDefinedInImage(const struct mach_header* mh, const char* symbolName)
+{
+    log_apis("NSIsSymbolNameDefinedInImage(%p, %s)\n", mh, symbolName);
+
+    MachOParser::DependentFinder reExportFollower = ^(uint32_t depIndex, const char* depLoadPath, void* extra, const mach_header** foundMH, void** foundExtra) {
+        *foundMH = gAllImages.alreadyLoaded(depLoadPath, false);
+        return (*foundMH != nullptr);
+    };
+
+    MachOParser parser(mh);
+    void* result;
+    return parser.hasExportedSymbol(symbolName, reExportFollower, &result);
+}
+
+NSSymbol NSLookupAndBindSymbol(const char* symbolName)
+{
+    log_apis("NSLookupAndBindSymbol(%s)\n", symbolName);
+
+    const mach_header* foundInImageAtLoadAddress;
+    void* symbolAddress;
+    if ( flatFindSymbol(symbolName, &symbolAddress, &foundInImageAtLoadAddress) ) {
+        return (NSSymbol)symbolAddress;
+    }
+    return nullptr;
+}
+
+NSSymbol NSLookupAndBindSymbolWithHint(const char* symbolName, const char* libraryNameHint)
+{
+    log_apis("NSLookupAndBindSymbolWithHint(%s, %s)\n", symbolName, libraryNameHint);
+
+    const mach_header* foundInImageAtLoadAddress;
+    void* symbolAddress;
+    if ( flatFindSymbol(symbolName, &symbolAddress, &foundInImageAtLoadAddress) ) {
+        return (NSSymbol)symbolAddress;
+    }
+    return nullptr;
+}
+
+NSSymbol NSLookupSymbolInModule(NSModule module, const char* symbolName)
+{
+    log_apis("NSLookupSymbolInModule(%p. %s)\n", module, symbolName);
+
+    MachOParser::DependentFinder reExportFollower = ^(uint32_t depIndex, const char* depLoadPath, void* extra, const mach_header** foundMH, void** foundExtra) {
+        *foundMH = gAllImages.alreadyLoaded(depLoadPath, false);
+        return (*foundMH != nullptr);
+    };
+
+    const mach_header* mh = (const mach_header*)module;
+    uint32_t loadIndex;
+    if ( gAllImages.findIndexForLoadAddress(mh, loadIndex) ) {
+        MachOParser parser(mh);
+        void* symAddress;
+        if ( parser.hasExportedSymbol(symbolName, reExportFollower, &symAddress) ) {
+            return (NSSymbol)symAddress;
+        }
+    }
+    return nullptr;
+}
+
+NSSymbol NSLookupSymbolInImage(const struct mach_header* mh, const char* symbolName, uint32_t options)
+{
+    log_apis("NSLookupSymbolInImage(%p, \"%s\", 0x%08X)\n", mh, symbolName, options);
+
+    MachOParser::DependentFinder reExportFollower = ^(uint32_t depIndex, const char* depLoadPath, void* extra, const mach_header** foundMH, void** foundExtra) {
+        *foundMH = gAllImages.alreadyLoaded(depLoadPath, false);
+        return (*foundMH != nullptr);
+    };
+
+    MachOParser parser(mh);
+    void* result;
+    if ( parser.hasExportedSymbol(symbolName, reExportFollower, &result) ) {
+        log_apis("   NSLookupSymbolInImage() => %p\n", result);
+        return (NSSymbol)result;
+    }
+
+    if ( options & NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR ) {
+        log_apis("   NSLookupSymbolInImage() => NULL\n");
+        return nullptr;
+    }
+    return nullptr;
+}
+
+const char* NSNameOfSymbol(NSSymbol symbol)
+{
+    halt("NSNameOfSymbol() is obsolete");
+}
+
+void* NSAddressOfSymbol(NSSymbol symbol)
+{
+    log_apis("NSAddressOfSymbol(%p)\n", symbol);
+
+    // in dyld 1.0, NSSymbol was a pointer to the nlist entry in the symbol table
+    return (void*)symbol;
+}
+
+NSModule NSModuleForSymbol(NSSymbol symbol)
+{
+    log_apis("NSModuleForSymbol(%p)\n", symbol);
+
+    const mach_header* foundInLoadAddress;
+    launch_cache::Image image = gAllImages.findByOwnedAddress(symbol, &foundInLoadAddress);
+    if ( image.valid() ) {
+        return (NSModule)foundInLoadAddress;
+    }
+    return nullptr;
+}
+
+void NSLinkEditError(NSLinkEditErrors *c, int *errorNumber, const char** fileName, const char** errorString)
+{
+    log_apis("NSLinkEditError(%p, %p, %p, %p)\n", c, errorNumber, fileName, errorString);
+    *c = NSLinkEditOtherError;
+    *errorNumber = 0;
+    *fileName = NULL;
+    *errorString = NULL;
+}
+
+bool NSAddLibrary(const char* pathName)
+{
+    log_apis("NSAddLibrary(%s)\n", pathName);
+
+    return ( dlopen(pathName, 0) != nullptr);
+}
+
+bool NSAddLibraryWithSearching(const char* pathName)
+{
+    log_apis("NSAddLibraryWithSearching(%s)\n", pathName);
+
+    return ( dlopen(pathName, 0) != nullptr);
+}
+
+const mach_header* NSAddImage(const char* imageName, uint32_t options)
+{
+    log_apis("NSAddImage(\"%s\", 0x%08X)\n", imageName, options);
+
+    // Note: this is a quick and dirty implementation that just uses dlopen() and ignores some option flags
+    uint32_t dloptions = 0;
+    if ( (options & NSADDIMAGE_OPTION_RETURN_ONLY_IF_LOADED) != 0 )
+        dloptions |= RTLD_NOLOAD;
+
+    void* h = dlopen(imageName, dloptions);
+    if ( h != nullptr ) {
+        const mach_header* mh;
+        bool dontContinue;
+        parseDlHandle(h, &mh, &dontContinue);
+        return mh;
+    }
+
+    if ( (options & (NSADDIMAGE_OPTION_RETURN_ON_ERROR|NSADDIMAGE_OPTION_RETURN_ONLY_IF_LOADED)) == 0 ) {
+        halt("NSAddImage() image not found");
+    }
+    return nullptr;
+}
+
+void NSInstallLinkEditErrorHandlers(const NSLinkEditErrorHandlers *handlers)
+{
+    halt("NSInstallLinkEditErrorHandlers() is obsolete");
+}
+
+bool _dyld_present(void)
+{
+    log_apis("_dyld_present()\n");
+
+    return true;
+}
+
+bool _dyld_launched_prebound(void)  
+{
+    halt("_dyld_launched_prebound() is obsolete");
+}
+
+bool _dyld_all_twolevel_modules_prebound(void)
+{
+    halt("_dyld_all_twolevel_modules_prebound() is obsolete");
+}
+
+bool _dyld_bind_fully_image_containing_address(const void* address)
+{
+    log_apis("_dyld_bind_fully_image_containing_address(%p)\n", address);
+
+    // in dyld3, everything is always fully bound
+    return true;
+}
+
+bool _dyld_image_containing_address(const void* address)
+{
+    log_apis("_dyld_image_containing_address(%p)\n", address);
+
+    return (dyld_image_header_containing_address(address) != nullptr);
+}
+
+void _dyld_lookup_and_bind(const char* symbolName, void **address, NSModule* module)
+{
+    log_apis("_dyld_lookup_and_bind(%s, %p, %p)\n", symbolName, address, module);
+
+    const mach_header* foundInImageAtLoadAddress;
+    if ( flatFindSymbol(symbolName, address, &foundInImageAtLoadAddress) ) {
+        *module = (NSModule)foundInImageAtLoadAddress;
+        return;
+    }
+
+    *address = 0;
+    *module = 0;
+}
+
+void _dyld_lookup_and_bind_with_hint(const char* symbolName, const char* libraryNameHint, void** address, NSModule* module)
+{
+    log_apis("_dyld_lookup_and_bind_with_hint(%s, %s, %p, %p)\n", symbolName, libraryNameHint, address, module);
+
+    const mach_header* foundInImageAtLoadAddress;
+    if ( flatFindSymbol(symbolName, address, &foundInImageAtLoadAddress) ) {
+        *module = (NSModule)foundInImageAtLoadAddress;
+        return;
+    }
+
+    *address = 0;
+    *module = 0;
+}
+
+
+void _dyld_lookup_and_bind_fully(const char* symbolName, void** address, NSModule* module)
+{
+    log_apis("_dyld_lookup_and_bind_fully(%s, %p, %p)\n", symbolName, address, module);
+
+    const mach_header* foundInImageAtLoadAddress;
+    if ( flatFindSymbol(symbolName, address, &foundInImageAtLoadAddress) ) {
+        *module = (NSModule)foundInImageAtLoadAddress;
+        return;
+    }
+
+    *address = 0;
+    *module = 0;
+}
+
+const struct mach_header* _dyld_get_image_header_containing_address(const void* address)
+{
+    log_apis("_dyld_get_image_header_containing_address(%p)\n", address);
+
+    return dyld_image_header_containing_address(address);
+}
+
+#endif
+
+
+} // namespace dyld3
+
diff --git a/dyld3/AllImages.cpp b/dyld3/AllImages.cpp
new file mode 100644 (file)
index 0000000..6d64e36
--- /dev/null
@@ -0,0 +1,1562 @@
+/*
+ * Copyright (c) 2017 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 <stdint.h>
+#include <sys/stat.h>
+#include <sys/sysctl.h>
+#include <mach/mach_time.h> // mach_absolute_time()
+#include <pthread/pthread.h>
+#include <libkern/OSAtomic.h>
+
+#include <vector>
+#include <algorithm>
+
+#include "AllImages.h"
+#include "MachOParser.h"
+#include "libdyldEntryVector.h"
+#include "Logging.h"
+#include "Loading.h"
+#include "Tracing.h"
+#include "LaunchCache.h"
+#include "DyldSharedCache.h"
+#include "PathOverrides.h"
+#include "DyldCacheParser.h"
+
+extern const char** appleParams;
+
+// should be a header for these
+struct __cxa_range_t {
+    const void* addr;
+    size_t      length;
+};
+extern "C" void __cxa_finalize_ranges(const __cxa_range_t ranges[], unsigned int count);
+
+VIS_HIDDEN bool gUseDyld3 = false;
+
+
+namespace dyld3 {
+
+class VIS_HIDDEN LoadedImage {
+public:
+    enum class State { uninited=3, beingInited=2, inited=0 };
+    typedef launch_cache::binary_format::Image      BinaryImage;
+
+                        LoadedImage(const mach_header* mh, const BinaryImage* bi);
+    bool                operator==(const LoadedImage& rhs) const;
+    void                init(const mach_header* mh, const BinaryImage* bi);
+    const mach_header*  loadedAddress() const   { return (mach_header*)((uintptr_t)_loadAddress & ~0x7ULL); }
+    State               state() const           { return (State)((uintptr_t)_loadAddress & 0x3ULL); }
+    const BinaryImage*  image() const           { return _image; }
+    bool                neverUnload() const     { return ((uintptr_t)_loadAddress & 0x4ULL); }
+    void                setState(State s)       { _loadAddress = (mach_header*)((((uintptr_t)_loadAddress) & ~0x3ULL) | (uintptr_t)s); }
+    void                setNeverUnload()        { _loadAddress = (mach_header*)(((uintptr_t)_loadAddress) | 0x4ULL); }
+
+private:
+    const mach_header*  _loadAddress; // low bits: bit2=neverUnload, bit1/bit0 contain State
+    const BinaryImage*  _image;
+};
+
+
+bool LoadedImage::operator==(const LoadedImage& rhs) const
+{
+    return (_image == rhs._image) && (loadedAddress() == rhs.loadedAddress());
+}
+
+
+
+struct VIS_HIDDEN DlopenCount {
+    bool                operator==(const DlopenCount& rhs) const;
+    const mach_header*  loadAddress;
+    uintptr_t           refCount;
+};
+
+bool DlopenCount::operator==(const DlopenCount& rhs) const
+{
+    return (loadAddress == rhs.loadAddress) && (refCount == rhs.refCount);
+}
+
+LoadedImage::LoadedImage(const mach_header* mh, const BinaryImage* bi)
+    : _loadAddress(mh), _image(bi)
+{
+    assert(loadedAddress() == mh);
+    setState(State::uninited);
+}
+
+void LoadedImage::init(const mach_header* mh, const BinaryImage* bi)
+{
+    _loadAddress = mh;
+    _image = bi;
+    assert(loadedAddress() == mh);
+    setState(State::uninited);
+}
+
+// forward reference
+template <typename T, int C> class ReaderWriterChunkedVector;
+
+template <typename T, int C>
+class VIS_HIDDEN ChunkedVector {
+public:
+    static ChunkedVector<T,C>*  make(uint32_t count);
+
+    void                        forEach(uint32_t& startIndex, bool& outerStop, void (^callback)(uint32_t index, const T& value, bool& stop)) const;
+    void                        forEach(uint32_t& startIndex, bool& outerStop, void (^callback)(uint32_t index, T& value, bool& stop));
+    T*                          add(const T& value);
+    T*                          add(uint32_t count, const T values[]);
+    void                        remove(uint32_t index);
+    uint32_t                    count() const { return _inUseCount; }
+    uint32_t                    freeCount() const { return _allocCount - _inUseCount; }
+private:
+    T&                          element(uint32_t index) { return ((T*)_elements)[index]; }
+    const T&                    element(uint32_t index) const { return ((T*)_elements)[index]; }
+
+    friend class ReaderWriterChunkedVector<T,C>;
+
+    ChunkedVector<T,C>*     _next           = nullptr;
+    uint32_t                _allocCount     = C;
+    uint32_t                _inUseCount     = 0;
+    uint8_t                 _elements[C*sizeof(T)] = { 0 };
+};
+
+template <typename T, int C>
+class VIS_HIDDEN ReaderWriterChunkedVector {
+public:
+    T*                  add(uint32_t count, const T values[]);
+    T*                  add(const T& value) { return add(1, &value); }
+    T*                  addNoLock(uint32_t count, const T values[]);
+    T*                  addNoLock(const T& value) { return addNoLock(1, &value); }
+    void                remove(const T& value);
+    uint32_t            count() const;
+    void                forEachWithReadLock(void (^callback)(uint32_t index, const T& value, bool& stop)) const;
+    void                forEachWithWriteLock(void (^callback)(uint32_t index, T& value, bool& stop));
+    void                forEachNoLock(void (^callback)(uint32_t index, const T& value, bool& stop)) const;
+    T&                  operator[](size_t index);
+    uint32_t            countNoLock() const;
+
+    void                withReadLock(void (^withLock)()) const;
+    void                withWriteLock(void (^withLock)()) const;
+    void                acquireWriteLock();
+    void                releaseWriteLock();
+    void                dump(void (^callback)(const T& value)) const;
+
+private:
+    mutable pthread_rwlock_t    _lock           = PTHREAD_RWLOCK_INITIALIZER;
+    ChunkedVector<T,C>          _firstChunk;
+};
+
+
+typedef void (*NotifyFunc)(const mach_header* mh, intptr_t slide);
+
+static  ReaderWriterChunkedVector<NotifyFunc, 4>                                sLoadNotifiers;
+static  ReaderWriterChunkedVector<NotifyFunc, 4>                                sUnloadNotifiers;
+static  ReaderWriterChunkedVector<LoadedImage, 4>                               sLoadedImages;
+static  ReaderWriterChunkedVector<DlopenCount, 4>                               sDlopenRefCounts;
+static  ReaderWriterChunkedVector<const launch_cache::BinaryImageGroupData*, 4> sKnownGroups;
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
+static     ReaderWriterChunkedVector<__NSObjectFileImage, 2>  sNSObjectFileImages;
+#endif
+
+
+/////////////////////  ChunkedVector ////////////////////////////
+
+template <typename T, int C>
+ChunkedVector<T,C>* ChunkedVector<T,C>::make(uint32_t count)
+{
+    size_t size = sizeof(ChunkedVector) + sizeof(T) * (count-C);
+    ChunkedVector<T,C>* result = (ChunkedVector<T,C>*)malloc(size);
+    result->_next       = nullptr;
+    result->_allocCount = count;
+    result->_inUseCount = 0;
+    return result;
+}
+
+template <typename T, int C>
+void ChunkedVector<T,C>::forEach(uint32_t& outerIndex, bool& outerStop, void (^callback)(uint32_t index, const T& value, bool& stop)) const
+{
+    for (uint32_t i=0; i < _inUseCount; ++i) {
+        callback(outerIndex, element(i), outerStop);
+        ++outerIndex;
+        if ( outerStop )
+            break;
+    }
+}
+
+template <typename T, int C>
+void ChunkedVector<T,C>::forEach(uint32_t& outerIndex, bool& outerStop, void (^callback)(uint32_t index, T& value, bool& stop))
+{
+    for (uint32_t i=0; i < _inUseCount; ++i) {
+        callback(outerIndex, element(i), outerStop);
+        ++outerIndex;
+        if ( outerStop )
+            break;
+    }
+}
+
+template <typename T, int C>
+T* ChunkedVector<T,C>::add(const T& value)
+{
+    return add(1, &value);
+}
+
+template <typename T, int C>
+T* ChunkedVector<T,C>::add(uint32_t count, const T values[])
+{
+    assert(count <= (_allocCount - _inUseCount));
+    T* result = &element(_inUseCount);
+    memmove(result, values, sizeof(T)*count);
+    _inUseCount += count;
+    return result;
+}
+
+template <typename T, int C>
+void ChunkedVector<T,C>::remove(uint32_t index)
+{
+    assert(index < _inUseCount);
+    int moveCount = _inUseCount - index - 1;
+    if ( moveCount >= 1 ) {
+        memmove(&element(index), &element(index+1), sizeof(T)*moveCount);
+    }
+    _inUseCount--;
+}
+
+
+/////////////////////  ReaderWriterChunkedVector ////////////////////////////
+
+
+
+template <typename T, int C>
+void ReaderWriterChunkedVector<T,C>::withReadLock(void (^work)()) const
+{
+    assert(pthread_rwlock_rdlock(&_lock) == 0);
+    work();
+    assert(pthread_rwlock_unlock(&_lock) == 0);
+}
+
+template <typename T, int C>
+void ReaderWriterChunkedVector<T,C>::withWriteLock(void (^work)()) const
+{
+    assert(pthread_rwlock_wrlock(&_lock) == 0);
+    work();
+    assert(pthread_rwlock_unlock(&_lock) == 0);
+}
+
+template <typename T, int C>
+void ReaderWriterChunkedVector<T,C>::acquireWriteLock()
+{
+    assert(pthread_rwlock_wrlock(&_lock) == 0);
+}
+
+template <typename T, int C>
+void ReaderWriterChunkedVector<T,C>::releaseWriteLock()
+{
+    assert(pthread_rwlock_unlock(&_lock) == 0);
+}
+
+template <typename T, int C>
+uint32_t ReaderWriterChunkedVector<T,C>::count() const
+{
+    __block uint32_t result = 0;
+    withReadLock(^() {
+        for (const ChunkedVector<T,C>* chunk = &_firstChunk; chunk != nullptr; chunk = chunk->_next) {
+            result += chunk->count();
+        }
+    });
+    return result;
+}
+
+template <typename T, int C>
+uint32_t ReaderWriterChunkedVector<T,C>::countNoLock() const
+{
+    uint32_t result = 0;
+    for (const ChunkedVector<T,C>* chunk = &_firstChunk; chunk != nullptr; chunk = chunk->_next) {
+        result += chunk->count();
+    }
+    return result;
+}
+
+template <typename T, int C>
+T* ReaderWriterChunkedVector<T,C>::addNoLock(uint32_t count, const T values[])
+{
+    T* result = nullptr;
+    ChunkedVector<T,C>* lastChunk = &_firstChunk;
+    while ( lastChunk->_next != nullptr )
+        lastChunk = lastChunk->_next;
+
+    if ( lastChunk->freeCount() >= count ) {
+        // append to last chunk
+        result = lastChunk->add(count, values);
+    }
+    else {
+        // append new chunk
+        uint32_t allocCount = count;
+        uint32_t remainder = count % C;
+        if ( remainder != 0 )
+            allocCount = count + C - remainder;
+        ChunkedVector<T,C>* newChunk = ChunkedVector<T,C>::make(allocCount);
+        result = newChunk->add(count, values);
+        lastChunk->_next = newChunk;
+    }
+
+    return result;
+}
+
+template <typename T, int C>
+T* ReaderWriterChunkedVector<T,C>::add(uint32_t count, const T values[])
+{
+    __block T* result = nullptr;
+    withWriteLock(^() {
+        result = addNoLock(count, values);
+    });
+    return result;
+}
+
+template <typename T, int C>
+void ReaderWriterChunkedVector<T,C>::remove(const T& valueToRemove)
+{
+    __block bool stopStorage = false;
+    withWriteLock(^() {
+        ChunkedVector<T,C>* chunkNowEmpty = nullptr;
+        __block uint32_t indexStorage = 0;
+        __block bool found = false;
+        for (ChunkedVector<T,C>* chunk = &_firstChunk; chunk != nullptr; chunk = chunk->_next) {
+            uint32_t chunkStartIndex = indexStorage;
+            __block uint32_t foundIndex = 0;
+            chunk->forEach(indexStorage, stopStorage, ^(uint32_t index, const T& value, bool& stop) {
+                if ( value == valueToRemove ) {
+                    foundIndex = index - chunkStartIndex;
+                    found = true;
+                    stop = true;
+                }
+            });
+            if ( found ) {
+                chunk->remove(foundIndex);
+                found = false;
+                if ( chunk->count() == 0 )
+                    chunkNowEmpty = chunk;
+            }
+        }
+        // if chunk is now empty, remove from linked list and free
+        if ( chunkNowEmpty ) {
+            for (ChunkedVector<T,C>* chunk = &_firstChunk; chunk != nullptr; chunk = chunk->_next) {
+                if ( chunk->_next == chunkNowEmpty ) {
+                    chunk->_next = chunkNowEmpty->_next;
+                    if ( chunkNowEmpty != &_firstChunk )
+                        free(chunkNowEmpty);
+                    break;
+                }
+            }
+        }
+    });
+}
+
+template <typename T, int C>
+void ReaderWriterChunkedVector<T,C>::forEachWithReadLock(void (^callback)(uint32_t index, const T& value, bool& stop)) const
+{
+    __block uint32_t index = 0;
+    __block bool stop = false;
+    withReadLock(^() {
+        for (const ChunkedVector<T,C>* chunk = &_firstChunk; chunk != nullptr; chunk = chunk->_next) {
+            chunk->forEach(index, stop, callback);
+            if ( stop )
+                break;
+        }
+    });
+}
+
+template <typename T, int C>
+void ReaderWriterChunkedVector<T,C>::forEachWithWriteLock(void (^callback)(uint32_t index, T& value, bool& stop))
+{
+    __block uint32_t index = 0;
+    __block bool stop = false;
+    withReadLock(^() {
+        for (ChunkedVector<T,C>* chunk = &_firstChunk; chunk != nullptr; chunk = chunk->_next) {
+            chunk->forEach(index, stop, callback);
+            if ( stop )
+                break;
+        }
+    });
+}
+
+template <typename T, int C>
+void ReaderWriterChunkedVector<T,C>::forEachNoLock(void (^callback)(uint32_t index, const T& value, bool& stop)) const
+{
+    uint32_t index = 0;
+    bool stop = false;
+    for (const ChunkedVector<T,C>* chunk = &_firstChunk; chunk != nullptr; chunk = chunk->_next) {
+        chunk->forEach(index, stop, callback);
+        if ( stop )
+            break;
+    }
+}
+
+template <typename T, int C>
+T& ReaderWriterChunkedVector<T,C>::operator[](size_t targetIndex)
+{
+    __block T* result = nullptr;
+    forEachNoLock(^(uint32_t index, T const& value, bool& stop) {
+        if ( index == targetIndex ) {
+            result = (T*)&value;
+            stop = true;
+        }
+    });
+    return *result;
+}
+
+template <typename T, int C>
+void ReaderWriterChunkedVector<T,C>::dump(void (^callback)(const T& value)) const
+{
+    log("dump ReaderWriterChunkedVector at %p\n", this);
+    __block uint32_t index = 0;
+    __block bool stop = false;
+    withReadLock(^() {
+        for (const ChunkedVector<T,C>* chunk = &_firstChunk; chunk != nullptr; chunk = chunk->_next) {
+            log(" chunk at %p\n", chunk);
+            chunk->forEach(index, stop, ^(uint32_t i, const T& value, bool& s) {
+                callback(value);
+            });
+        }
+    });
+}
+
+
+
+/////////////////////  AllImages ////////////////////////////
+
+
+AllImages gAllImages;
+
+
+
+void AllImages::init(const BinaryClosure* closure, const void* dyldCacheLoadAddress, const char* dyldCachePath,
+                     const dyld3::launch_cache::DynArray<loader::ImageInfo>& initialImages)
+{
+    _mainClosure        = closure;
+    _initialImages      = &initialImages;
+    _dyldCacheAddress   = dyldCacheLoadAddress;
+    _dyldCachePath      = dyldCachePath;
+
+    // Make temporary old image array, so libSystem initializers can be debugged
+    uint32_t count = (uint32_t)initialImages.count();
+    dyld_image_info oldDyldInfo[count];
+    for (int i=0; i < count; ++i) {
+        launch_cache::Image img(initialImages[i].imageData);
+        oldDyldInfo[i].imageLoadAddress = initialImages[i].loadAddress;
+        oldDyldInfo[i].imageFilePath    = img.path();
+        oldDyldInfo[i].imageFileModDate = 0;
+    }
+    _oldAllImageInfos->infoArray        = oldDyldInfo;
+    _oldAllImageInfos->infoArrayCount   = count;
+    _oldAllImageInfos->notification(dyld_image_adding, count, oldDyldInfo);
+    _oldAllImageInfos->infoArray        = nullptr;
+    _oldAllImageInfos->infoArrayCount   = 0;
+}
+
+void AllImages::setProgramVars(ProgramVars* vars)
+{
+    _programVars = vars;
+}
+
+void AllImages::applyInitialImages()
+{
+    addImages(*_initialImages);
+    _initialImages = nullptr;  // this was stack allocated
+}
+
+void AllImages::mirrorToOldAllImageInfos()
+{
+   // set infoArray to NULL to denote it is in-use
+    _oldAllImageInfos->infoArray = nullptr;
+
+    // if array not large enough, re-alloc it
+    uint32_t imageCount = sLoadedImages.countNoLock();
+    if ( _oldArrayAllocCount < imageCount ) {
+        uint32_t newAllocCount    = imageCount + 16;
+        dyld_image_info* newArray = (dyld_image_info*)malloc(sizeof(dyld_image_info)*newAllocCount);
+        if ( _oldAllImageArray != nullptr ) {
+            memcpy(newArray, _oldAllImageArray, sizeof(dyld_image_info)*_oldAllImageInfos->infoArrayCount);
+            free(_oldAllImageArray);
+        }
+        _oldAllImageArray   = newArray;
+        _oldArrayAllocCount = newAllocCount;
+    }
+
+    // fill out array to mirror current image list
+    sLoadedImages.forEachNoLock(^(uint32_t index, const LoadedImage& loadedImage, bool& stop) {
+        launch_cache::Image img(loadedImage.image());
+        _oldAllImageArray[index].imageLoadAddress = loadedImage.loadedAddress();
+        _oldAllImageArray[index].imageFilePath    = imagePath(loadedImage.image());
+        _oldAllImageArray[index].imageFileModDate = 0;
+    });
+
+    // set infoArray back to base address of array (so other process can now read)
+    _oldAllImageInfos->infoArrayCount           = imageCount;
+    _oldAllImageInfos->infoArrayChangeTimestamp = mach_absolute_time();
+    _oldAllImageInfos->infoArray                = _oldAllImageArray;
+}
+
+void AllImages::addImages(const launch_cache::DynArray<loader::ImageInfo>& newImages)
+{
+    uint32_t count = (uint32_t)newImages.count();
+    assert(count != 0);
+
+    // build stack array of LoadedImage to copy into sLoadedImages
+    STACK_ALLOC_DYNARRAY(LoadedImage, count, loadedImagesArray);
+    for (uint32_t i=0; i < count; ++i) {
+        loadedImagesArray[i].init(newImages[i].loadAddress, newImages[i].imageData);
+        if (newImages[i].neverUnload)
+            loadedImagesArray[i].setNeverUnload();
+    }
+    sLoadedImages.add(count, &loadedImagesArray[0]);
+
+    if ( _oldAllImageInfos != nullptr ) {
+        // sync to old all image infos struct
+        if ( _initialImages != nullptr ) {
+            // libSystem not initialized yet, don't use locks
+            mirrorToOldAllImageInfos();
+        }
+        else {
+            sLoadedImages.withReadLock(^{
+                mirrorToOldAllImageInfos();
+            });
+        }
+
+        // tell debugger about new images
+        dyld_image_info oldDyldInfo[count];
+        for (int i=0; i < count; ++i) {
+            launch_cache::Image img(newImages[i].imageData);
+            oldDyldInfo[i].imageLoadAddress = newImages[i].loadAddress;
+            oldDyldInfo[i].imageFilePath    = imagePath(newImages[i].imageData);
+            oldDyldInfo[i].imageFileModDate = 0;
+        }
+        _oldAllImageInfos->notification(dyld_image_adding, count, oldDyldInfo);
+    }
+
+    // log loads
+    for (int i=0; i < count; ++i) {
+        launch_cache::Image img(newImages[i].imageData);
+        log_loads("dyld: %s\n", imagePath(newImages[i].imageData));
+    }
+
+#if !TARGET_IPHONE_SIMULATOR
+    // call kdebug trace for each image
+    if (kdebug_is_enabled(KDBG_CODE(DBG_DYLD, DBG_DYLD_UUID, DBG_DYLD_UUID_MAP_A))) {
+        for (uint32_t i=0; i < count; ++i) {
+            launch_cache::Image img(newImages[i].imageData);
+            struct stat stat_buf;
+            fsid_t fsid = {{ 0, 0 }};
+            fsobj_id_t fsobjid = { 0, 0 };
+            if (img.isDiskImage() && stat(imagePath(newImages[i].imageData), &stat_buf) == 0 ) {
+                fsobjid = *(fsobj_id_t*)&stat_buf.st_ino;
+                fsid = {{ stat_buf.st_dev, 0 }};
+            }
+            kdebug_trace_dyld_image(DBG_DYLD_UUID_MAP_A, img.uuid(), fsobjid, fsid, newImages[i].loadAddress);
+        }
+    }
+#endif
+    // call each _dyld_register_func_for_add_image function with each image
+    const uint32_t  existingNotifierCount = sLoadNotifiers.count();
+    NotifyFunc      existingNotifiers[existingNotifierCount];
+    NotifyFunc*     existingNotifierArray = existingNotifiers;
+    sLoadNotifiers.forEachWithReadLock(^(uint32_t index, const NotifyFunc& func, bool& stop) {
+        if ( index < existingNotifierCount )
+            existingNotifierArray[index] = func;
+    });
+    // we don't want to hold lock while calling out, so prebuild array (with lock) then do calls on that array (without lock)
+    for (uint32_t j=0; j < existingNotifierCount; ++j) {
+        NotifyFunc func = existingNotifierArray[j];
+        for (uint32_t i=0; i < count; ++i) {
+            MachOParser parser(newImages[i].loadAddress);
+            log_notifications("dyld: add notifier %p called with mh=%p\n", func, newImages[i].loadAddress);
+            func(newImages[i].loadAddress, parser.getSlide());
+        }
+    }
+
+    // call objc about images that use objc
+    if ( _objcNotifyMapped != nullptr ) {
+        const char*         pathsBuffer[count];
+        const mach_header*  mhBuffer[count];
+        uint32_t            imagesWithObjC = 0;
+        for (uint32_t i=0; i < count; ++i) {
+            launch_cache::Image img(newImages[i].imageData);
+            if ( img.hasObjC() ) {
+                pathsBuffer[imagesWithObjC] = imagePath(newImages[i].imageData);
+                mhBuffer[imagesWithObjC]    = newImages[i].loadAddress;
+               ++imagesWithObjC;
+            }
+        }
+        if ( imagesWithObjC != 0 ) {
+            (*_objcNotifyMapped)(imagesWithObjC, pathsBuffer, mhBuffer);
+            if ( log_notifications("dyld: objc-mapped-notifier called with %d images:\n", imagesWithObjC) ) {
+                for (uint32_t i=0; i < imagesWithObjC; ++i) {
+                    log_notifications("dyld:  objc-mapped: %p %s\n",  mhBuffer[i], pathsBuffer[i]);
+                }
+            }
+        }
+    }
+
+    // notify any processes tracking loads in this process
+    notifyMonitorLoads(newImages);
+}
+
+void AllImages::removeImages(const launch_cache::DynArray<loader::ImageInfo>& unloadImages)
+{
+    uint32_t count = (uint32_t)unloadImages.count();
+    assert(count != 0);
+
+    // call each _dyld_register_func_for_remove_image function with each image
+    // do this before removing image from internal data structures so that the callback can query dyld about the image
+    const uint32_t  existingNotifierCount = sUnloadNotifiers.count();
+    NotifyFunc      existingNotifiers[existingNotifierCount];
+    NotifyFunc*     existingNotifierArray = existingNotifiers;
+    sUnloadNotifiers.forEachWithReadLock(^(uint32_t index, const NotifyFunc& func, bool& stop) {
+        if ( index < existingNotifierCount )
+            existingNotifierArray[index] = func;
+    });
+    // we don't want to hold lock while calling out, so prebuild array (with lock) then do calls on that array (without lock)
+    for (uint32_t j=0; j < existingNotifierCount; ++j) {
+        NotifyFunc func = existingNotifierArray[j];
+        for (uint32_t i=0; i < count; ++i) {
+            MachOParser parser(unloadImages[i].loadAddress);
+            log_notifications("dyld: remove notifier %p called with mh=%p\n", func, unloadImages[i].loadAddress);
+            func(unloadImages[i].loadAddress, parser.getSlide());
+        }
+    }
+
+    // call objc about images going away
+    if ( _objcNotifyUnmapped != nullptr ) {
+        for (uint32_t i=0; i < count; ++i) {
+            launch_cache::Image img(unloadImages[i].imageData);
+            if ( img.hasObjC() ) {
+                (*_objcNotifyUnmapped)(imagePath(unloadImages[i].imageData), unloadImages[i].loadAddress);
+                log_notifications("dyld: objc-unmapped-notifier called with image %p %s\n", unloadImages[i].loadAddress, imagePath(unloadImages[i].imageData));
+            }
+        }
+    }
+
+#if !TARGET_IPHONE_SIMULATOR
+    // call kdebug trace for each image
+    if (kdebug_is_enabled(KDBG_CODE(DBG_DYLD, DBG_DYLD_UUID, DBG_DYLD_UUID_MAP_A))) {
+        for (uint32_t i=0; i < count; ++i) {
+            launch_cache::Image img(unloadImages[i].imageData);
+            struct stat stat_buf;
+            fsid_t fsid = {{ 0, 0 }};
+            fsobj_id_t fsobjid = { 0, 0 };
+            if (stat(imagePath(unloadImages[i].imageData), &stat_buf) == 0 ) {
+                fsobjid = *(fsobj_id_t*)&stat_buf.st_ino;
+                fsid = {{ stat_buf.st_dev, 0 }};
+            }
+            kdebug_trace_dyld_image(DBG_DYLD_UUID_UNMAP_A, img.uuid(), fsobjid, fsid, unloadImages[i].loadAddress);
+        }
+    }
+#endif
+
+    // remove each from sLoadedImages
+    for (uint32_t i=0; i < count; ++i) {
+        LoadedImage info(unloadImages[i].loadAddress, unloadImages[i].imageData);
+        sLoadedImages.remove(info);
+    }
+
+    // sync to old all image infos struct
+    sLoadedImages.withReadLock(^{
+        mirrorToOldAllImageInfos();
+    });
+
+    // tell debugger about removed images
+    dyld_image_info oldDyldInfo[count];
+    for (int i=0; i < count; ++i) {
+        launch_cache::Image img(unloadImages[i].imageData);
+        oldDyldInfo[i].imageLoadAddress = unloadImages[i].loadAddress;
+        oldDyldInfo[i].imageFilePath    = imagePath(unloadImages[i].imageData);
+        oldDyldInfo[i].imageFileModDate = 0;
+    }
+    _oldAllImageInfos->notification(dyld_image_removing, count, oldDyldInfo);
+
+    // unmap images
+    for (int i=0; i < count; ++i) {
+        launch_cache::Image img(unloadImages[i].imageData);
+        loader::unmapImage(unloadImages[i].imageData, unloadImages[i].loadAddress);
+        log_loads("dyld: unloaded %s\n", imagePath(unloadImages[i].imageData));
+    }
+
+    // notify any processes tracking loads in this process
+    notifyMonitorUnloads(unloadImages);
+}
+
+void AllImages::setNeverUnload(const loader::ImageInfo& existingImage)
+{
+    sLoadedImages.forEachWithWriteLock(^(uint32_t index, dyld3::LoadedImage &value, bool &stop) {
+        if (value.image() == existingImage.imageData) {
+            value.setNeverUnload();
+            stop = true;
+        }
+    });
+}
+
+uint32_t AllImages::count() const
+{
+    return sLoadedImages.count();
+}
+
+
+launch_cache::Image AllImages::findByLoadOrder(uint32_t index, const mach_header** loadAddress) const
+{
+    __block const BinaryImage* foundImage = nullptr;
+    sLoadedImages.forEachWithReadLock(^(uint32_t anIndex, const LoadedImage& loadedImage, bool& stop) {
+        if ( anIndex == index ) {
+            foundImage   = loadedImage.image();
+            *loadAddress = loadedImage.loadedAddress();
+            stop = true;
+        }
+    });
+    return launch_cache::Image(foundImage);
+}
+
+launch_cache::Image AllImages::findByLoadAddress(const mach_header* loadAddress) const
+{
+    __block const BinaryImage* foundImage = nullptr;
+    sLoadedImages.forEachWithReadLock(^(uint32_t anIndex, const LoadedImage& loadedImage, bool& stop) {
+        if ( loadedImage.loadedAddress() == loadAddress ) {
+            foundImage = loadedImage.image();
+            stop = true;
+        }
+    });
+    return launch_cache::Image(foundImage);
+}
+
+bool AllImages::findIndexForLoadAddress(const mach_header* loadAddress, uint32_t& index)
+{
+    __block bool result = false;
+    sLoadedImages.forEachWithReadLock(^(uint32_t anIndex, const LoadedImage& loadedImage, bool& stop) {
+        if ( loadedImage.loadedAddress() == loadAddress ) {
+            index = anIndex;
+            result = true;
+            stop = true;
+        }
+    });
+    return result;
+}
+
+void AllImages::forEachImage(void (^handler)(uint32_t imageIndex, const mach_header* loadAddress, const launch_cache::Image image, bool& stop)) const
+{
+       sLoadedImages.forEachWithReadLock(^(uint32_t imageIndex, const LoadedImage& loadedImage, bool& stop) {
+       handler(imageIndex, loadedImage.loadedAddress(), launch_cache::Image(loadedImage.image()), stop);
+    });
+}
+
+launch_cache::Image AllImages::findByOwnedAddress(const void* addr, const mach_header** loadAddress, uint8_t* permissions) const
+{
+    if ( _initialImages != nullptr ) {
+        // being called during libSystem initialization, so sLoadedImages not allocated yet
+        for (int i=0; i < _initialImages->count(); ++i) {
+            const loader::ImageInfo& entry = (*_initialImages)[i];
+            launch_cache::Image anImage(entry.imageData);
+            if ( anImage.containsAddress(addr, entry.loadAddress, permissions) ) {
+                *loadAddress = entry.loadAddress;
+                return entry.imageData;
+            }
+        }
+        return launch_cache::Image(nullptr);
+    }
+
+    // if address is in cache, do fast search of cache
+    if ( (_dyldCacheAddress != nullptr) && (addr > _dyldCacheAddress) ) {
+        const DyldSharedCache* dyldCache = (DyldSharedCache*)_dyldCacheAddress;
+        if ( addr < (void*)((uint8_t*)_dyldCacheAddress+dyldCache->mappedSize()) ) {
+            size_t cacheVmOffset = ((uint8_t*)addr - (uint8_t*)_dyldCacheAddress);
+            DyldCacheParser cacheParser(dyldCache, false);
+            launch_cache::ImageGroup cachedDylibsGroup(cacheParser.cachedDylibsGroup());
+            uint32_t mhCacheOffset;
+            uint8_t  foundPermissions;
+            launch_cache::Image image(cachedDylibsGroup.findImageByCacheOffset(cacheVmOffset, mhCacheOffset, foundPermissions));
+            if ( image.valid() ) {
+                *loadAddress = (mach_header*)((uint8_t*)_dyldCacheAddress + mhCacheOffset);
+                if ( permissions != nullptr )
+                    *permissions = foundPermissions;
+                return image;
+            }
+        }
+    }
+
+    __block const BinaryImage* foundImage = nullptr;
+    sLoadedImages.forEachWithReadLock(^(uint32_t anIndex, const LoadedImage& loadedImage, bool& stop) {
+        launch_cache::Image anImage(loadedImage.image());
+        if ( anImage.containsAddress(addr, loadedImage.loadedAddress(), permissions) ) {
+            *loadAddress = loadedImage.loadedAddress();
+            foundImage   = loadedImage.image();
+            stop = true;
+        }
+    });
+    return launch_cache::Image(foundImage);
+}
+
+const mach_header* AllImages::findLoadAddressByImage(const BinaryImage* targetImage) const
+{
+    __block const mach_header* foundAddress = nullptr;
+    sLoadedImages.forEachWithReadLock(^(uint32_t anIndex, const LoadedImage& loadedImage, bool& stop) {
+        if ( targetImage == loadedImage.image() ) {
+            foundAddress = loadedImage.loadedAddress();
+            stop = true;
+        }
+    });
+    return foundAddress;
+}
+
+const mach_header* AllImages::mainExecutable() const
+{
+    assert(_programVars != nullptr);
+    return (const mach_header*)_programVars->mh;
+}
+
+launch_cache::Image AllImages::mainExecutableImage() const
+{
+    assert(_mainClosure != nullptr);
+    const launch_cache::Closure            mainClosure(_mainClosure);
+    const dyld3::launch_cache::ImageGroup  mainGroup           = mainClosure.group();
+    const uint32_t                         mainExecutableIndex = mainClosure.mainExecutableImageIndex();
+    const dyld3::launch_cache::Image       mainImage           = mainGroup.image(mainExecutableIndex);
+    return mainImage;
+}
+
+void AllImages::setMainPath(const char* path )
+{
+    _mainExeOverridePath = path;
+}
+
+const char* AllImages::imagePath(const BinaryImage* binImage) const
+{
+#if __IPHONE_OS_VERSION_MIN_REQUIRED
+    // on iOS and watchOS, apps may be moved on device after closure built
+       if ( _mainExeOverridePath != nullptr ) {
+        if ( binImage == mainExecutableImage().binaryData() )
+            return _mainExeOverridePath;
+    }
+#endif
+    launch_cache::Image image(binImage);
+    return image.path();
+}
+
+void AllImages::setInitialGroups()
+{
+    DyldCacheParser cacheParser((DyldSharedCache*)_dyldCacheAddress, false);
+    sKnownGroups.addNoLock(cacheParser.cachedDylibsGroup());
+    sKnownGroups.addNoLock(cacheParser.otherDylibsGroup());
+    launch_cache::Closure closure(_mainClosure);
+    sKnownGroups.addNoLock(closure.group().binaryData());
+}
+
+const launch_cache::binary_format::ImageGroup* AllImages::cachedDylibsGroup()
+{
+    return sKnownGroups[0];
+}
+
+const launch_cache::binary_format::ImageGroup* AllImages::otherDylibsGroup()
+{
+    return sKnownGroups[1];
+}
+
+const AllImages::BinaryImageGroup* AllImages::mainClosureGroup()
+{
+    return sKnownGroups[2];
+}
+
+uint32_t AllImages::currentGroupsCount() const
+{
+    return sKnownGroups.count();
+}
+
+void AllImages::copyCurrentGroups(ImageGroupList& groups) const
+{
+    sKnownGroups.forEachWithReadLock(^(uint32_t index, const dyld3::launch_cache::binary_format::ImageGroup* const &grpData, bool &stop) {
+        if ( index < groups.count() )
+            groups[index] = grpData;
+    });
+}
+
+void AllImages::copyCurrentGroupsNoLock(ImageGroupList& groups) const
+{
+    sKnownGroups.forEachNoLock(^(uint32_t index, const dyld3::launch_cache::binary_format::ImageGroup* const &grpData, bool &stop) {
+        if ( index < groups.count() )
+            groups[index] = grpData;
+    });
+}
+
+const mach_header* AllImages::alreadyLoaded(uint64_t inode, uint64_t mtime, bool bumpRefCount)
+{
+    __block const mach_header* result = nullptr;
+    sLoadedImages.forEachWithReadLock(^(uint32_t anIndex, const LoadedImage& loadedImage, bool& stop) {
+        launch_cache::Image img(loadedImage.image());
+        if ( img.validateUsingModTimeAndInode() ) {
+            if ( (img.fileINode() == inode) && (img.fileModTime() == mtime) ) {
+                result = loadedImage.loadedAddress();
+                if ( bumpRefCount && !loadedImage.neverUnload() )
+                    incRefCount(loadedImage.loadedAddress());
+                stop = true;
+            }
+        }
+    });
+    return result;
+}
+
+const mach_header* AllImages::alreadyLoaded(const char* path, bool bumpRefCount)
+{
+    __block const mach_header* result = nullptr;
+    uint32_t targetHash = launch_cache::ImageGroup::hashFunction(path);
+    sLoadedImages.forEachWithReadLock(^(uint32_t anIndex, const LoadedImage& loadedImage, bool& stop) {
+        launch_cache::Image img(loadedImage.image());
+        if ( (img.pathHash() == targetHash) && (strcmp(path, imagePath(loadedImage.image())) == 0) ) {
+            result = loadedImage.loadedAddress();
+            if ( bumpRefCount && !loadedImage.neverUnload() )
+                incRefCount(loadedImage.loadedAddress());
+            stop = true;
+        }
+    });
+    if ( result == nullptr ) {
+        // perhaps there was an image override
+        launch_cache::ImageGroup mainGroup(mainClosureGroup());
+        STACK_ALLOC_DYNARRAY(const launch_cache::BinaryImageGroupData*, currentGroupsCount(), currentGroupsList);
+        copyCurrentGroups(currentGroupsList);
+        mainGroup.forEachImageRefOverride(currentGroupsList, ^(launch_cache::Image standardDylib, launch_cache::Image overrideDyilb, bool& stop) {
+            if ( strcmp(standardDylib.path(), path) == 0 ) {
+                result = alreadyLoaded(overrideDyilb.path(), bumpRefCount);
+                stop = true;
+            }
+        });
+    }
+    return result;
+}
+
+const mach_header* AllImages::alreadyLoaded(const BinaryImage* binImage, bool bumpRefCount)
+{
+    const mach_header* result = findLoadAddressByImage(binImage);
+    if ( result != nullptr ) {
+        launch_cache::Image loadedImage(binImage);
+        if ( bumpRefCount && !loadedImage.neverUnload() )
+            incRefCount(result);
+    }
+    return result;
+}
+
+void AllImages::incRefCount(const mach_header* loadAddress)
+{
+    __block bool found = false;
+    sDlopenRefCounts.forEachWithWriteLock(^(uint32_t index, DlopenCount& entry, bool& stop) {
+        if ( entry.loadAddress == loadAddress ) {
+            found = true;
+            entry.refCount += 1;
+            stop = true;
+        }
+    });
+    if ( !found ) {
+        DlopenCount newEnty = { loadAddress, 1 };
+        sDlopenRefCounts.add(newEnty);
+    }
+}
+
+void AllImages::decRefCount(const mach_header* loadAddress)
+{
+    __block bool refCountNowZero = false;
+    sDlopenRefCounts.forEachWithWriteLock(^(uint32_t index, DlopenCount& entry, bool& stop) {
+        if ( entry.loadAddress == loadAddress ) {
+            entry.refCount -= 1;
+            stop = true;
+            if ( entry.refCount == 0 )
+                refCountNowZero = true;
+        }
+    });
+    if ( refCountNowZero ) {
+        DlopenCount delEnty = { loadAddress, 0 };
+        sDlopenRefCounts.remove(delEnty);
+        garbageCollectImages();
+    }
+}
+
+
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
+__NSObjectFileImage* AllImages::addNSObjectFileImage()
+{
+    // look for empty slot first
+    __block __NSObjectFileImage* result = nullptr;
+    sNSObjectFileImages.forEachWithWriteLock(^(uint32_t index, __NSObjectFileImage& value, bool& stop) {
+        if ( (value.path == nullptr) && (value.memSource == nullptr) ) {
+            result = &value;
+            stop = true;
+        }
+    });
+    if ( result != nullptr )
+        return result;
+
+    // otherwise allocate new slot
+    __NSObjectFileImage empty;
+    return sNSObjectFileImages.add(empty);
+}
+
+bool AllImages::hasNSObjectFileImage(__NSObjectFileImage* ofi)
+{
+    __block bool result = false;
+    sNSObjectFileImages.forEachNoLock(^(uint32_t index, const __NSObjectFileImage& value, bool& stop) {
+        if ( &value == ofi ) {
+            result = ((value.memSource != nullptr) || (value.path != nullptr));
+            stop = true;
+        }
+    });
+    return result;
+}
+
+void AllImages::removeNSObjectFileImage(__NSObjectFileImage* ofi)
+{
+    sNSObjectFileImages.forEachWithWriteLock(^(uint32_t index, __NSObjectFileImage& value, bool& stop) {
+        if ( &value == ofi ) {
+            // mark slot as empty
+            ofi->path        = nullptr;
+            ofi->memSource   = nullptr;
+            ofi->memLength   = 0;
+            ofi->loadAddress = nullptr;
+            ofi->binImage    = nullptr;
+            stop = true;
+        }
+    });
+}
+#endif
+
+
+class VIS_HIDDEN Reaper
+{
+public:
+                        Reaper(uint32_t count, const LoadedImage** unloadables, bool* inUseArray);
+    void                garbageCollect();
+    void                finalizeDeadImages();
+
+private:
+    typedef launch_cache::binary_format::Image      BinaryImage;
+
+    void                markDirectlyDlopenedImagesAsUsed();
+    void                markDependentOfInUseImages();
+    void                markDependentsOf(const LoadedImage*);
+    bool                loadAddressIsUnloadable(const mach_header* loadAddr, uint32_t& index);
+    bool                imageIsUnloadable(const BinaryImage* binImage, uint32_t& foundIndex);
+    uint32_t            inUseCount();
+    void                dump(const char* msg);
+
+    const LoadedImage** _unloadablesArray;
+    bool*               _inUseArray;
+    uint32_t            _arrayCount;
+    uint32_t            _deadCount;
+};
+
+Reaper::Reaper(uint32_t count, const LoadedImage** unloadables, bool* inUseArray)
+ : _unloadablesArray(unloadables), _inUseArray(inUseArray),_arrayCount(count)
+{
+}
+
+
+bool Reaper::loadAddressIsUnloadable(const mach_header* loadAddr, uint32_t& foundIndex)
+{
+    for (uint32_t i=0; i < _arrayCount; ++i) {
+        if ( _unloadablesArray[i]->loadedAddress() == loadAddr ) {
+            foundIndex = i;
+            return true;
+        }
+    }
+    return false;
+}
+
+bool Reaper::imageIsUnloadable(const BinaryImage* binImage, uint32_t& foundIndex)
+{
+    for (uint32_t i=0; i < _arrayCount; ++i) {
+        if ( _unloadablesArray[i]->image() == binImage ) {
+            foundIndex = i;
+            return true;
+        }
+    }
+    return false;
+}
+
+void Reaper::markDirectlyDlopenedImagesAsUsed()
+{
+    sDlopenRefCounts.forEachWithReadLock(^(uint32_t refCountIndex, const dyld3::DlopenCount& dlEntry, bool& stop) {
+        if ( dlEntry.refCount != 0 ) {
+            uint32_t foundIndex;
+            if ( loadAddressIsUnloadable(dlEntry.loadAddress, foundIndex) ) {
+                _inUseArray[foundIndex] = true;
+            }
+         }
+    });
+}
+
+uint32_t Reaper::inUseCount()
+{
+    uint32_t count = 0;
+    for (uint32_t i=0; i < _arrayCount; ++i) {
+        if ( _inUseArray[i] )
+            ++count;
+    }
+    return count;
+}
+
+void Reaper::markDependentsOf(const LoadedImage* entry)
+{
+    const launch_cache::Image image(entry->image());
+    STACK_ALLOC_DYNARRAY(const launch_cache::BinaryImageGroupData*, gAllImages.currentGroupsCount(), currentGroupsList);
+    gAllImages.copyCurrentGroups(currentGroupsList);
+    image.forEachDependentImage(currentGroupsList, ^(uint32_t depIndex, dyld3::launch_cache::Image depImage, dyld3::launch_cache::Image::LinkKind kind, bool& stop) {
+        uint32_t foundIndex;
+        if ( !depImage.neverUnload() && imageIsUnloadable(depImage.binaryData(), foundIndex) ) {
+            _inUseArray[foundIndex] = true;
+        }
+    });
+}
+
+void Reaper::markDependentOfInUseImages()
+{
+    for (uint32_t i=0; i < _arrayCount; ++i) {
+        if ( _inUseArray[i] )
+            markDependentsOf(_unloadablesArray[i]);
+    }
+}
+
+void Reaper::dump(const char* msg)
+{
+    //log("%s:\n", msg);
+    for (uint32_t i=0; i < _arrayCount; ++i) {
+        dyld3::launch_cache::Image image(_unloadablesArray[i]->image());
+        //log("  in-used=%d  %s\n", _inUseArray[i], image.path());
+    }
+}
+
+void Reaper::garbageCollect()
+{
+    //dump("all unloadable images");
+
+    // mark all dylibs directly dlopen'ed as in use
+    markDirectlyDlopenedImagesAsUsed();
+
+    //dump("directly dlopen()'ed marked");
+
+    // iteratively mark dependents of in-use dylibs as in-use until in-use count stops changing
+    uint32_t lastCount = inUseCount();
+    bool countChanged = false;
+    do {
+        markDependentOfInUseImages();
+        //dump("dependents marked");
+        uint32_t newCount = inUseCount();
+        countChanged = (newCount != lastCount);
+        lastCount = newCount;
+    } while (countChanged);
+
+    _deadCount = _arrayCount - inUseCount();
+}
+
+void Reaper::finalizeDeadImages()
+{
+    if ( _deadCount == 0 )
+        return;
+    __cxa_range_t ranges[_deadCount];
+    __cxa_range_t* rangesArray = ranges;
+    __block unsigned int rangesCount = 0;
+    for (uint32_t i=0; i < _arrayCount; ++i) {
+        if ( _inUseArray[i] )
+            continue;
+        dyld3::launch_cache::Image image(_unloadablesArray[i]->image());
+        image.forEachDiskSegment(^(uint32_t segIndex, uint32_t fileOffset, uint32_t fileSize, int64_t vmOffset, uint64_t vmSize, uint8_t permissions, bool &stop) {
+            if ( permissions & VM_PROT_EXECUTE ) {
+                rangesArray[rangesCount].addr   = (char*)(_unloadablesArray[i]->loadedAddress()) + vmOffset;
+                rangesArray[rangesCount].length = (size_t)vmSize;
+                ++rangesCount;
+           }
+        });
+    }
+    __cxa_finalize_ranges(ranges, rangesCount);
+}
+
+
+// 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 sLoadedImages writer lock, so any dlopen/dlclose
+// on other threads are blocked while this garbage collections runs
+//
+void AllImages::garbageCollectImages()
+{
+    // if some other thread is currently GC'ing images, let other thread do the work
+    int32_t newCount = OSAtomicIncrement32(&_gcCount);
+    if ( newCount != 1 )
+        return;
+
+    do {
+        const uint32_t      loadedImageCount                    = sLoadedImages.count();
+        const LoadedImage*  unloadables[loadedImageCount];
+        bool                unloadableInUse[loadedImageCount];
+        const LoadedImage** unloadablesArray                    = unloadables;
+        bool*               unloadableInUseArray                = unloadableInUse;
+        __block uint32_t    unloadableCount                     = 0;
+        // do GC with lock, so no other images can be added during GC
+        sLoadedImages.withReadLock(^() {
+            sLoadedImages.forEachNoLock(^(uint32_t index, const LoadedImage& entry, bool& stop) {
+                const launch_cache::Image image(entry.image());
+                if ( !image.neverUnload() && !entry.neverUnload() ) {
+                    unloadablesArray[unloadableCount] = &entry;
+                    unloadableInUseArray[unloadableCount] = false;
+                    //log("unloadable[%d] %p %s\n", unloadableCount, entry.loadedAddress(), image.path());
+                    ++unloadableCount;
+                }
+            });
+            // make reaper object to do garbage collection and notifications
+            Reaper reaper(unloadableCount, unloadablesArray, unloadableInUseArray);
+            reaper.garbageCollect();
+
+            // FIXME: we should sort dead images so higher level ones are terminated first
+
+            // call cxa_finalize_ranges of dead images
+            reaper.finalizeDeadImages();
+
+            // FIXME: call static terminators of dead images
+
+            // FIXME: DOF unregister
+        });
+
+        //log("sLoadedImages before GC removals:\n");
+        //sLoadedImages.dump(^(const LoadedImage& entry) {
+        //    const launch_cache::Image image(entry.image());
+        //    log("   loadAddr=%p, path=%s\n", entry.loadedAddress(), image.path());
+        //});
+
+        // make copy of LoadedImages we want to remove
+        // because unloadables[] points into ChunkVector we are shrinking
+        uint32_t removalCount = 0;
+        for (uint32_t i=0; i < unloadableCount; ++i) {
+            if ( !unloadableInUse[i] )
+                ++removalCount;
+        }
+        if ( removalCount > 0 ) {
+            STACK_ALLOC_DYNARRAY(loader::ImageInfo, removalCount, unloadImages);
+            uint32_t removalIndex = 0;
+            for (uint32_t i=0; i < unloadableCount; ++i) {
+                if ( !unloadableInUse[i] ) {
+                    unloadImages[removalIndex].loadAddress = unloadables[i]->loadedAddress();
+                    unloadImages[removalIndex].imageData   = unloadables[i]->image();
+                    ++removalIndex;
+                }
+            }
+            // remove entries from sLoadedImages
+            removeImages(unloadImages);
+
+            //log("sLoadedImages after GC removals:\n");
+            //sLoadedImages.dump(^(const LoadedImage& entry) {
+            //    const launch_cache::Image image(entry.image());
+            //    //log("   loadAddr=%p, path=%s\n", entry.loadedAddress(), image.path());
+            //});
+        }
+
+        // if some other thread called GC during our work, redo GC on its behalf
+        newCount = OSAtomicDecrement32(&_gcCount);
+    }
+    while (newCount > 0);
+}
+
+
+
+VIS_HIDDEN
+const launch_cache::binary_format::Image* AllImages::messageClosured(const char* path, const char* apiName, const char* closuredErrorMessages[3], int& closuredErrorMessagesCount)
+{
+    __block const launch_cache::binary_format::Image* result = nullptr;
+    sKnownGroups.withWriteLock(^() {
+        ClosureBuffer::CacheIdent cacheIdent;
+        bzero(&cacheIdent, sizeof(cacheIdent));
+        if ( _dyldCacheAddress != nullptr ) {
+            const DyldSharedCache* dyldCache = (DyldSharedCache*)_dyldCacheAddress;
+            dyldCache->getUUID(cacheIdent.cacheUUID);
+            cacheIdent.cacheAddress     = (unsigned long)_dyldCacheAddress;
+            cacheIdent.cacheMappedSize  = dyldCache->mappedSize();
+        }
+        gPathOverrides.forEachPathVariant(path, ^(const char* possiblePath, bool& stopVariants) {
+            struct stat statBuf;
+            if ( stat(possiblePath, &statBuf) == 0 ) {
+                if ( S_ISDIR(statBuf.st_mode) ) {
+                    log_apis("   %s: path is directory: %s\n", apiName, possiblePath);
+                    if ( closuredErrorMessagesCount < 3 )
+                        closuredErrorMessages[closuredErrorMessagesCount++] = strdup("not a file");
+                }
+                else {
+                    // file exists, ask closured to build info for it
+                    STACK_ALLOC_DYNARRAY(const launch_cache::BinaryImageGroupData*, sKnownGroups.countNoLock(), currentGroupsList);
+                    gAllImages.copyCurrentGroupsNoLock(currentGroupsList);
+                    dyld3::launch_cache::DynArray<const dyld3::launch_cache::binary_format::ImageGroup*> nonCacheGroupList(currentGroupsList.count()-2, &currentGroupsList[2]);
+                    const dyld3::launch_cache::binary_format::ImageGroup* closuredCreatedGroupData = nullptr;
+                    ClosureBuffer closureBuilderInput(cacheIdent, path, nonCacheGroupList, gPathOverrides);
+                    ClosureBuffer closureBuilderOutput = dyld3::closured_CreateImageGroup(closureBuilderInput);
+                    if ( !closureBuilderOutput.isError() ) {
+                        vm_protect(mach_task_self(), closureBuilderOutput.vmBuffer(), closureBuilderOutput.vmBufferSize(), false, VM_PROT_READ);
+                        closuredCreatedGroupData = closureBuilderOutput.imageGroup();
+                        log_apis("   %s: closured built ImageGroup for path: %s\n", apiName, possiblePath);
+                        sKnownGroups.addNoLock(closuredCreatedGroupData);
+                        launch_cache::ImageGroup group(closuredCreatedGroupData);
+                        result = group.imageBinary(0);
+                        stopVariants = true;
+                    }
+                    else {
+                        log_apis("   %s: closured failed for path: %s, error: %s\n", apiName, possiblePath, closureBuilderOutput.errorMessage());
+                        if ( closuredErrorMessagesCount < 3 ) {
+                            closuredErrorMessages[closuredErrorMessagesCount++] = strdup(closureBuilderOutput.errorMessage());
+                        }
+                        closureBuilderOutput.free();
+                    }
+                }
+            }
+            else {
+                log_apis("   %s: file does not exist for path: %s\n", apiName, possiblePath);
+            }
+        });
+    });
+
+    return result;
+}
+
+const AllImages::BinaryImage* AllImages::findImageInKnownGroups(const char* path)
+{
+    __block const AllImages::BinaryImage* result = nullptr;
+    sKnownGroups.forEachWithReadLock(^(uint32_t index, const dyld3::launch_cache::binary_format::ImageGroup* const& grpData, bool& stop) {
+        launch_cache::ImageGroup group(grpData);
+        uint32_t ignore;
+        if ( const AllImages::BinaryImage* binImage = group.findImageByPath(path, ignore) ) {
+            result = binImage;
+            stop = true;
+        }
+    });
+    return result;
+}
+
+bool AllImages::imageUnloadable(const launch_cache::Image& image, const mach_header* loadAddress) const
+{
+    // check if statically determined in clousre that this can never be unloaded
+    if ( image.neverUnload() )
+        return false;
+
+    // check if some runtime decision made this be never-unloadable
+    __block bool foundAsNeverUnload = false;
+    sLoadedImages.forEachWithReadLock(^(uint32_t anIndex, const LoadedImage& loadedImage, bool& stop) {
+        if ( loadedImage.loadedAddress() == loadAddress ) {
+            stop = true;
+            if ( loadedImage.neverUnload() )
+                foundAsNeverUnload = true;
+        }
+    });
+    if ( foundAsNeverUnload )
+        return false;
+
+    return true;
+}
+
+void AllImages::addLoadNotifier(NotifyFunc func)
+{
+    // callback about already loaded images
+    const uint32_t      existingCount = sLoadedImages.count();
+    const mach_header*  existingMHs[existingCount];
+    const mach_header** existingArray = existingMHs;
+    sLoadedImages.forEachWithReadLock(^(uint32_t anIndex, const LoadedImage& loadedImage, bool& stop) {
+        if ( anIndex < existingCount )
+            existingArray[anIndex] = loadedImage.loadedAddress();
+    });
+    // we don't want to hold lock while calling out, so prebuild array (with lock) then do calls on that array (without lock)
+    for (uint32_t i=0; i < existingCount; i++) {
+        MachOParser parser(existingArray[i]);
+        log_notifications("dyld: add notifier %p called with mh=%p\n", func, existingArray[i]);
+        func(existingArray[i], parser.getSlide());
+    }
+
+    // add to list of functions to call about future loads
+    sLoadNotifiers.add(func);
+}
+
+void AllImages::addUnloadNotifier(NotifyFunc func)
+{
+    // add to list of functions to call about future unloads
+    sUnloadNotifiers.add(func);
+}
+
+void AllImages::setObjCNotifiers(_dyld_objc_notify_mapped map, _dyld_objc_notify_init init, _dyld_objc_notify_unmapped unmap)
+{
+    _objcNotifyMapped   = map;
+    _objcNotifyInit     = init;
+    _objcNotifyUnmapped = unmap;
+
+    // callback about already loaded images
+    uint32_t                    maxCount = count();
+    const char*                 pathsBuffer[maxCount];
+    const mach_header*          mhBuffer[maxCount];
+    __block const char**        paths = pathsBuffer;
+    __block const mach_header** mhs = mhBuffer;
+    __block uint32_t            imagesWithObjC = 0;
+    sLoadedImages.forEachWithReadLock(^(uint32_t anIndex, const LoadedImage& loadedImage, bool& stop) {
+        launch_cache::Image img(loadedImage.image());
+        if ( img.hasObjC() ) {
+            mhs[imagesWithObjC]   = loadedImage.loadedAddress();
+            paths[imagesWithObjC] = imagePath(loadedImage.image());
+            ++imagesWithObjC;
+       }
+    });
+    if ( imagesWithObjC != 0 ) {
+        (*map)(imagesWithObjC, pathsBuffer, mhBuffer);
+        if ( log_notifications("dyld: objc-mapped-notifier called with %d images:\n", imagesWithObjC) ) {
+            for (uint32_t i=0; i < imagesWithObjC; ++i) {
+                log_notifications("dyld:  objc-mapped: %p %s\n",  mhBuffer[i], pathsBuffer[i]);
+            }
+        }
+    }
+}
+
+void AllImages::vmAccountingSetSuspended(bool suspend)
+{
+#if __arm__ || __arm64__
+    // <rdar://problem/29099600> dyld should tell the kernel when it is doing fix-ups caused by roots
+    log_fixups("vm.footprint_suspend=%d\n", suspend);
+    int newValue = suspend ? 1 : 0;
+    int oldValue = 0;
+    size_t newlen = sizeof(newValue);
+    size_t oldlen = sizeof(oldValue);
+    sysctlbyname("vm.footprint_suspend", &oldValue, &oldlen, &newValue, newlen);
+#endif
+}
+
+void AllImages::applyInterposingToDyldCache(const launch_cache::binary_format::Closure* closure, const dyld3::launch_cache::DynArray<loader::ImageInfo>& initialImages)
+{
+    launch_cache::Closure    mainClosure(closure);
+    launch_cache::ImageGroup mainGroup = mainClosure.group();
+    DyldCacheParser cacheParser((DyldSharedCache*)_dyldCacheAddress, false);
+    const launch_cache::binary_format::ImageGroup* dylibsGroupData = cacheParser.cachedDylibsGroup();
+    launch_cache::ImageGroup dyldCacheDylibGroup(dylibsGroupData);
+    __block bool suspendedAccounting = false;
+    mainGroup.forEachDyldCacheSymbolOverride(^(uint32_t patchTableIndex, const launch_cache::binary_format::Image* imageData, uint32_t imageOffset, bool& stop) {
+        bool foundInImages = false;
+        for (int i=0; i < initialImages.count(); ++i) {
+            if ( initialImages[i].imageData == imageData ) {
+                foundInImages = true;
+                uintptr_t replacement = (uintptr_t)(initialImages[i].loadAddress) + imageOffset;
+                dyldCacheDylibGroup.forEachDyldCachePatchLocation(_dyldCacheAddress, patchTableIndex, ^(uintptr_t* locationToPatch, uintptr_t addend, bool& innerStop) {
+                    if ( !suspendedAccounting ) {
+                        vmAccountingSetSuspended(true);
+                        suspendedAccounting = true;
+                    }
+                    log_fixups("dyld: cache fixup: *%p = %p\n", locationToPatch, (void*)replacement);
+                    *locationToPatch = replacement + addend;
+                });
+                break;
+            }
+        }
+        if ( !foundInImages ) {
+            launch_cache::Image img(imageData);
+            log_fixups("did not find loaded image to patch into cache: %s\n", img.path());
+        }
+    });
+    if ( suspendedAccounting )
+        vmAccountingSetSuspended(false);
+}
+
+void AllImages::runLibSystemInitializer(const mach_header* libSystemAddress, const launch_cache::binary_format::Image* libSystemBinImage)
+{
+    // run all initializers in image
+    launch_cache::Image libSystemImage(libSystemBinImage);
+    libSystemImage.forEachInitializer(libSystemAddress, ^(const void* func) {
+        Initializer initFunc = (Initializer)func;
+        dyld3::kdebug_trace_dyld_duration(DBG_DYLD_TIMING_STATIC_INITIALIZER, (uint64_t)func, 0, ^{
+            initFunc(NXArgc, NXArgv, environ, appleParams, _programVars);
+        });
+        log_initializers("called initialzer %p in %s\n", initFunc, libSystemImage.path());
+    });
+
+    // mark libSystem.dylib as being init, so later recursive-init would re-run it
+    sLoadedImages.forEachWithWriteLock(^(uint32_t anIndex, LoadedImage& loadedImage, bool& stop) {
+        if ( loadedImage.loadedAddress() == libSystemAddress ) {
+            loadedImage.setState(LoadedImage::State::inited);
+            stop = true;
+        }
+    });
+}
+
+void AllImages::runInitialzersBottomUp(const mach_header* imageLoadAddress)
+{
+    launch_cache::Image topImage = findByLoadAddress(imageLoadAddress);
+    if ( topImage.isInvalid() )
+        return;
+
+    // closure contains list of intializers to run in-order
+    STACK_ALLOC_DYNARRAY(const launch_cache::BinaryImageGroupData*, currentGroupsCount(), currentGroupsList);
+    copyCurrentGroups(currentGroupsList);
+    topImage.forEachInitBefore(currentGroupsList, ^(launch_cache::Image imageToInit) {
+        // find entry
+        __block LoadedImage* foundEntry = nullptr;
+        sLoadedImages.forEachWithReadLock(^(uint32_t index, const LoadedImage& entry, bool& stop) {
+            if ( entry.image() == imageToInit.binaryData() ) {
+                foundEntry = (LoadedImage*)&entry;
+                stop = true;
+            }
+        });
+        assert(foundEntry != nullptr);
+        pthread_mutex_lock(&_initializerLock);
+            // Note, due to the large lock in dlopen, we can't be waiting on another thread
+            // here, but its possible that we are in a dlopen which is initialising us again
+            if ( foundEntry->state() == LoadedImage::State::beingInited ) {
+                log_initializers("dyld: already initializing '%s'\n", imagePath(imageToInit.binaryData()));
+            }
+            // at this point, the image is either initialized or not
+            // if not, initialize it on this thread
+            if ( foundEntry->state() == LoadedImage::State::uninited ) {
+                foundEntry->setState(LoadedImage::State::beingInited);
+                // release initializer lock, so other threads can run initializers
+                pthread_mutex_unlock(&_initializerLock);
+                // tell objc to run any +load methods in image
+                if ( (_objcNotifyInit != nullptr) && imageToInit.mayHavePlusLoads() ) {
+                    log_notifications("dyld: objc-init-notifier called with mh=%p, path=%s\n", foundEntry->loadedAddress(), imagePath(imageToInit.binaryData()));
+                    (*_objcNotifyInit)(imagePath(imageToInit.binaryData()), foundEntry->loadedAddress());
+                }
+                // run all initializers in image
+                imageToInit.forEachInitializer(foundEntry->loadedAddress(), ^(const void* func) {
+                    Initializer initFunc = (Initializer)func;
+                    dyld3::kdebug_trace_dyld_duration(DBG_DYLD_TIMING_STATIC_INITIALIZER, (uint64_t)func, 0, ^{
+                        initFunc(NXArgc, NXArgv, environ, appleParams, _programVars);
+                    });
+                    log_initializers("dyld: called initialzer %p in %s\n", initFunc, imageToInit.path());
+                });
+                // reaquire initializer lock to switch state to inited
+                pthread_mutex_lock(&_initializerLock);
+                foundEntry->setState(LoadedImage::State::inited);
+            }
+        pthread_mutex_unlock(&_initializerLock);
+    });
+}
+
+
+} // namespace dyld3
+
+
+
+
+
+
diff --git a/dyld3/AllImages.h b/dyld3/AllImages.h
new file mode 100644 (file)
index 0000000..1f644bc
--- /dev/null
@@ -0,0 +1,169 @@
+/*
+ * Copyright (c) 2017 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 __ALL_IMAGES_H__
+#define __ALL_IMAGES_H__
+
+#include <pthread.h>
+#include <mach-o/loader.h>
+
+#include "dyld_priv.h"
+
+#include "LaunchCache.h"
+#include "Loading.h"
+
+
+
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
+// only in macOS and deprecated 
+struct VIS_HIDDEN __NSObjectFileImage
+{
+    const char*                                         path        = nullptr;
+    const void*                                         memSource   = nullptr;
+    size_t                                              memLength   = 0;
+    const mach_header*                                  loadAddress = nullptr;
+    const dyld3::launch_cache::binary_format::Image*    binImage    = nullptr;
+};
+#endif
+
+namespace dyld3 {
+
+class VIS_HIDDEN AllImages
+{
+public:
+    typedef launch_cache::binary_format::Closure    BinaryClosure;
+    typedef launch_cache::binary_format::ImageGroup BinaryImageGroup;
+    typedef launch_cache::binary_format::Image      BinaryImage;
+    typedef launch_cache::ImageGroupList            ImageGroupList;
+    typedef void                                    (*NotifyFunc)(const mach_header* mh, intptr_t slide);
+
+    void                        init(const BinaryClosure* closure, const void* dyldCacheLoadAddress, const char* dyldCachePath,
+                                     const dyld3::launch_cache::DynArray<loader::ImageInfo>& initialImages);
+    void                        setMainPath(const char* path);
+    void                        applyInitialImages();
+
+    void                        addImages(const dyld3::launch_cache::DynArray<loader::ImageInfo>& newImages);
+    void                        removeImages(const launch_cache::DynArray<loader::ImageInfo>& unloadImages);
+    void                        setNeverUnload(const loader::ImageInfo& existingImage);
+    void                        applyInterposingToDyldCache(const launch_cache::binary_format::Closure* closure, const dyld3::launch_cache::DynArray<loader::ImageInfo>& initialImages);
+    void                        runInitialzersBottomUp(const mach_header* imageLoadAddress);
+    void                        setInitialGroups();
+
+    uint32_t                    count() const;
+    const BinaryImageGroup*     cachedDylibsGroup();
+    const BinaryImageGroup*     otherDylibsGroup();
+    const BinaryImageGroup*     mainClosureGroup();
+    const BinaryClosure*        mainClosure() { return _mainClosure; }
+    uint32_t                    currentGroupsCount() const;
+    void                        copyCurrentGroups(ImageGroupList& groups) const;
+
+    const BinaryImage*          messageClosured(const char* path, const char* apiName, const char* closuredErrorMessages[3], int& closuredErrorMessagesCount);
+
+    launch_cache::Image         findByLoadOrder(uint32_t index, const mach_header** loadAddress) const;
+    launch_cache::Image         findByLoadAddress(const mach_header* loadAddress) const;
+    launch_cache::Image         findByOwnedAddress(const void* addr, const mach_header** loadAddress, uint8_t* permissions=nullptr) const;
+    const mach_header*          findLoadAddressByImage(const BinaryImage*) const;
+    bool                        findIndexForLoadAddress(const mach_header* loadAddress, uint32_t& index);
+    void                        forEachImage(void (^handler)(uint32_t imageIndex, const mach_header* loadAddress, const launch_cache::Image image, bool& stop)) const;
+
+    const mach_header*          mainExecutable() const;
+    launch_cache::Image         mainExecutableImage() const;
+    const void*                 cacheLoadAddress() const { return _dyldCacheAddress; }
+    const char*                 dyldCachePath() const { return _dyldCachePath; }
+    const char*                 imagePath(const BinaryImage*) const;
+
+    const mach_header*          alreadyLoaded(const char* path, bool bumpRefCount);
+    const mach_header*          alreadyLoaded(const BinaryImage*, bool bumpRefCount);
+    const mach_header*          alreadyLoaded(uint64_t inode, uint64_t mtime, bool bumpRefCount);
+    const BinaryImage*          findImageInKnownGroups(const char* path);
+
+    bool                        imageUnloadable(const launch_cache::Image& image, const mach_header* loadAddress) const;
+    void                        incRefCount(const mach_header* loadAddress);
+    void                        decRefCount(const mach_header* loadAddress);
+
+    void                        addLoadNotifier(NotifyFunc);
+    void                        addUnloadNotifier(NotifyFunc);
+    void                        setObjCNotifiers(_dyld_objc_notify_mapped, _dyld_objc_notify_init, _dyld_objc_notify_unmapped);
+    void                        notifyObjCUnmap(const char* path, const struct mach_header* mh);
+
+    void                        runLibSystemInitializer(const mach_header* imageLoadAddress, const launch_cache::binary_format::Image* binImage);
+
+    void                        setOldAllImageInfo(dyld_all_image_infos* old) { _oldAllImageInfos = old; }
+    dyld_all_image_infos*       oldAllImageInfo() const { return _oldAllImageInfos;}
+
+    void                        notifyMonitorMain();
+    void                        notifyMonitorLoads(const launch_cache::DynArray<loader::ImageInfo>& newImages);
+    void                        notifyMonitorUnloads(const launch_cache::DynArray<loader::ImageInfo>& unloadingImages);
+
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
+    __NSObjectFileImage*        addNSObjectFileImage();
+    bool                        hasNSObjectFileImage(__NSObjectFileImage*);
+    void                        removeNSObjectFileImage(__NSObjectFileImage*);
+#endif
+
+    struct ProgramVars
+    {
+        const void*        mh;
+        int*               NXArgcPtr;
+        const char***      NXArgvPtr;
+        const char***      environPtr;
+        const char**       __prognamePtr;
+    };
+    void                    setProgramVars(ProgramVars* vars);
+
+private:
+
+    typedef void (*Initializer)(int argc, const char* argv[], char* envp[], const char* apple[], const ProgramVars* vars);
+    typedef const launch_cache::DynArray<loader::ImageInfo> StartImageArray;
+    
+    void                        runInitialzersInImage(const mach_header* imageLoadAddress, const launch_cache::binary_format::Image* binImage);
+    void                        mirrorToOldAllImageInfos();
+    void                        garbageCollectImages();
+    void                        vmAccountingSetSuspended(bool suspend);
+    void                        copyCurrentGroupsNoLock(ImageGroupList& groups) const;
+
+    const BinaryClosure*                    _mainClosure         = nullptr;
+    const void*                             _dyldCacheAddress    = nullptr;
+    const char*                             _dyldCachePath       = nullptr;
+    StartImageArray*                        _initialImages       = nullptr;
+    const char*                             _mainExeOverridePath = nullptr;
+    _dyld_objc_notify_mapped                _objcNotifyMapped    = nullptr;
+    _dyld_objc_notify_init                  _objcNotifyInit      = nullptr;
+    _dyld_objc_notify_unmapped              _objcNotifyUnmapped  = nullptr;
+    ProgramVars*                            _programVars         = nullptr;
+    dyld_all_image_infos*                   _oldAllImageInfos    = nullptr;
+    dyld_image_info*                        _oldAllImageArray    = nullptr;
+    uint32_t                                _oldArrayAllocCount  = 0;
+    pthread_mutex_t                         _initializerLock     = PTHREAD_RECURSIVE_MUTEX_INITIALIZER;
+    pthread_cond_t                          _initializerCondition= PTHREAD_COND_INITIALIZER;
+    int32_t                                 _gcCount             = 0;
+};
+
+extern AllImages gAllImages;
+
+
+} // dyld3
+
+
+#endif // __ALL_IMAGES_H__
diff --git a/dyld3/ClosureBuffer.cpp b/dyld3/ClosureBuffer.cpp
new file mode 100644 (file)
index 0000000..0231827
--- /dev/null
@@ -0,0 +1,273 @@
+/*
+ * Copyright (c) 2017 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 <assert.h>
+#include <mach/mach.h>
+#include <dispatch/dispatch.h>
+#include <bootstrap.h>
+
+#include "ClosureBuffer.h"
+#include "PathOverrides.h"
+
+
+namespace dyld3 {
+
+TypedContentBuffer::TypedContentBuffer(size_t elementsCount, size_t elementsTotalSize)
+{
+    _size = elementsTotalSize + (elementsCount+1)*(sizeof(Element)+4);  // worst case padding, plus "end" element
+    vm_address_t bufferAddress = 0;
+    assert(::vm_allocate(mach_task_self(), &bufferAddress, _size, VM_FLAGS_ANYWHERE) == 0);
+    _buffer = (Element*)bufferAddress;
+    _currentEnd = _buffer;
+    _readOnly = false;
+}
+
+void TypedContentBuffer::free()
+{
+    if ( _buffer != nullptr )
+        vm_deallocate(mach_task_self(), (long)_buffer, _size);
+    _buffer = nullptr;
+}
+
+void TypedContentBuffer::addItem(uint32_t k, const void* content, size_t len)
+{
+    assert(!_readOnly);
+    assert(((char*)_currentEnd + len) < ((char*)_buffer + _size));
+    _currentEnd->kind = k;
+    _currentEnd->contentLength = (uint32_t)len;
+    if ( len != 0 )
+        memmove(&(_currentEnd->content), content, len);
+    size_t delta = (sizeof(Element) + len + 3) & (-4);
+    _currentEnd = (Element*)((char*)_currentEnd + delta);
+}
+
+vm_address_t TypedContentBuffer::vmBuffer() const
+{
+    assert(_readOnly);
+    return (vm_address_t)_buffer;
+}
+
+uint32_t TypedContentBuffer::vmBufferSize() const
+{
+    assert(_readOnly);
+    return (uint32_t)_size;
+}
+
+void TypedContentBuffer::doneBuilding()
+{
+    _readOnly = true;
+}
+
+
+const TypedContentBuffer::Element* TypedContentBuffer::Element::next() const
+{
+   return (Element*)((char*)this + sizeof(Element) + ((contentLength + 3) & -4));
+}
+
+TypedContentBuffer::TypedContentBuffer(const void* buff, size_t buffSize)
+    : _size(buffSize), _buffer((Element*)buff), _currentEnd((Element*)((char*)buff+buffSize)), _readOnly(true)
+{
+}
+
+unsigned TypedContentBuffer::count(uint32_t kind) const
+{
+    assert(_readOnly);
+    unsigned count = 0;
+    for (const Element* e = _buffer; e->kind != 0; e = e->next()) {
+        if ( e->kind == kind )
+            ++count;
+    }
+    return count;
+}
+
+void TypedContentBuffer::forEach(uint32_t kind, void (^callback)(const void* content, size_t length)) const
+{
+    assert(_readOnly);
+    for (const Element* e = _buffer; e->kind != 0; e = e->next()) {
+        if ( e->kind == kind ) {
+            callback(&(e->content), e->contentLength);
+        }
+    }
+}
+
+#if !BUILDING_CLOSURED
+
+ClosureBuffer::ClosureBuffer(const CacheIdent& cacheIdent, const char* path, const launch_cache::ImageGroupList& groups, const PathOverrides& envVars)
+    : TypedContentBuffer(2 + envVars.envVarCount() + groups.count(), computeSize(path, groups, envVars))
+{
+    addItem(kindCacheIdent, &cacheIdent, sizeof(CacheIdent));
+    addItem(kindTargetPath, path, strlen(path)+1);
+    envVars.forEachEnvVar(^(const char* envVar) {
+        addItem(kindEnvVar, envVar, strlen(envVar)+1);
+    });
+    for (size_t i=0; i < groups.count(); ++i) {
+        launch_cache::ImageGroup group(groups[i]);
+        addItem(kindImageGroup, group.binaryData(), group.size());
+    }
+    addItem(kindEnd, nullptr, 0);
+    doneBuilding();
+}
+
+size_t ClosureBuffer::computeSize(const char* path, const launch_cache::ImageGroupList& groups, const PathOverrides& envVars)
+{
+    __block size_t result = sizeof(CacheIdent);
+    result += (strlen(path) + 1);
+    envVars.forEachEnvVar(^(const char* envVar) {
+        result += (strlen(envVar) + 1);
+    });
+    for (size_t i=0; i < groups.count(); ++i) {
+        launch_cache::ImageGroup group(groups[i]);
+        result += group.size();
+    }
+    return result;
+}
+
+#endif
+
+ClosureBuffer::ClosureBuffer(const char* errorMessage)
+    : TypedContentBuffer(1, strlen(errorMessage+2))
+{
+    addItem(kindErrorMessage, errorMessage, strlen(errorMessage)+1);
+    doneBuilding();
+}
+
+ClosureBuffer::ClosureBuffer(const launch_cache::BinaryImageGroupData* imageGroup)
+    : TypedContentBuffer(1, launch_cache::ImageGroup(imageGroup).size())
+{
+    addItem(kindImageGroup, imageGroup, launch_cache::ImageGroup(imageGroup).size());
+    doneBuilding();
+}
+
+ClosureBuffer::ClosureBuffer(const launch_cache::BinaryClosureData* closure)
+    : TypedContentBuffer(1, launch_cache::Closure(closure).size())
+{
+    addItem(kindClosure, closure, launch_cache::Closure(closure).size());
+    doneBuilding();
+}
+
+
+ClosureBuffer::ClosureBuffer(const void* buff, size_t buffSize)
+    : TypedContentBuffer(buff, buffSize)
+{
+}
+
+const ClosureBuffer::CacheIdent& ClosureBuffer::cacheIndent() const
+{
+    __block CacheIdent* ident = nullptr;
+    forEach(kindCacheIdent, ^(const void* content, size_t length) {
+        ident = (CacheIdent*)content;
+        assert(length == sizeof(CacheIdent));
+    });
+    assert(ident != nullptr);
+    return *ident;
+}
+
+const char* ClosureBuffer::targetPath() const
+{
+    __block char* path = nullptr;
+    forEach(kindTargetPath, ^(const void* content, size_t length) {
+        path = (char*)content;
+    });
+    assert(path != nullptr);
+    return path;
+}
+
+uint32_t ClosureBuffer::envVarCount() const
+{
+    __block uint32_t count = 0;
+    forEach(kindEnvVar, ^(const void* content, size_t length) {
+        ++count;
+    });
+    return count;
+}
+
+void ClosureBuffer::copyImageGroups(const char* envVars[]) const
+{
+    __block uint32_t index = 0;
+    forEach(kindEnvVar, ^(const void* content, size_t length) {
+        envVars[index] = (char*)content;
+        ++index;
+    });
+}
+
+uint32_t ClosureBuffer::imageGroupCount() const
+{
+    __block uint32_t count = 0;
+    forEach(kindImageGroup, ^(const void* content, size_t length) {
+        ++count;
+    });
+    return count;
+}
+
+void ClosureBuffer::copyImageGroups(const launch_cache::BinaryImageGroupData* imageGroups[]) const
+{
+    __block uint32_t index = 0;
+    forEach(kindImageGroup, ^(const void* content, size_t length) {
+        imageGroups[index] = (launch_cache::BinaryImageGroupData*)content;
+        ++index;
+    });
+}
+
+bool ClosureBuffer::isError() const
+{
+    return ( errorMessage() != nullptr );
+}
+
+const char* ClosureBuffer::errorMessage() const
+{
+    __block char* message = nullptr;
+    forEach(kindErrorMessage, ^(const void* content, size_t length) {
+        message = (char*)content;
+    });
+    return message;
+}
+
+const launch_cache::BinaryClosureData* ClosureBuffer::closure() const
+{
+   __block const launch_cache::BinaryClosureData* result = nullptr;
+    forEach(kindClosure, ^(const void* content, size_t length) {
+        result = (const launch_cache::BinaryClosureData*)content;
+    });
+    assert(result != nullptr);
+    return result;
+}
+
+
+const launch_cache::BinaryImageGroupData* ClosureBuffer::imageGroup() const
+{
+    __block const launch_cache::BinaryImageGroupData* result = nullptr;
+    forEach(kindImageGroup, ^(const void* content, size_t length) {
+        result = (const launch_cache::BinaryImageGroupData*)content;
+    });
+    assert(result != nullptr);
+    return result;
+}
+
+
+
+
+
+
+} // namespace dyld3
+
diff --git a/dyld3/ClosureBuffer.h b/dyld3/ClosureBuffer.h
new file mode 100644 (file)
index 0000000..c1ab2c3
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 2017 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_CLOSURE_BUFFER_H__
+#define __DYLD_CLOSURE_BUFFER_H__
+
+#include "Logging.h"
+#include "LaunchCache.h"
+#include "PathOverrides.h"
+
+namespace dyld3 {
+
+
+// simple class for packing typed content into a vm_allocated buffer
+class VIS_HIDDEN TypedContentBuffer
+{
+public:
+    // buffer creation
+                    TypedContentBuffer(size_t elementsCount, size_t elementsTotalSize);
+    void            addItem(uint32_t k, const void* content, size_t len);
+    void            doneBuilding();
+    vm_address_t    vmBuffer() const;
+    uint32_t        vmBufferSize() const;
+
+    // buffer parsing
+                TypedContentBuffer(const void* buff, size_t buffSize);
+    unsigned    count(uint32_t) const;
+    void        forEach(uint32_t, void (^callback)(const void* content, size_t length)) const;
+
+    void        free();
+
+private:
+    struct Element
+    {
+        uint32_t    kind;
+        uint32_t    contentLength;
+        uint8_t     content[];
+
+        const Element* next() const;
+    };
+
+    size_t      _size;
+    Element*    _buffer;
+    Element*    _currentEnd;
+    bool        _readOnly;
+};
+
+
+class VIS_HIDDEN ClosureBuffer : public TypedContentBuffer
+{
+public:
+
+    struct CacheIdent
+    {
+        uint8_t     cacheUUID[16];
+        uint64_t    cacheAddress;
+        uint64_t    cacheMappedSize;
+    };
+
+    // client creation
+                        ClosureBuffer(const CacheIdent&, const char* path, const launch_cache::ImageGroupList& groups, const PathOverrides& envVars);
+
+    // closured creation
+                        ClosureBuffer(const char* errorMessage);
+                        ClosureBuffer(const launch_cache::BinaryImageGroupData* imageGroupResult);
+                        ClosureBuffer(const launch_cache::BinaryClosureData* closureResult);
+
+    // client extraction
+    bool                                        isError() const;
+    const char*                                 errorMessage() const;
+    const launch_cache::BinaryClosureData*      closure() const;
+    const launch_cache::BinaryImageGroupData*   imageGroup() const;
+
+    // closure builder usage
+                        ClosureBuffer(const void* buff, size_t buffSize);
+    const CacheIdent&   cacheIndent() const;
+    const char*         targetPath() const;
+    uint32_t            envVarCount() const;
+    void                copyImageGroups(const char* envVars[]) const;
+    uint32_t            imageGroupCount() const;
+    void                copyImageGroups(const launch_cache::BinaryImageGroupData* imageGroups[]) const;
+
+private:
+    enum { kindEnd=0, kindCacheIdent, kindTargetPath, kindEnvVar, kindImageGroup, kindClosure, kindErrorMessage };
+    static size_t       computeSize(const char* path, const launch_cache::ImageGroupList& groups, const PathOverrides& envVars);
+
+};
+
+
+
+
+} // namespace dyld3
+
+#endif // __DYLD_CLOSURE_BUFFER_H__
diff --git a/dyld3/CodeSigningTypes.h b/dyld3/CodeSigningTypes.h
new file mode 100644 (file)
index 0000000..c84a708
--- /dev/null
@@ -0,0 +1,156 @@
+/* -*- 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 <stdint.h>
+#include <stddef.h>
+
+
+//
+// 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_ALTERNATE_CODEDIRECTORIES     = 0x1000,
+    CSSLOT_ALTERNATE_CODEDIRECTORY_MAX   = 5,
+    CSSLOT_ALTERNATE_CODEDIRECTORY_LIMIT =
+        CSSLOT_ALTERNATE_CODEDIRECTORIES + CSSLOT_ALTERNATE_CODEDIRECTORY_MAX,
+    CSSLOT_CMS_SIGNATURE                 = 0x10000,
+
+    kSecCodeSignatureAdhoc      = 2
+};
+
+enum {
+    CS_REQUIRE_LV               = 0x0002000    // require library validation
+};
+
+//
+// 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)
+
+    char end_earliest[0];
+
+    /* Version 0x20100 */
+    uint32_t scatterOffset;         /* offset of optional scatter vector */
+    char end_withScatter[0];
+
+    /* Version 0x20200 */
+    uint32_t teamOffset;            /* offset of optional team identifier */
+    char end_withTeam[0];
+
+    /* Version 0x20300 */
+    uint32_t spare3;                /* unused (must be zero) */
+    uint64_t codeLimit64;           /* limit to main image signature range, 64 bits */
+    char end_withCodeLimit64[0];
+
+    /* Version 0x20400 */
+    uint64_t execSegBase;           /* offset of executable segment */
+    uint64_t execSegLimit;          /* limit of executable segment */
+    uint64_t execSegFlags;          /* exec segment flags */
+    char end_withExecSeg[0];
+
+    /* 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/dyld3/Diagnostics.cpp b/dyld3/Diagnostics.cpp
new file mode 100644 (file)
index 0000000..a8e4bca
--- /dev/null
@@ -0,0 +1,214 @@
+/*
+ * Copyright (c) 2017 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 <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <uuid/uuid.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <_simple.h>
+#include <unistd.h>
+#include <sys/uio.h>
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#include <sys/resource.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <dirent.h>
+#include <mach/mach.h>
+#include <mach/machine.h>
+#include <mach-o/loader.h>
+#include <mach-o/nlist.h>
+#include <mach-o/fat.h>
+#include <pthread.h>
+#include <libc_private.h>
+
+#include "Diagnostics.h"
+
+#if BUILDING_CACHE_BUILDER
+  #include <dispatch/dispatch.h>
+  dispatch_queue_t sWarningQueue = dispatch_queue_create("com.apple.dyld.cache-builder.warnings", NULL);
+#endif
+
+Diagnostics::Diagnostics(bool verbose)
+#if !DYLD_IN_PROCESS
+    : _verbose(verbose)
+    , _prefix("")
+#endif
+{
+}
+
+#if !DYLD_IN_PROCESS
+Diagnostics::Diagnostics(const std::string& prefix, bool verbose)
+    : _verbose(verbose)
+    , _prefix(prefix)
+{
+}
+#endif
+
+Diagnostics::~Diagnostics()
+{
+    clearError();
+}
+
+void Diagnostics::error(const char* format, ...)
+{
+    _buffer = _simple_salloc();
+    va_list    list;
+    va_start(list, format);
+    _simple_vsprintf(_buffer, format, list);
+    va_end(list);
+
+#if !DYLD_IN_PROCESS
+    if ( !_verbose )
+        return;
+    
+    char *output_string;
+    va_start(list, format);
+    vasprintf(&output_string, format, list);
+    va_end(list);
+    
+    if (_prefix.empty()) {
+        fprintf(stderr, "%s", output_string);
+    } else {
+        fprintf(stderr, "[%s] %s", _prefix.c_str(), output_string);
+    }
+#endif
+}
+
+bool Diagnostics::hasError() const
+{
+    return _buffer != nullptr;
+}
+
+bool Diagnostics::noError() const
+{
+    return _buffer == nullptr;
+}
+
+void Diagnostics::clearError()
+{
+    if ( _buffer )
+        _simple_sfree(_buffer);
+    _buffer = nullptr;
+}
+
+void Diagnostics::assertNoError() const
+{
+    if ( _buffer != nullptr )
+        abort_report_np("%s", _simple_string(_buffer));
+}
+
+#if DYLD_IN_PROCESS
+const char* Diagnostics::errorMessage() const
+{
+    return _simple_string(_buffer);
+}
+
+#else
+void Diagnostics::warning(const char* format, ...)
+{
+    _SIMPLE_STRING tmp = _simple_salloc();
+    va_list    list;
+    va_start(list, format);
+    _simple_vsprintf(tmp, format, list);
+    va_end(list);
+#if BUILDING_CACHE_BUILDER
+    dispatch_sync(sWarningQueue, ^{
+        _warnings.insert(_simple_string(tmp));
+    });
+#else
+    _warnings.insert(_simple_string(tmp));
+#endif
+    _simple_sfree(tmp);
+}
+
+void Diagnostics::verbose(const char* format, ...)
+{
+    if ( !_verbose )
+        return;
+
+    char*   output_string;
+    va_list list;
+    va_start(list, format);
+    vasprintf(&output_string, format, list);
+    va_end(list);
+
+    if (_prefix.empty()) {
+        fprintf(stderr, "%s", output_string);
+    } else {
+        fprintf(stderr, "[%s] %s", _prefix.c_str(), output_string);
+    }
+}
+
+const std::string Diagnostics::prefix() const
+{
+    return _prefix;
+}
+
+void Diagnostics::copy(const Diagnostics& other)
+{
+    if ( other.hasError() )
+        error("%s", other.errorMessage().c_str());
+    for (const std::string& warn : other.warnings())
+        warning("%s", warn.c_str());
+}
+
+std::string Diagnostics::errorMessage() const
+{
+    if ( _buffer != nullptr )
+        return _simple_string(_buffer);
+    else
+        return std::string();
+}
+
+const std::set<std::string> Diagnostics::warnings() const
+{
+#if BUILDING_CACHE_BUILDER
+    __block std::set<std::string> retval;
+    dispatch_sync(sWarningQueue, ^{
+        retval = _warnings;
+    });
+    return retval;
+#else
+    return _warnings;
+#endif
+}
+
+void Diagnostics::clearWarnings()
+{
+#if BUILDING_CACHE_BUILDER
+    dispatch_sync(sWarningQueue, ^{
+        _warnings.clear();
+    });
+#else
+    _warnings.clear();
+#endif
+}
+
+#endif
+
diff --git a/dyld3/Diagnostics.h b/dyld3/Diagnostics.h
new file mode 100644 (file)
index 0000000..81fcf82
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2017 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 Diagnostics_h
+#define Diagnostics_h
+
+#include <stdint.h>
+
+#if !DYLD_IN_PROCESS
+#include <set>
+#include <string>
+#include <vector>
+#include <dispatch/dispatch.h>
+#endif
+
+#include "Logging.h"
+
+
+class VIS_HIDDEN Diagnostics
+{
+public:
+                    Diagnostics(bool verbose=false);
+                    ~Diagnostics();
+
+    void            error(const char* format, ...)  __attribute__((format(printf, 2, 3)));
+#if !DYLD_IN_PROCESS
+                    Diagnostics(const std::string& prefix, bool verbose=false);
+
+    void            warning(const char* format, ...)  __attribute__((format(printf, 2, 3)));
+    void            verbose(const char* format, ...)  __attribute__((format(printf, 2, 3)));
+    void            copy(const Diagnostics&);
+#endif
+
+    bool            hasError() const;
+    bool            noError() const;
+    void            clearError();
+    void            assertNoError() const;
+
+#if DYLD_IN_PROCESS
+    const char*                     errorMessage() const;
+#else
+    const std::string               prefix() const;
+    std::string                     errorMessage() const;
+    const std::set<std::string>     warnings() const;
+    void                            clearWarnings();
+#endif
+
+private:
+    void*                    _buffer = nullptr;
+#if !DYLD_IN_PROCESS
+    std::string              _prefix;
+    std::set<std::string>    _warnings;
+    bool                     _verbose = false;
+#endif
+};
+
+
+
+
+#endif // Diagnostics_h
diff --git a/dyld3/DyldCacheParser.cpp b/dyld3/DyldCacheParser.cpp
new file mode 100644 (file)
index 0000000..4547027
--- /dev/null
@@ -0,0 +1,181 @@
+/*
+ * Copyright (c) 2017 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 <stdint.h>
+#include <string.h>
+#include <assert.h>
+#include <uuid/uuid.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/uio.h>
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#include <sys/resource.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+
+
+#include "DyldCacheParser.h"
+#include "Trie.hpp"
+
+
+namespace dyld3 {
+
+DyldCacheParser::DyldCacheParser(const DyldSharedCache* cacheHeader, bool rawFile)
+{
+    _data = (long)cacheHeader;
+    if ( rawFile )
+        _data |= 1;
+}
+
+const dyld_cache_header* DyldCacheParser::header() const
+{
+    return (dyld_cache_header*)(_data & -2);
+}
+
+const DyldSharedCache* DyldCacheParser::cacheHeader() const
+{
+    return (DyldSharedCache*)header();
+}
+
+bool DyldCacheParser::cacheIsMappedRaw() const
+{
+    return (_data & 1);
+}
+
+
+uint64_t DyldCacheParser::dataRegionRuntimeVmOffset() const
+{
+    const dyld_cache_header* cacheHeader = header();
+    const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((char*)cacheHeader + cacheHeader->mappingOffset);
+    return (mappings[1].address - mappings[0].address);
+}
+
+const dyld3::launch_cache::binary_format::ImageGroup* DyldCacheParser::cachedDylibsGroup() const
+{
+    const dyld_cache_header* cacheHeader = header();
+    const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((char*)cacheHeader + cacheHeader->mappingOffset);
+
+    if ( cacheIsMappedRaw() ) {
+        // Whole file is mapped read-only. Use mapping file-offsets to find ImageGroup
+        uint64_t offsetInLinkEditRegion = (cacheHeader->dylibsImageGroupAddr - mappings[2].address);
+        return (dyld3::launch_cache::binary_format::ImageGroup*)((uint8_t*)cacheHeader + mappings[2].fileOffset + offsetInLinkEditRegion);
+    }
+    else {
+        // Cache file is mapped in three non-contiguous ranges.  Use mapping addresses to find ImageGroup
+        return (dyld3::launch_cache::binary_format::ImageGroup*)((uint8_t*)cacheHeader + (cacheHeader->dylibsImageGroupAddr - mappings[0].address));
+    }
+}
+
+
+const dyld3::launch_cache::binary_format::ImageGroup* DyldCacheParser::otherDylibsGroup() const
+{
+    const dyld_cache_header* cacheHeader = header();
+    const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((char*)cacheHeader + cacheHeader->mappingOffset);
+
+    if ( cacheIsMappedRaw() ) {
+        // Whole file is mapped read-only. Use mapping file-offsets to find ImageGroup
+        uint64_t offsetInLinkEditRegion = (cacheHeader->otherImageGroupAddr - mappings[2].address);
+        return (dyld3::launch_cache::binary_format::ImageGroup*)((uint8_t*)cacheHeader + mappings[2].fileOffset + offsetInLinkEditRegion);
+    }
+    else {
+        // Cache file is mapped in three non-contiguous ranges.  Use mapping addresses to find ImageGroup
+        return (dyld3::launch_cache::binary_format::ImageGroup*)((uint8_t*)cacheHeader + (cacheHeader->otherImageGroupAddr - mappings[0].address));
+    }
+}
+
+const dyld3::launch_cache::binary_format::Closure* DyldCacheParser::findClosure(const char* path) const
+{
+    const dyld_cache_header* cacheHeader = header();
+    const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((char*)cacheHeader + cacheHeader->mappingOffset);
+
+    const uint8_t* executableTrieStart   = nullptr;
+    const uint8_t* executableTrieEnd     = nullptr;
+    const uint8_t* closuresStart         = nullptr;
+
+    if ( cacheIsMappedRaw() ) {
+        // Whole file is mapped read-only. Use mapping file-offsets to find trie and closures
+        executableTrieStart   = (uint8_t*)cacheHeader + cacheHeader->progClosuresTrieAddr - mappings[2].address + mappings[2].fileOffset;
+        executableTrieEnd     = executableTrieStart + cacheHeader->progClosuresTrieSize;
+        closuresStart         = (uint8_t*)cacheHeader + cacheHeader->progClosuresAddr - mappings[2].address + mappings[2].fileOffset;
+    }
+    else {
+        // Cache file is mapped in three non-contiguous ranges.  Use mapping addresses to find trie and closures
+        uintptr_t slide       = (uintptr_t)cacheHeader - (uintptr_t)(mappings[0].address);
+        executableTrieStart   = (uint8_t*)(cacheHeader->progClosuresTrieAddr + slide);
+        executableTrieEnd     = executableTrieStart + cacheHeader->progClosuresTrieSize;
+        closuresStart         = (uint8_t*)(cacheHeader->progClosuresAddr + slide);
+    }
+    Diagnostics diag;
+    const uint8_t* imageNode = dyld3::MachOParser::trieWalk(diag, executableTrieStart, executableTrieEnd, path);
+    if ( imageNode != NULL ) {
+        uint32_t closureOffset = (uint32_t)dyld3::MachOParser::read_uleb128(diag, imageNode, executableTrieEnd);
+        return (const dyld3::launch_cache::BinaryClosureData*)((uint8_t*)closuresStart + closureOffset);
+    }
+    return nullptr;
+}
+
+
+#if !DYLD_IN_PROCESS
+void DyldCacheParser::forEachClosure(void (^handler)(const char* runtimePath, const dyld3::launch_cache::binary_format::Closure* cls)) const
+{
+    const dyld_cache_header* cacheHeader = header();
+    const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((char*)cacheHeader + cacheHeader->mappingOffset);
+
+    const uint8_t* executableTrieStart   = nullptr;
+    const uint8_t* executableTrieEnd     = nullptr;
+    const uint8_t* closuresStart         = nullptr;
+
+    if ( cacheIsMappedRaw() ) {
+        // Whole file is mapped read-only. Use mapping file-offsets to find trie and closures
+        executableTrieStart   = (uint8_t*)cacheHeader + cacheHeader->progClosuresTrieAddr - mappings[2].address + mappings[2].fileOffset;
+        executableTrieEnd     = executableTrieStart + cacheHeader->progClosuresTrieSize;
+        closuresStart         = (uint8_t*)cacheHeader + cacheHeader->progClosuresAddr - mappings[2].address + mappings[2].fileOffset;
+    }
+    else {
+        // Cache file is mapped in three non-contiguous ranges.  Use mapping addresses to find trie and closures
+        uintptr_t slide       = (uintptr_t)cacheHeader - (uintptr_t)(mappings[0].address);
+        executableTrieStart   = (uint8_t*)(cacheHeader->progClosuresTrieAddr + slide);
+        executableTrieEnd     = executableTrieStart + cacheHeader->progClosuresTrieSize;
+        closuresStart         = (uint8_t*)(cacheHeader->progClosuresAddr + slide);
+    }
+
+    std::vector<DylibIndexTrie::Entry> closureEntries;
+    if ( Trie<DylibIndex>::parseTrie(executableTrieStart, executableTrieEnd, closureEntries) ) {
+        for (DylibIndexTrie::Entry& entry : closureEntries ) {
+            uint32_t offset = entry.info.index;
+            if ( offset < cacheHeader->progClosuresSize )
+                handler(entry.name.c_str(), (const dyld3::launch_cache::binary_format::Closure*)(closuresStart+offset));
+        }
+    }
+}
+#endif
+
+
+
+
+} // namespace dyld3
+
diff --git a/dyld3/DyldCacheParser.h b/dyld3/DyldCacheParser.h
new file mode 100644 (file)
index 0000000..34deee4
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2017 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 DyldCacheParser_h
+#define DyldCacheParser_h
+
+#include <stdint.h>
+#include <uuid/uuid.h>
+#include <mach-o/loader.h>
+
+#include "Diagnostics.h"
+#include "DyldSharedCache.h"
+#include "LaunchCacheFormat.h"
+
+namespace dyld3 {
+
+class VIS_HIDDEN DyldCacheParser
+{
+public:
+#if !DYLD_IN_PROCESS
+    static bool isValidDyldCache(Diagnostics& diag, const std::string& archName, Platform platform, const void* fileContent, size_t fileLength, const std::string& pathOpened, bool ignoreMainExecutables);
+#endif
+
+                            DyldCacheParser(const DyldSharedCache* cacheHeader, bool rawFile);
+    const DyldSharedCache*  cacheHeader() const;
+    bool                    cacheIsMappedRaw() const;
+
+
+
+    //
+    // Get ImageGroup for cached dylibs built into this cache files
+    //
+    const dyld3::launch_cache::binary_format::ImageGroup*   cachedDylibsGroup() const;
+
+
+    //
+    // Get ImageGroup for other OS dylibs and bundles built into this cache files
+    //
+    const dyld3::launch_cache::binary_format::ImageGroup*   otherDylibsGroup() const;
+
+
+    //
+    // returns closure for given path, or nullptr if no closure found
+    //
+    const dyld3::launch_cache::binary_format::Closure*      findClosure(const char* path) const;
+
+    //
+    // returns what vmOffset of data (r/w) region from cache header will be when cache is used in a process
+    //
+    uint64_t                                                dataRegionRuntimeVmOffset() const;
+
+#if !DYLD_IN_PROCESS
+    //
+    // Iterates over closure of OS programs built into shared cache
+    //
+    void forEachClosure(void (^handler)(const char* runtimePath, const dyld3::launch_cache::binary_format::Closure*)) const;
+#endif
+
+private:
+    const dyld_cache_header*    header() const;
+
+    long                        _data;  // low bit means rawFile
+};
+
+} // namespace dyld3
+
+#endif // DyldCacheParser_h
diff --git a/dyld3/LaunchCache.h b/dyld3/LaunchCache.h
new file mode 100644 (file)
index 0000000..447a2c7
--- /dev/null
@@ -0,0 +1,382 @@
+/*
+ * Copyright (c) 2017 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 LaunchCache_h
+#define LaunchCache_h
+
+
+#include <stdint.h>
+#include <stdio.h>
+#include <assert.h>
+#include <uuid/uuid.h>
+#include <mach/mach.h>
+#include <mach-o/loader.h>
+
+#if !DYLD_IN_PROCESS
+  #include <vector>
+  #include <unordered_set>
+  #include <string>
+  #include "shared-cache/DyldSharedCache.h"
+#endif
+
+#include "Diagnostics.h"
+
+#define VIS_HIDDEN __attribute__((visibility("hidden")))
+
+
+namespace dyld3 {
+
+class DyldCacheParser;
+
+namespace launch_cache {
+
+
+namespace binary_format {
+    struct Image;
+    struct ImageGroup;
+    union  ImageRef;
+    struct Closure;
+    struct DiskImage;
+    struct CachedImage;
+    struct AllFixupsBySegment;
+    struct SegmentFixupsByPage;
+}
+
+typedef binary_format::Image                BinaryImageData;
+typedef binary_format::ImageGroup           BinaryImageGroupData;
+typedef binary_format::Closure              BinaryClosureData;
+
+
+struct VIS_HIDDEN MemoryRange
+{
+    bool        contains(const MemoryRange& other) const;
+    bool        intersects(const MemoryRange& other) const;
+
+    const void* address;
+    uint64_t    size;
+};
+
+
+class VIS_HIDDEN SlowLoadSet
+{
+public:
+            SlowLoadSet(const BinaryImageData** start, const BinaryImageData** end) : _start(start), _end(end), _current(start) { }
+    bool    contains(const BinaryImageData*);
+    bool    add(const BinaryImageData*);
+    void    forEach(void (^handler)(const BinaryImageData*));
+    void    forEach(void (^handler)(const BinaryImageData*, bool& stop));
+    long    count() const;
+private:
+    const BinaryImageData** const  _start;
+    const BinaryImageData** const  _end;
+    const BinaryImageData**        _current;
+};
+
+struct ImageGroup;
+
+
+template <typename T>
+class VIS_HIDDEN DynArray
+{
+public:
+                DynArray(uintptr_t count, T* storage) : _count(count), _elements(storage) { }
+#if !DYLD_IN_PROCESS
+                DynArray(const std::vector<T>& vec) : _count(vec.size()), _elements((T*)&vec[0]) { }
+#endif
+
+    T&          operator[](size_t idx)        { assert(idx < _count); return _elements[idx]; }
+    const T&    operator[](size_t idx) const  { assert(idx < _count); return _elements[idx]; }
+    uintptr_t   count() const                      { return _count; }
+private:
+    uintptr_t  _count;
+    T*         _elements;
+};
+
+
+//  STACK_ALLOC_DYNARRAY(foo, 10, myarray);
+#define STACK_ALLOC_DYNARRAY(_type, _count, _name)  \
+    uintptr_t __##_name##_array_alloc[1 + ((sizeof(_type)*(_count))/sizeof(uintptr_t))]; \
+    dyld3::launch_cache::DynArray<_type> _name(_count, (_type*)__##_name##_array_alloc);
+
+
+typedef DynArray<const BinaryImageGroupData*> ImageGroupList;
+
+
+// In the pre-computed fixups for an Image, each fixup location is set to a TargetSymbolValue
+// which is an abstract encoding of a resolved symbol in an image that can be turned into a
+// real address once all ASLR slides are known.
+struct VIS_HIDDEN TargetSymbolValue
+{
+#if DYLD_IN_PROCESS
+    class LoadedImages
+    {
+    public:
+        virtual const uint8_t*      dyldCacheLoadAddressForImage() = 0;
+        virtual const mach_header*  loadAddressFromGroupAndIndex(uint32_t groupNum, uint32_t indexInGroup) = 0;
+        virtual void                forEachImage(void (^handler)(uint32_t anIndex, const BinaryImageData*, const mach_header*, bool& stop)) = 0;
+        virtual void                setAsNeverUnload(uint32_t anIndex) = 0;
+    };
+
+    uintptr_t                resolveTarget(Diagnostics& diag, const ImageGroup& inGroup, LoadedImages& images) const;
+#else
+    static TargetSymbolValue makeInvalid();
+    static TargetSymbolValue makeAbsolute(uint64_t value);
+    static TargetSymbolValue makeSharedCacheOffset(uint32_t offset);
+    static TargetSymbolValue makeGroupValue(uint32_t groupIndex, uint32_t imageIndexInGroup, uint64_t offsetInImage, bool isIndirectGroupNum);
+    static TargetSymbolValue makeDynamicGroupValue(uint32_t imagePathPoolOffset, uint32_t imageSymbolPoolOffset, bool weakImport);
+    std::string              asString(ImageGroup group) const;
+    bool                     operator==(const TargetSymbolValue& other) const { return (_data.raw == other._data.raw); }
+    bool                     isSharedCacheTarget(uint64_t& offsetInCache) const;
+    bool                     isGroupImageTarget(uint32_t& groupNum, uint32_t& indexInGroup, uint64_t& offsetInImage) const;
+    bool                     isInvalid() const;
+#endif
+private:
+                             TargetSymbolValue();
+    
+    enum Kinds { kindSharedCache, kindAbsolute, kindGroup, kindDynamicGroup };
+
+
+    struct SharedCacheOffsetTarget {
+        uint64_t    kind            :  2,       // kindSharedCache
+                    offsetIntoCache : 62;
+    };
+    struct AbsoluteTarget {
+        uint64_t    kind            :  2,       // kindAbsolute
+                    value           : 62;
+    };
+    struct GroupImageTarget {
+        uint64_t    kind            :  2,       // kindGroup
+                    isIndirectGroup :  1,       // 0 => use groupNum directly.  1 => index indirect side table
+                    groupNum        :  7,       // 0 not used, 1 => other dylibs, 2 => main closure, 3 => first dlopen group
+                    indexInGroup    : 12,
+                    offsetInImage   : 42;
+    };
+    struct DynamicGroupImageTarget {
+        uint64_t    kind            :  2,       // kindDynamicGroup
+                    weakImport      :  1,
+                    imagePathOffset : 30,
+                    symbolNameOffset: 31;
+    };
+    union {
+        SharedCacheOffsetTarget sharedCache;
+        AbsoluteTarget          absolute;
+        GroupImageTarget        group;
+        DynamicGroupImageTarget dynamicGroup;
+        uint64_t                raw;
+    } _data;
+
+    static_assert(sizeof(_data) == 8, "Overflow in size of TargetSymbolValue");
+};
+
+
+struct VIS_HIDDEN Image
+{
+    enum class LinkKind { regular=0, weak=1, upward=2, reExport=3 };
+    enum class FixupKind { rebase32, rebase64, bind32, bind64, rebaseText32, bindText32, bindTextRel32, bindImportJmp32 };
+
+                                        Image(const BinaryImageData* binaryData) : _binaryData(binaryData) { }
+
+    bool                                valid() const { return (_binaryData != nullptr); }
+    const BinaryImageData*              binaryData() const { return _binaryData; }
+    const ImageGroup                    group() const;
+    uint32_t                            maxLoadCount() const;
+    const char*                         path() const;
+    const char*                         leafName() const;
+    uint32_t                            pathHash() const;
+    const uuid_t*                       uuid() const;
+    bool                                isInvalid() const;
+    bool                                hasObjC() const;
+    bool                                isBundle() const;
+    bool                                hasWeakDefs() const;
+    bool                                mayHavePlusLoads() const;
+    bool                                hasTextRelocs() const;
+    bool                                neverUnload() const;
+    bool                                cwdMustBeThisDir() const;
+    bool                                isPlatformBinary() const;
+    bool                                overridableDylib() const;
+    bool                                validateUsingModTimeAndInode() const;
+    bool                                validateUsingCdHash() const;
+    uint64_t                            fileModTime() const;
+    uint64_t                            fileINode() const;
+    const uint8_t*                      cdHash16() const;
+    void                                forEachDependentImage(const ImageGroupList& groups, void (^handler)(uint32_t depIndex, Image depImage, LinkKind kind, bool& stop)) const;
+#if !DYLD_IN_PROCESS
+    bool                                recurseAllDependentImages(const ImageGroupList& groups, std::unordered_set<const BinaryImageData*>& allDependents) const;
+#endif
+    bool                                recurseAllDependentImages(const ImageGroupList& groups, SlowLoadSet& allDependents,
+                                                                  void (^handler)(const dyld3::launch_cache::binary_format::Image* aBinImage, bool& stop)) const;
+    bool                                containsAddress(const void* addr, const void* imageLoadAddress, uint8_t* permissions) const;
+    bool                                segmentHasFixups(uint32_t segIndex) const;
+    void                                forEachInitializer(const void* imageLoadAddress, void (^handler)(const void* initializer)) const;
+    void                                forEachInitBefore(const ImageGroupList& groups, void (^handler)(Image imageToInit)) const;
+    void                                forEachInitBefore(void (^handler)(binary_format::ImageRef imageToInit)) const;
+    void                                forEachDOF(const void* imageLoadAddress, void (^handler)(const void* initializer)) const;
+
+    bool                                isDiskImage() const;
+
+    // the following are only valid if isDiskImage() returns false
+    const binary_format::CachedImage*   asCachedImage() const;
+    uint32_t                            cacheOffset() const;
+    uint32_t                            patchStartIndex() const;
+    uint32_t                            patchCount() const;
+
+
+    // the following are only valid if isDiskImage() returns true
+    const binary_format::DiskImage*     asDiskImage() const;
+    uint64_t                            sliceOffsetInFile() const;
+    bool                                hasCodeSignature(uint32_t& fileOffset, uint32_t& size) const;
+    bool                                isFairPlayEncrypted(uint32_t& textOffset, uint32_t& size) const;
+    uint64_t                            vmSizeToMap() const;
+    void                                forEachDiskSegment(void (^handler)(uint32_t segIndex, uint32_t fileOffset, uint32_t fileSize, int64_t vmOffset, uint64_t vmSize, uint8_t permissions, bool& stop)) const;
+    void                                forEachCacheSegment(void (^handler)(uint32_t segIndex, uint64_t vmOffset, uint64_t vmSize, uint8_t permissions, bool& stop)) const;
+    void                                forEachFixup(uint32_t segIndex, MemoryRange segContent,
+                                                     void (^handler)(uint64_t segOffset, FixupKind kind, TargetSymbolValue value, bool& stop)) const;
+
+#if !DYLD_IN_PROCESS
+    void                                 printAsJSON(const ImageGroupList& groupList, bool printFixups=false, bool printDependentsDetails=false, FILE* out=stdout) const;
+#endif
+
+// todo: fairPlayTextPages
+
+private:
+    friend struct ImageGroup;
+    friend struct Closure;
+
+    bool                                        recurseAllDependentImages(const ImageGroupList& groups, SlowLoadSet& allDependents, bool& stopped,
+                                                                          void (^handler)(const dyld3::launch_cache::binary_format::Image* aBinImage, bool& stop)) const;
+    uint32_t                                    pageSize() const;
+    const binary_format::SegmentFixupsByPage*   segmentFixups(uint32_t segIndex) const;
+    static void                                 forEachFixup(const uint8_t* pageFixups, const void* segContent, uint32_t& offset, uint32_t& ordinal,
+                                                             void (^handler)(uint32_t pageOffset, FixupKind kind, uint32_t targetOrdinal, bool& stop));
+    static Image                                resolveImageRef(const ImageGroupList& groups, binary_format::ImageRef ref, bool applyOverrides=true);
+
+
+    const BinaryImageData*              _binaryData;
+};
+
+
+struct VIS_HIDDEN ImageGroup
+{
+                                    ImageGroup(const BinaryImageGroupData* binaryData) : _binaryData(binaryData) { }
+
+    size_t                          size() const;
+    uint32_t                        imageCount() const;
+    uint32_t                        groupNum() const;
+    bool                            dylibsExpectedOnDisk() const;
+    const Image                     image(uint32_t index) const;
+    uint32_t                        indexInGroup(const BinaryImageData* image) const;
+    const BinaryImageData*          findImageByPath(const char* path, uint32_t& foundIndex) const;
+    const BinaryImageData*          findImageByCacheOffset(size_t cacheVmOffset, uint32_t& mhCacheOffset, uint8_t& foundPermissions) const;
+    const BinaryImageData*          imageBinary(uint32_t index) const;
+    binary_format::ImageRef         dependentPool(uint32_t index) const;
+    const BinaryImageGroupData*     binaryData() const { return _binaryData; }
+    const char*                     stringFromPool(uint32_t offset) const;
+    uint32_t                        indirectGroupNum(uint32_t index) const;
+    void                            forEachImageRefOverride(void (^handler)(binary_format::ImageRef standardDylibRef, binary_format::ImageRef overrideDyilbRef, bool& stop)) const;
+    void                            forEachImageRefOverride(const ImageGroupList& groupList, void (^handler)(Image standardDylib, Image overrideDyilb, bool& stop)) const;
+    void                            forEachAliasOf(uint32_t imageIndex, void (^handler)(const char* aliasPath, uint32_t aliasPathHash, bool& stop)) const;
+#if DYLD_IN_PROCESS
+    void                            forEachDyldCacheSymbolOverride(void (^handler)(uint32_t patchTableIndex, const BinaryImageData* image, uint32_t imageOffset, bool& stop)) const;
+    void                            forEachDyldCachePatchLocation(const void* dyldCacheLoadAddress, uint32_t patchTargetIndex,
+                                                                  void (^handler)(uintptr_t* locationToPatch, uintptr_t addend, bool& stop)) const;
+#else
+    void                            forEachDyldCacheSymbolOverride(void (^handler)(uint32_t patchTableIndex, uint32_t imageIndexInClosure, uint32_t imageOffset, bool& stop)) const;
+    void                            forEachDyldCachePatchLocation(const DyldCacheParser& cacheParser, void (^handler)(uint32_t targetCacheOffset, const std::vector<uint32_t>& usesPointersCacheOffsets, bool& stop)) const;
+    bool                            hasPatchTableIndex(uint32_t targetCacheOffset, uint32_t& index) const;
+#endif
+
+    static uint32_t                 hashFunction(const char* s);
+#if !DYLD_IN_PROCESS
+    void                            printAsJSON(const ImageGroupList& groupList, bool printFixups=false, bool printDependentsDetails=false, FILE* out=stdout) const;
+    void                            printStatistics(FILE* out=stderr) const;
+#endif
+
+private:
+    friend struct Image;
+
+    const char*                                 stringPool() const;
+    uint32_t                                    stringPoolSize() const;
+    const uint64_t*                             segmentPool(uint32_t index) const;
+    const binary_format::AllFixupsBySegment*    fixUps(uint32_t offset) const;
+    const TargetSymbolValue*                    targetValuesArray() const;
+    uint32_t                                    targetValuesCount() const;
+    uint32_t                                    initializersPoolCount() const;
+    const uint32_t*                             initializerOffsetsPool() const;
+    const uint32_t                              initializerOffsetsCount() const;
+    const binary_format::ImageRef*              intializerListPool() const;
+    const uint32_t                              intializerListPoolCount() const;
+    const uint32_t*                             dofOffsetsPool() const;
+    const uint32_t                              dofOffsetsCount() const;
+    const uint32_t*                             indirectGroupNumsPool() const;
+    const uint32_t                              indirectGroupNumsCount() const;
+    void                                        forEachDyldCachePatch(uint32_t patchTargetIndex, uint32_t cacheDataVmOffset,
+                                                                      void (^handler)(uint32_t targetCacheOffset, uint32_t usePointersCacheOffset, bool hasAddend, bool& stop)) const;
+
+    const BinaryImageGroupData*        _binaryData;
+};
+
+
+
+struct VIS_HIDDEN Closure
+{
+                                        Closure(const BinaryClosureData* binaryData);
+
+    size_t                              size() const;
+    const uuid_t*                       dyldCacheUUID() const;
+    const uint8_t*                      cdHash() const;
+    uint32_t                            initialImageCount() const;
+    uint32_t                            mainExecutableImageIndex() const;
+    uint32_t                            mainExecutableEntryOffset() const;
+    bool                                mainExecutableUsesCRT() const;
+    bool                                isRestricted() const;
+    bool                                usesLibraryValidation() const;
+    const BinaryImageData*              libSystem(const ImageGroupList& groups);
+    const BinaryImageData*              libDyld(const ImageGroupList& groups);
+    uint32_t                            libdyldVectorOffset() const;
+    const ImageGroup                    group() const;
+    const BinaryClosureData*            binaryData() const { return _binaryData; }
+    void                                forEachMustBeMissingFile(void (^handler)(const char* path, bool& stop)) const;
+    void                                forEachEnvVar(void (^handler)(const char* keyEqualValue, bool& stop)) const;
+
+#if !DYLD_IN_PROCESS
+    void                                printAsJSON(const ImageGroupList& groupList, bool printFixups=true, bool printDependentsDetails=false, FILE* out=stdout) const;
+    void                                printStatistics(FILE* out=stderr) const;
+#endif
+
+private:
+    const BinaryClosureData*            _binaryData;
+};
+
+
+
+
+
+
+} //  namespace launch_cache
+} //  namespace dyld3
+
+
+#endif // LaunchCache_h
+
+
diff --git a/dyld3/LaunchCacheFormat.h b/dyld3/LaunchCacheFormat.h
new file mode 100644 (file)
index 0000000..bea67ad
--- /dev/null
@@ -0,0 +1,346 @@
+/*
+ * Copyright (c) 2017 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 LaunchCacheFormat_h
+#define LaunchCacheFormat_h
+
+
+#include <stdint.h>
+#include <assert.h>
+#include <uuid/uuid.h>
+#include <mach/mach.h>
+
+#include "LaunchCache.h"
+
+
+namespace dyld3 {
+namespace launch_cache {
+namespace binary_format {
+
+
+// bump this number each time binary format changes
+enum  { kFormatVersion = 8 };
+
+union VIS_HIDDEN ImageRef {
+    ImageRef() : val(0xFFFFFFFF) { }
+                ImageRef(uint8_t kind, uint32_t groupNum, uint32_t indexInGroup) : _linkKind(kind), _groupNum(groupNum), _indexInGroup(indexInGroup) {
+                    assert(groupNum < (1 << 18));
+                    assert(indexInGroup < (1 << 12));
+                }
+    uint8_t     kind()  const { return _linkKind; }
+    uint32_t    groupNum() const { return _groupNum; }
+    uint32_t    indexInGroup() const { return _indexInGroup; }
+    uint16_t    value() const { return val; }
+    void        clearKind()  { _linkKind = 0; }
+    
+    bool operator==(const ImageRef& rhs) const {
+        return (val == rhs.val);
+    }
+    bool operator!=(const ImageRef& rhs) const {
+        return (val != rhs.val);
+    }
+    static ImageRef weakImportMissing();
+    static ImageRef makeEmptyImageRef() { return ImageRef(); }
+
+private:
+    ImageRef(uint32_t v) : val(v) { }
+
+    uint32_t     val;
+    struct {
+        uint32_t _linkKind       :   2,     // Image::LinkKind
+                 _groupNum       :  18,     // 0 => cached dylib group, 1 => other dylib group, 2 => main closure, etc
+                 _indexInGroup   :  12;     // max 64K images in group
+    };
+};
+
+
+
+
+// In disk based images, all segments are multiples of page size
+// This struct just tracks the size (disk and vm) of each segment.
+// This is compact for most every image which have contiguous segments.
+// If the image does not have contiguous segments (rare), an extra
+// DiskSegment is inserted with the paddingNotSeg bit set.
+struct DiskSegment
+{
+    uint64_t    filePageCount   : 30,
+                vmPageCount     : 30,
+                permissions     : 3,
+                paddingNotSeg   : 1;
+};
+
+
+// In cache DATA_DIRTY is not page aligned or sized
+// This struct allows segments with any alignment and up to 256MB in size
+struct DyldCacheSegment
+{
+    uint64_t    cacheOffset : 32,
+                size        : 28,
+                permissions : 4;
+};
+
+// When an Image is built on the device, the mtime and inode are recorded.
+// When built off device, the first 16 bytes of SHA1 of CodeDirectory is recorded.
+union FileInfo
+{
+    struct {
+        uint64_t mtime;
+        uint64_t inode;
+    } statInfo;
+    struct {
+        uint8_t  bytes[16];
+    } cdHash16;
+};
+
+struct Image
+{
+    uint32_t            isDiskImage      : 1,       // images are DiskImage - not Image
+                        isInvalid        : 1,       // an error occurred creating the info for this image
+                        has16KBpages     : 1,
+                        hasTextRelocs    : 1,
+                        hasObjC          : 1,
+                        mayHavePlusLoads : 1,
+                        isEncrypted      : 1,       // image is DSMOS or FairPlay encrypted
+                        hasWeakDefs      : 1,
+                        neverUnload      : 1,
+                        cwdSameAsThis    : 1,       // dylibs use file system relative paths, cwd must be main's dir
+                        isPlatformBinary : 1,       // part of OS - can be loaded into LV process
+                        isBundle         : 1,
+                        overridableDylib : 1,       // only applicable to group 0
+                        padding          : 7,
+                        maxLoadCount     : 12;
+    int32_t             groupOffset;                // back pointer to containing ImageGroup (from start of Image)
+    uint32_t            pathPoolOffset;
+    uint32_t            pathHash;
+    FileInfo            fileInfo;
+    uuid_t              uuid;
+    uint16_t            dependentsArrayStartIndex;
+    uint16_t            dependentsArrayCount;
+    uint16_t            segmentsArrayStartIndex;
+    uint16_t            segmentsArrayCount;
+    uint16_t            initBeforeArrayStartIndex;
+    uint16_t            initBeforeArrayCount;
+    uint16_t            initOffsetsArrayStartIndex;
+    uint16_t            initOffsetsArrayCount;
+    uint16_t            dofOffsetsArrayStartIndex;
+    uint16_t            dofOffsetsArrayCount;
+};
+
+// an image in the dyld shared cache
+struct CachedImage : public Image
+{
+    uint32_t            patchStartIndex;
+    uint32_t            patchCount;
+};
+
+// an image not in the dyld shared cache (loaded from disk at runtime)
+struct DiskImage : public Image
+{
+    uint32_t            totalVmPages;
+    uint32_t            sliceOffsetIn4K;
+    uint32_t            codeSignFileOffset;
+    uint32_t            codeSignFileSize;
+    uint32_t            fixupsPoolOffset      : 28,    // offset in ImageGroup's pool for AllFixupsBySegment
+                        fixupsPoolSegCount    : 4;     // count of segments in AllFixupsBySegment for this image
+    uint32_t            fairPlayTextPageCount : 28,
+                        fairPlayTextStartPage : 4;
+    uint32_t            targetsArrayStartIndex;         // index in ImageGroup's pool of OrdinalEntry
+    uint32_t            targetsArrayCount;
+};
+
+
+// if an Image has an alias (symlink to it), the Image does not record the alias, but the ImageGroup does
+struct AliasEntry
+{
+    uint32_t    aliasHash;
+    uint32_t    imageIndexInGroup;
+    uint32_t    aliasOffsetInStringPool;
+};
+
+// each DiskImage points to an array of these, one per segment with fixups
+struct AllFixupsBySegment
+{
+    uint32_t    segIndex    : 4,
+                offset      : 28;    // from start of AllFixupsBySegment to this seg's SegmentFixupsByPage
+};
+
+
+// This struct is suitable for passing into kernel when kernel supports fixups on page-in.
+struct SegmentFixupsByPage
+{
+    uint32_t    size;                // of this struct, including fixup opcodes
+    uint32_t    pageSize;            // 0x1000 or 0x4000
+    uint32_t    pageCount;
+    uint32_t    pageInfoOffsets[1];  // array size is pageCount
+    // each page info is a FixUpOpcode[]
+};
+
+enum class FixUpOpcode : uint8_t {
+      done            = 0x00,
+//    apply           = 0x10,
+      rebase32        = 0x10,    // add32 slide at current pageOffset, increment pageOffset by 4
+      rebase64        = 0x11,    // add64 slide at current pageOffset, increment pageOffset by 8
+      bind32          = 0x12,    // set 32-bit ordinal value at current pageOffset, increment pageOffset by 4
+      bind64          = 0x13,    // set 64-bit ordinal value at current pageOffset, increment pageOffset by 8
+      rebaseText32    = 0x14,    // add32 slide at current text pageOffset, increment pageOffset by 4
+      bindText32      = 0x15,    // set 32-bit ordinal value at current text pageOffset, increment pageOffset by 4
+      bindTextRel32   = 0x16,    // set delta to 32-bit ordinal value at current text pageOffset, increment pageOffset by 4 (i386 CALL to dylib)
+      bindImportJmp32 = 0x17,    // set delta to 32-bit ordinal value at current text pageOffset, increment pageOffset by 4 (i386 JMP to dylib)
+//    fixupChain64    = 0x18,    // current page offset is start of a chain of locations to fix up
+//    adjPageOffset   = 0x20,
+      setPageOffset   = 0x20,    // low 4-bits is amount to increment (1 to 15).  If zero, then add next ULEB (note: can set offset for unaligned pointer)
+      incPageOffset   = 0x30,    // low 4-bits *4 is amount to increment (4 to 60).  If zero, then add next ULEB * 4
+//    adjOrdinal      = 0x40,
+      setOrdinal      = 0x40,    // low 4-bits is ordinal (1-15).  If zero, then ordinal is next ULEB
+      incOrdinal      = 0x50,    // low 4-bits is ordinal inc amount (1-15).  If zero, then ordinal is next ULEB
+    repeat            = 0x60     // low 5-bits is how many next bytes to repeat.  next ULEB is repeat count
+};
+
+// If a closure uses DYLD_LIBRARY_PATH to override an OS dylib, there is an
+// ImageRefOverride entry to redirect uses of the OS dylib.
+struct ImageRefOverride
+{
+    ImageRef standardDylib;
+    ImageRef overrideDylib;
+};
+
+// If a closure interposes on, or has a dylib that overrides, something in the dyld shared cache,
+// then closure's ImageGroup contains an array of these
+struct DyldCacheOverride
+{
+    uint64_t    patchTableIndex     : 24,       // index into PatchTable array of group 0
+                imageIndex          : 8,        // index in this group (2) of what to replace with
+                imageOffset         : 32;       // offset within image to override something in cache
+};
+
+
+// The ImageGroup for the dyld shared cache dylibs contains and array of these
+// with one entry for each symbol in a cached dylib that is used by some other cached dylib.
+struct PatchTable
+{
+    uint32_t    targetCacheOffset;      // delta from the base address of the cache to the address of the symbol to patch
+    uint32_t    offsetsStartIndex;      // index into the PatchOffset array of first location to patch, last offset has low bit set
+};
+
+struct PatchOffset
+{
+    uint32_t    last             : 1,
+                hasAddend        : 1,
+                dataRegionOffset : 30;
+};
+
+struct ImageGroup
+{
+    uint32_t        imagesEntrySize         : 8,
+                    dylibsExpectedOnDisk    : 1,
+                    imageFileInfoIsCdHash   : 1,
+                    padding                 : 14;
+    uint32_t        groupNum;
+    uint32_t        imagesPoolCount;
+    uint32_t        imagesPoolOffset;           // offset to array of Image or DiskImage
+    uint32_t        imageAliasCount;
+    uint32_t        imageAliasOffset;           // offset to array of AliasEntry
+    uint32_t        segmentsPoolCount;
+    uint32_t        segmentsPoolOffset;         // offset to array of Segment or DyldCacheSegment
+    uint32_t        dependentsPoolCount;
+    uint32_t        dependentsPoolOffset;       // offset to array of ImageRef
+    uint32_t        intializerOffsetPoolCount;
+    uint32_t        intializerOffsetPoolOffset; // offset to array of uint32_t
+    uint32_t        intializerListPoolCount;
+    uint32_t        intializerListPoolOffset;   // offset to array of ImageRef
+    uint32_t        targetsPoolCount;
+    uint32_t        targetsOffset;              // offset to array of TargetSymbolValue
+    uint32_t        fixupsPoolSize;
+    uint32_t        fixupsOffset;               // offset to list of AllFixupsBySegment
+    uint32_t        cachePatchTableCount;
+    uint32_t        cachePatchTableOffset;      // offset to array of PatchTable (used only in group 0)
+    uint32_t        cachePatchOffsetsCount;
+    uint32_t        cachePatchOffsetsOffset;    // offset to array of PatchOffset cache offsets (used only in group 0)
+    uint32_t        symbolOverrideTableCount;
+    uint32_t        symbolOverrideTableOffset;  // offset to array of DyldCacheOverride (used only in group 2)
+    uint32_t        imageOverrideTableCount;
+    uint32_t        imageOverrideTableOffset;   // offset to array of ImageRefOverride (used only in group 2)
+    uint32_t        dofOffsetPoolCount;
+    uint32_t        dofOffsetPoolOffset;        // offset to array of uint32_t
+    uint32_t        indirectGroupNumPoolCount;
+    uint32_t        indirectGroupNumPoolOffset; // offset to array of uint32_t
+    uint32_t        stringsPoolSize;
+    uint32_t        stringsPoolOffset;
+    // Image array
+    // Alias array
+    // Segment array
+    // ImageRef array
+    // Initializer offsets array
+    // Initializer ImageRef array
+    // TargetSymbolValue array
+    // AllFixupsBySegment pool
+    // PatchTable array
+    // PatchOffset array
+    // DyldCacheOverride array
+    // ImageRefOverride array
+    // string pool
+    // DOF offsets array
+};
+
+
+struct Closure
+{
+    enum { magicV1 = 0x31646c6e };
+
+    uint32_t        magic;
+    uint32_t        usesCRT                  : 1,
+                    isRestricted             : 1,
+                    usesLibraryValidation    : 1,
+                    padding                  : 29;
+    uint32_t        missingFileComponentsOffset;    // offset to array of 16-bit string pool offset of path components
+    uint32_t        dyldEnvVarsOffset;
+    uint32_t        dyldEnvVarsCount;
+    uint32_t        stringPoolOffset;
+    uint32_t        stringPoolSize;
+    ImageRef        libSystemRef;
+    ImageRef        libDyldRef;
+    uint32_t        libdyldVectorOffset;
+    uint32_t        mainExecutableIndexInGroup;
+    uint32_t        mainExecutableEntryOffset;
+    uint32_t        initialImageCount;
+    uuid_t          dyldCacheUUID;                // all zero if this closure is embedded in a dyld cache
+    uint8_t         mainExecutableCdHash[20];     // or UUID if not code signed
+    ImageGroup      group;
+    // MissingFile array
+    // env vars array
+    // string pool
+};
+
+
+
+} // namespace binary_format
+
+} // namespace launch_cache
+} // namespace dyld
+
+
+#endif // LaunchCacheFormat_h
+
+
diff --git a/dyld3/LaunchCachePrinter.cpp b/dyld3/LaunchCachePrinter.cpp
new file mode 100644 (file)
index 0000000..f5efd16
--- /dev/null
@@ -0,0 +1,457 @@
+/*
+ * Copyright (c) 2017 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 <string.h>
+
+#include <string>
+#include <map>
+#include <vector>
+
+#include "LaunchCache.h"
+#include "LaunchCacheFormat.h"
+
+#if !DYLD_IN_PROCESS
+
+namespace dyld3 {
+namespace launch_cache {
+
+struct Node
+{
+    std::string                 value;
+    std::map<std::string, Node> map;
+    std::vector<Node>           array;
+};
+
+static std::string hex(uint64_t value) {
+    char buff[64];
+    sprintf(buff, "0x%llX", value);
+    return buff;
+}
+
+static std::string hex5(uint64_t value) {
+    char buff[64];
+    sprintf(buff, "0x%05llX", value);
+    return buff;
+}
+
+static std::string decimal(uint64_t value) {
+    char buff[64];
+    sprintf(buff, "%llu", value);
+    return buff;
+}
+
+static Node buildImageNode(const Image& image, const ImageGroupList& groupList, bool printFixups, bool printDependentsDetails)
+{
+    __block Node imageNode;
+
+    if ( image.isInvalid() )
+        return imageNode;
+
+    const ImageGroup group = image.group();
+    imageNode.map["path"].value = image.path();
+    __block Node imageAliases;
+    group.forEachAliasOf(group.indexInGroup(image.binaryData()), ^(const char* aliasPath, uint32_t aliasPathHash, bool& stop) {
+        Node anAlias;
+        anAlias.value = aliasPath;
+        imageAliases.array.push_back(anAlias);
+    });
+    if ( !imageAliases.array.empty() )
+        imageNode.map["aliases"] = imageAliases;
+    uuid_string_t uuidStr;
+    uuid_unparse(*image.uuid(), uuidStr);
+    imageNode.map["uuid"].value = uuidStr;
+    imageNode.map["has-objc"].value = (image.hasObjC() ? "true" : "false");
+    imageNode.map["has-weak-defs"].value = (image.hasWeakDefs() ? "true" : "false");
+    imageNode.map["never-unload"].value = (image.neverUnload() ? "true" : "false");
+    imageNode.map["platform-binary"].value = (image.isPlatformBinary() ? "true" : "false");
+    if ( group.groupNum() == 0 )
+        imageNode.map["overridable-dylib"].value = (image.overridableDylib() ? "true" : "false");
+    if ( image.cwdMustBeThisDir() )
+        imageNode.map["cwd-must-be-this-dir"].value = "true";
+    if ( image.isDiskImage() ) {
+        uint32_t csFileOffset;
+        uint32_t csSize;
+        if ( image.hasCodeSignature(csFileOffset, csSize) ) {
+            imageNode.map["code-sign-location"].map["offset"].value = hex(csFileOffset);
+            imageNode.map["code-sign-location"].map["size"].value = hex(csSize);
+        }
+        uint32_t fpTextOffset;
+        uint32_t fpSize;
+        if ( image.isFairPlayEncrypted(fpTextOffset, fpSize) ) {
+            imageNode.map["fairplay-encryption-location"].map["offset"].value = hex(fpTextOffset);
+            imageNode.map["fairplay-encryption-location"].map["size"].value = hex(fpSize);
+        }
+        if ( image.validateUsingModTimeAndInode() ) {
+            imageNode.map["file-mod-time"].value = hex(image.fileModTime());
+            imageNode.map["file-inode"].value = hex(image.fileINode());
+        }
+        else {
+            const uint8_t* cdHash = image.cdHash16();
+            std::string cdHashStr;
+            cdHashStr.reserve(32);
+            for (int j=0; j < 16; ++j) {
+                uint8_t byte = cdHash[j];
+                uint8_t nibbleL = byte & 0x0F;
+                uint8_t nibbleH = byte >> 4;
+                if ( nibbleH < 10 )
+                    cdHashStr += '0' + nibbleH;
+                else
+                    cdHashStr += 'a' + (nibbleH-10);
+                if ( nibbleL < 10 )
+                    cdHashStr += '0' + nibbleL;
+                else
+                    cdHashStr += 'a' + (nibbleL-10);
+            }
+            imageNode.map["file-cd-hash-16"].value = cdHashStr;
+        }
+        imageNode.map["total-vm-size"].value = hex(image.vmSizeToMap());
+        uint64_t sliceOffset = image.sliceOffsetInFile();
+        if ( sliceOffset != 0 )
+            imageNode.map["file-offset-of-slice"].value = hex(sliceOffset);
+        if ( image.hasTextRelocs() )
+            imageNode.map["has-text-relocs"].value = "true";
+        image.forEachDiskSegment(^(uint32_t segIndex, uint32_t fileOffset, uint32_t fileSize, int64_t vmOffset, uint64_t vmSize, uint8_t permissions, bool& stop) {
+            Node segInfoNode;
+            segInfoNode.map["file-offset"].value = hex(fileOffset);
+            segInfoNode.map["file-size"].value = hex(fileSize);
+            segInfoNode.map["vm-size"].value = hex(vmSize);
+            segInfoNode.map["permissions"].value = hex(permissions);
+            imageNode.map["mappings"].array.push_back(segInfoNode);
+        });
+        if ( printFixups ) {
+            image.forEachDiskSegment(^(uint32_t segIndex, uint32_t fileOffset, uint32_t fileSize, int64_t vmOffset, uint64_t vmSize, uint8_t permissions, bool &segStop) {
+                MemoryRange segContent = { nullptr, vmSize };
+                std::string segName = "segment-" + decimal(segIndex);
+                __block Node segmentFixupsNode;
+                image.forEachFixup(segIndex, segContent, ^(uint64_t segOffset, Image::FixupKind kind, TargetSymbolValue value, bool& stop) {
+                    switch ( kind ) {
+                    case Image::FixupKind::rebase32:
+                        segmentFixupsNode.map[segName].map[hex5(segOffset)].value = "32-bit rebase";
+                        break;
+                    case Image::FixupKind::rebase64:
+                        segmentFixupsNode.map[segName].map[hex5(segOffset)].value = "64-bit rebase";
+                        break;
+                    case Image::FixupKind::rebaseText32 :
+                        segmentFixupsNode.map[segName].map[hex5(segOffset)].value = "32-bit text rebase";
+                        break;
+                    case Image::FixupKind::bind32:
+                        segmentFixupsNode.map[segName].map[hex5(segOffset)].value = std::string("32-bit bind, target=") + value.asString(group);
+                        break;
+                    case Image::FixupKind::bind64:
+                        segmentFixupsNode.map[segName].map[hex5(segOffset)].value = std::string("64-bit bind, target=") + value.asString(group);
+                        break;
+                    case Image::FixupKind::bindText32 :
+                        segmentFixupsNode.map[segName].map[hex5(segOffset)].value = std::string("32-bit text abs bind, target=") + value.asString(group);
+                        break;
+                    case Image::FixupKind::bindTextRel32 :
+                        segmentFixupsNode.map[segName].map[hex5(segOffset)].value = std::string("32-bit text rel bind, target=") + value.asString(group);
+                        break;
+                    case Image::FixupKind::bindImportJmp32 :
+                        segmentFixupsNode.map[segName].map[hex5(segOffset)].value = std::string("32-bit IMPORT JMP rel bind, target=") + value.asString(group);
+                        break;
+                    }
+                });
+                if ( segmentFixupsNode.map[segName].map.size() != 0 ) {
+                    imageNode.map["fixups"].array.push_back(segmentFixupsNode);
+                }
+            });
+        }
+    }
+    else {
+        imageNode.map["patch-start-index"].value = decimal(image.patchStartIndex());
+        imageNode.map["patch-count"].value = decimal(image.patchCount());
+    }
+
+    // add dependents
+    image.forEachDependentImage(groupList, ^(uint32_t depIndex, Image depImage, Image::LinkKind kind, bool& stop) {
+        Node depMapNode;
+        depMapNode.map["path"].value = depImage.path();
+        if ( printDependentsDetails ) {
+            ImageGroup depGroup = depImage.group();
+            uint32_t indexInGroup = depGroup.indexInGroup(depImage.binaryData());
+            depMapNode.map["group-index"].value = decimal(depGroup.groupNum());
+            depMapNode.map["index-in-group"].value = decimal(indexInGroup);
+        }
+        switch ( kind ) {
+            case Image::LinkKind::regular:
+                depMapNode.map["link"].value = "regular";
+                break;
+            case Image::LinkKind::reExport:
+                depMapNode.map["link"].value = "re-export";
+                break;
+            case Image::LinkKind::upward:
+                depMapNode.map["link"].value = "upward";
+                break;
+            case Image::LinkKind::weak:
+                depMapNode.map["link"].value = "weak";
+                break;
+        }
+        imageNode.map["dependents"].array.push_back(depMapNode);
+    });
+    // add things to init before this image
+    __block Node initBeforeNode;
+    image.forEachInitBefore(groupList, ^(Image beforeImage) {
+        Node beforeNode;
+        beforeNode.value = beforeImage.path();
+        imageNode.map["initializer-order"].array.push_back(beforeNode);
+    });
+
+    // add initializers
+    image.forEachInitializer(nullptr, ^(const void* initializer) {
+        Node initNode;
+        initNode.value = hex((long)initializer);
+        imageNode.map["initializer-offsets"].array.push_back(initNode);
+    });
+
+    // add override info if relevant
+    group.forEachImageRefOverride(groupList, ^(Image standardDylib, Image overrideDylib, bool& stop) {
+        if ( overrideDylib.binaryData() == image.binaryData() ) {
+            imageNode.map["override-of-cached-dylib"].value = standardDylib.path();
+        }
+    });
+
+    // add dtrace info
+    image.forEachDOF(nullptr, ^(const void* section) {
+        Node initNode;
+        initNode.value = hex((long)section);
+        imageNode.map["dof-offsets"].array.push_back(initNode);
+    });
+
+    return imageNode;
+}
+
+
+static Node buildImageGroupNode(const ImageGroup& group, const ImageGroupList& groupList, bool printFixups, bool printDependentsDetails)
+{
+    Node images;
+    uint32_t imageCount = group.imageCount();
+    images.array.reserve(imageCount);
+    for (uint32_t i=0; i < imageCount; ++i) {
+         images.array.push_back(buildImageNode(group.image(i), groupList, printFixups, printDependentsDetails));
+    }
+    return images;
+}
+
+static Node buildClosureNode(const Closure& closure, const ImageGroupList& groupList, bool printFixups, bool printDependentsDetails)
+{
+    __block Node root;
+
+    // add env-vars if they exist
+    closure.forEachEnvVar(^(const char* keyEqualValue, bool& stop) {
+        const char* equ = strchr(keyEqualValue, '=');
+        if ( equ != nullptr ) {
+            char key[512];
+            strncpy(key, keyEqualValue, equ-keyEqualValue);
+            key[equ-keyEqualValue] = '\0';
+            root.map["env-vars"].map[key].value = equ+1;
+        }
+    });
+
+    // add missing files array if they exist
+    closure.forEachMustBeMissingFile(^(const char* path, bool& stop) {
+        Node fileNode;
+        fileNode.value = path;
+        root.map["must-be-missing-files"].array.push_back(fileNode);
+    });
+
+    const uint8_t* cdHash = closure.cdHash();
+    std::string cdHashStr;
+    cdHashStr.reserve(24);
+    for (int i=0; i < 20; ++i) {
+        uint8_t byte = cdHash[i];
+        uint8_t nibbleL = byte & 0x0F;
+        uint8_t nibbleH = byte >> 4;
+        if ( nibbleH < 10 )
+            cdHashStr += '0' + nibbleH;
+        else
+            cdHashStr += 'a' + (nibbleH-10);
+        if ( nibbleL < 10 )
+            cdHashStr += '0' + nibbleL;
+        else
+            cdHashStr += 'a' + (nibbleL-10);
+    }
+    if ( cdHashStr != "0000000000000000000000000000000000000000" )
+        root.map["cd-hash"].value = cdHashStr;
+
+    // add uuid of dyld cache this closure requires
+    closure.dyldCacheUUID();
+    uuid_string_t cacheUuidStr;
+    uuid_unparse(*closure.dyldCacheUUID(), cacheUuidStr);
+    root.map["dyld-cache-uuid"].value = cacheUuidStr;
+
+    // add top level images
+    Node& rootImages = root.map["root-images"];
+    uint32_t initImageCount = closure.mainExecutableImageIndex();
+    rootImages.array.resize(initImageCount+1);
+    for (uint32_t i=0; i <= initImageCount; ++i) {
+        const Image image = closure.group().image(i);
+        uuid_string_t uuidStr;
+        uuid_unparse(*image.uuid(), uuidStr);
+        rootImages.array[i].value = uuidStr;
+    }
+    root.map["initial-image-count"].value = decimal(closure.initialImageCount());
+
+    // add images
+    root.map["images"] = buildImageGroupNode(closure.group(), groupList, printFixups, printDependentsDetails);
+    root.map["group-num"].value = decimal(closure.group().groupNum());
+
+    if ( closure.mainExecutableUsesCRT() )
+        root.map["main-offset"].value = hex(closure.mainExecutableEntryOffset());
+    else
+        root.map["start-offset"].value = hex(closure.mainExecutableEntryOffset());
+
+    root.map["libdyld-entry-offset"].value = hex(closure.libdyldVectorOffset());
+
+    root.map["restricted"].value = (closure.isRestricted() ? "true" : "false");
+
+    root.map["library-validation"].value = (closure.usesLibraryValidation() ? "true" : "false");
+
+    __block Node cacheOverrides;
+    closure.group().forEachDyldCacheSymbolOverride(^(uint32_t patchTableIndex, uint32_t imageIndexInClosure, uint32_t imageOffset, bool& stop) {
+        Node patch;
+        patch.map["patch-index"].value = decimal(patchTableIndex);
+        patch.map["replacement"].value = "{closure[" + decimal(imageIndexInClosure) + "]+" + hex(imageOffset) + "}";
+        cacheOverrides.array.push_back(patch);
+    });
+    if ( !cacheOverrides.array.empty() )
+        root.map["dyld-cache-overrides"].array = cacheOverrides.array;
+
+    return root;
+}
+
+static void indentBy(uint32_t spaces, FILE* out) {
+    for (int i=0; i < spaces; ++i) {
+        fprintf(out, " ");
+    }
+}
+
+static void printJSON(const Node& node, uint32_t indent, FILE* out)
+{
+    if ( !node.map.empty() ) {
+        fprintf(out, "{");
+        bool needComma = false;
+        for (const auto& entry : node.map) {
+            if ( needComma )
+                fprintf(out, ",");
+            fprintf(out, "\n");
+            indentBy(indent+2, out);
+            fprintf(out, "\"%s\": ", entry.first.c_str());
+            printJSON(entry.second, indent+2, out);
+            needComma = true;
+        }
+        fprintf(out, "\n");
+        indentBy(indent, out);
+        fprintf(out, "}");
+    }
+    else if ( !node.array.empty() ) {
+        fprintf(out, "[");
+        bool needComma = false;
+        for (const auto& entry : node.array) {
+            if ( needComma )
+                fprintf(out, ",");
+            fprintf(out, "\n");
+            indentBy(indent+2, out);
+            printJSON(entry, indent+2, out);
+            needComma = true;
+        }
+        fprintf(out, "\n");
+        indentBy(indent, out);
+        fprintf(out, "]");
+    }
+    else {
+        fprintf(out, "\"%s\"", node.value.c_str());
+    }
+    if ( indent == 0 )
+        fprintf(out, "\n");
+}
+
+
+void Image::printAsJSON(const ImageGroupList& groupList, bool printFixups, bool printDependentsDetails, FILE* out) const
+{
+    Node image = buildImageNode(*this, groupList, printFixups, printDependentsDetails);
+    printJSON(image, 0, out);
+}
+
+void ImageGroup::printAsJSON(const ImageGroupList& groupList, bool printFixups, bool printDependentsDetails, FILE* out) const
+{
+    Node root;
+    root.map["images"] = buildImageGroupNode(*this, groupList, printFixups, printDependentsDetails);
+    root.map["group-num"].value = decimal(groupNum());
+    root.map["dylibs-expected-on-disk"].value = (dylibsExpectedOnDisk() ? "true" : "false");
+       printJSON(root, 0, out);
+}
+
+void ImageGroup::printStatistics(FILE* out) const
+{
+    __block uint32_t totalRebases = 0;
+    __block uint32_t totalBinds   = 0;
+    for (uint32_t i=0; i < imageCount(); ++i) {
+        Image img(image(i));
+        img.forEachDiskSegment(^(uint32_t segIndex, uint32_t fileOffset, uint32_t fileSize, int64_t vmOffset, uint64_t vmSize, uint8_t permissions, bool &segStop) {
+            MemoryRange segContent = { nullptr, vmSize };
+            img.forEachFixup(segIndex, segContent, ^(uint64_t segOffset, Image::FixupKind kind, TargetSymbolValue value, bool& stop) {
+                if ( kind == Image::FixupKind::rebase64 )
+                    ++totalRebases;
+                else
+                    ++totalBinds;
+            });
+        });
+    }
+
+    fprintf(out, "ImageGroup:\n");
+    fprintf(out, "  image-count:            % 5d\n", _binaryData->imagesPoolCount);
+    fprintf(out, "  alias-count:            % 5d\n", _binaryData->imageAliasCount);
+    fprintf(out, "  segments-count:         % 5d\n", _binaryData->segmentsPoolCount);
+    fprintf(out, "  dependents-count:       % 5d\n", _binaryData->dependentsPoolCount);
+    fprintf(out, "  targets-count:          % 5d\n", _binaryData->targetsPoolCount);
+    fprintf(out, "  rebase-count:           % 5d\n", totalRebases);
+    fprintf(out, "  bind-count:             % 5d\n", totalBinds);
+    fprintf(out, "  fixups-size:            % 8d bytes\n",  _binaryData->fixupsPoolSize);
+    fprintf(out, "  targets-size:           % 8ld bytes\n", _binaryData->targetsPoolCount * sizeof(uint64_t));
+    fprintf(out, "  strings-size:           % 8d bytes\n",  _binaryData->stringsPoolSize);
+    fprintf(out, "  dofs-size:              % 8ld bytes\n",  _binaryData->dofOffsetPoolCount * sizeof(uint32_t));
+    fprintf(out, "  indirect-groups-size:   % 8ld bytes\n",  _binaryData->indirectGroupNumPoolCount * sizeof(uint32_t));
+}
+
+
+void Closure::printAsJSON(const ImageGroupList& groupList, bool printFixups, bool printDependentsDetails, FILE* out) const
+{
+    Node root = buildClosureNode(*this, groupList, printFixups, printDependentsDetails);
+    printJSON(root, 0, out);
+}
+
+void Closure::printStatistics(FILE* out) const
+{
+    fprintf(out, "closure size: %lu\n", size());
+    group().printStatistics(out);
+}
+
+
+
+} // namespace launch_cache
+} // namespace dyld3
+
+#endif
+
+
diff --git a/dyld3/LaunchCacheReader.cpp b/dyld3/LaunchCacheReader.cpp
new file mode 100644 (file)
index 0000000..06c73e1
--- /dev/null
@@ -0,0 +1,1471 @@
+/*
+ * Copyright (c) 2017 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 <stdint.h>
+#include <assert.h>
+#include <uuid/uuid.h>
+#include <unistd.h>
+#include <limits.h>
+
+#include "LaunchCacheFormat.h"
+#include "LaunchCache.h"
+#include "MachOParser.h"
+#include "DyldCacheParser.h"
+
+namespace dyld {
+    extern void log(const char* format, ...)  __attribute__((format(printf, 1, 2)));
+}
+
+namespace dyld3 {
+namespace launch_cache {
+
+static uintptr_t read_uleb128(const uint8_t*& p, const uint8_t* end)
+{
+    uint64_t result = 0;
+    int         bit = 0;
+    do {
+        if (p == end) {
+            assert("malformed uleb128");
+            break;
+        }
+        uint64_t slice = *p & 0x7f;
+
+        if (bit > 63) {
+            assert("uleb128 too big for uint64");
+            break;
+        }
+        else {
+            result |= (slice << bit);
+            bit += 7;
+        }
+    } while (*p++ & 0x80);
+    return (uintptr_t)result;
+}
+
+
+bool MemoryRange::contains(const MemoryRange& other) const
+{
+    if ( this->address > other.address )
+        return false;
+    const uint8_t* thisEnd = (uint8_t*)address + size;
+    const uint8_t* otherEnd = (uint8_t*)other.address + other.size;
+    return (thisEnd >= otherEnd);
+}
+
+bool MemoryRange::intersects(const MemoryRange& other) const
+{
+    const uint8_t* thisEnd = (uint8_t*)address + size;
+    const uint8_t* otherEnd = (uint8_t*)other.address + other.size;
+    if ( otherEnd < this->address )
+        return false;
+    return ( other.address < thisEnd );
+}
+
+
+////////////////////////////  SlowLoadSet ////////////////////////////////////////
+
+bool SlowLoadSet::contains(const BinaryImageData* image)
+{
+    for (const BinaryImageData** p=_start; p < _current; ++p) {
+        if ( *p == image )
+            return true;
+    }
+    return false;
+}
+
+bool SlowLoadSet::add(const BinaryImageData* image)
+{
+    if ( _current < _end ) {
+        *_current++ = image;
+        return true;
+    }
+    return false;
+}
+
+void SlowLoadSet::forEach(void (^handler)(const BinaryImageData*))
+{
+    for (const BinaryImageData** p=_start; p < _current; ++p) {
+        handler(*p);
+    }
+}
+
+void SlowLoadSet::forEach(void (^handler)(const BinaryImageData*, bool& stop))
+{
+    bool stop = false;
+    for (const BinaryImageData** p=_start; p < _current; ++p) {
+        handler(*p, stop);
+        if ( stop )
+            break;
+    }
+}
+
+
+long SlowLoadSet::count() const
+{
+    return (_current - _start);
+}
+
+
+////////////////////////////  TargetSymbolValue ////////////////////////////////////////
+
+#if DYLD_IN_PROCESS
+
+uintptr_t TargetSymbolValue::resolveTarget(Diagnostics& diag, const ImageGroup& inGroup, LoadedImages& images) const
+{
+    // this block is only used if findExportedSymbol() needs to trace re-exported dylibs to find a symbol
+    MachOParser::DependentFinder reExportFollower = ^(uint32_t depIndex, const char* depLoadPath, void* extra, const mach_header** foundMH, void** foundExtra) {
+        *foundMH = nullptr;
+        images.forEachImage(^(uint32_t idx, const BinaryImageData* binImage, const mach_header* mh, bool& stop) {
+            Image anImage(binImage);
+            if ( strcmp(depLoadPath, anImage.path()) == 0 ) {
+                *foundMH = mh;
+                stop = true;
+            }
+        });
+        return (*foundMH != nullptr);
+    };
+
+    uintptr_t offset;
+    switch ( _data.sharedCache.kind ) {
+    
+        case TargetSymbolValue::kindSharedCache:
+            assert(_data.sharedCache.offsetIntoCache != 0);
+            return (uintptr_t)(images.dyldCacheLoadAddressForImage() + _data.sharedCache.offsetIntoCache);
+            
+        case TargetSymbolValue::kindAbsolute:
+            offset = (uintptr_t)_data.absolute.value;
+            // sign extend 42 bit value
+            if ( offset & 0x2000000000000000ULL )
+                offset |= 0xC000000000000000ULL;
+            return offset;
+            
+        case TargetSymbolValue::kindGroup: {
+            uint32_t groupNum = _data.group.isIndirectGroup ? inGroup.indirectGroupNum(_data.group.groupNum) : _data.group.groupNum;
+            uintptr_t targetImageLoadAddress = (uintptr_t)(images.loadAddressFromGroupAndIndex(groupNum, _data.group.indexInGroup));
+            if ( targetImageLoadAddress == 0 )
+                diag.error("image for groupNum=%d, indexInGroup=%d not found", groupNum, _data.group.indexInGroup);
+            offset = (uintptr_t)_data.group.offsetInImage;
+            // sign extend 42 bit offset
+            if ( offset & 0x0000020000000000ULL )
+                offset |= 0xFFFFFC0000000000ULL;
+            return targetImageLoadAddress + offset;
+         }
+
+        case TargetSymbolValue::kindDynamicGroup: {
+            const char* imagePath =  inGroup.stringFromPool(_data.dynamicGroup.imagePathOffset);
+            const char* symbolName = inGroup.stringFromPool(_data.dynamicGroup.symbolNameOffset);
+            __block uintptr_t result = 0;
+            __block bool      found  = false;
+            if ( strcmp(imagePath, "@flat") == 0 ) {
+                // search all images in load order
+                images.forEachImage(^(uint32_t idx, const BinaryImageData* binImage, const mach_header* mh, bool& stop) {
+                    Diagnostics findSymbolDiag;
+                    dyld3::MachOParser parser(mh);
+                    dyld3::MachOParser::FoundSymbol foundInfo;
+                    if ( parser.findExportedSymbol(findSymbolDiag, symbolName, nullptr, foundInfo, ^(uint32_t, const char* depLoadPath, void*, const mach_header** foundMH, void**) {
+                            // <rdar://problem/31921090> need to follow re-exported symbols to support libc renamed and reexported symbols
+                           *foundMH = nullptr;
+                            images.forEachImage(^(uint32_t innerIndex, const BinaryImageData* innerBinImage, const mach_header* innerMH, bool& innerStop) {
+                                Image innerImage(innerBinImage);
+                                if ( strcmp(depLoadPath, innerImage.path()) == 0 ) {
+                                    *foundMH = innerMH;
+                                    innerStop = true;
+                                }
+                            });
+                            return (*foundMH != nullptr);
+                        }) ) {
+                        result = ((uintptr_t)(foundInfo.foundInDylib) + (uintptr_t)foundInfo.value);
+                        images.setAsNeverUnload(idx);
+                        found  = true;
+                        stop   = true;
+                    }
+                });
+                // <rdar://problem/31944092> bind unfound flat symbols to NULL to support lazy binding semantics
+                if ( !found ) {
+                    result = 0;
+                    found = true;
+                }
+            }
+            else if ( strcmp(imagePath, "@main") == 0 ) {
+                // search only main executable
+                images.forEachImage(^(uint32_t idx, const BinaryImageData* binImage, const mach_header* mh, bool& stop) {
+                    if ( mh->filetype == MH_EXECUTE ) {
+                        Diagnostics findSymbolDiag;
+                        dyld3::MachOParser parser(mh);
+                        dyld3::MachOParser::FoundSymbol foundInfo;
+                        if ( parser.findExportedSymbol(findSymbolDiag, symbolName, nullptr, foundInfo, nullptr) ) {
+                            result = ((uintptr_t)(foundInfo.foundInDylib) + (uintptr_t)foundInfo.value);
+                            found  = true;
+                            stop   = true;
+                        }
+                    }
+                });
+            }
+            else if ( strcmp(imagePath, "@weak_def") == 0 ) {
+                // search images with weak definitions in load order
+                images.forEachImage(^(uint32_t idx, const BinaryImageData* binImage, const mach_header* mh, bool& stop) {
+                    Image anImage(binImage);
+                    if ( anImage.hasWeakDefs() ) {
+                        Diagnostics findSymbolDiag;
+                        dyld3::MachOParser parser(mh);
+                        dyld3::MachOParser::FoundSymbol foundInfo;
+                        if ( parser.findExportedSymbol(findSymbolDiag, symbolName, nullptr, foundInfo, nullptr) ) {
+                            result = ((uintptr_t)(foundInfo.foundInDylib) + (uintptr_t)foundInfo.value);
+                            found  = true;
+                            images.setAsNeverUnload(idx);
+                            stop   = true;
+                        }
+                    }
+                });
+            }
+            else {
+                // search only image the matches supplied path
+                images.forEachImage(^(uint32_t idx, const BinaryImageData* binImage, const mach_header* mh, bool& stop) {
+                    Image anImage(binImage);
+                    if ( strcmp(anImage.path(), imagePath) == 0 ) {
+                        Diagnostics findSymbolDiag;
+                        dyld3::MachOParser parser(mh);
+                        dyld3::MachOParser::FoundSymbol foundInfo;
+                        if ( parser.findExportedSymbol(findSymbolDiag, symbolName, nullptr, foundInfo, reExportFollower) ) {
+                            result = ((uintptr_t)(foundInfo.foundInDylib) + (uintptr_t)foundInfo.value);
+                            found  = true;
+                            stop = true;
+                        }
+                    }
+                });
+            }
+            if ( found )
+                return result;
+            if ( _data.dynamicGroup.weakImport )
+                return 0;
+            diag.error("dynamic symbol '%s' not found for %s", symbolName, imagePath);
+            return 0;
+        }
+    }
+    assert(0 && "resolveTarget() not reachable");
+}
+
+#else
+
+TargetSymbolValue::TargetSymbolValue()
+{
+    _data.raw = 0;
+}
+
+TargetSymbolValue TargetSymbolValue::makeInvalid()
+{
+    return TargetSymbolValue();
+}
+
+TargetSymbolValue TargetSymbolValue::makeSharedCacheOffset(uint32_t offset)
+{
+    TargetSymbolValue t;
+    t._data.sharedCache.kind                = kindSharedCache;
+    t._data.sharedCache.offsetIntoCache     = offset;
+    return t;
+}
+
+TargetSymbolValue TargetSymbolValue::makeAbsolute(uint64_t value)
+{
+    TargetSymbolValue t;
+    t._data.absolute.kind                   = kindAbsolute;
+    t._data.absolute.value                  = value;
+    return t;
+}
+
+TargetSymbolValue TargetSymbolValue::makeGroupValue(uint32_t groupIndex, uint32_t imageIndexInGroup, uint64_t offsetInImage, bool isIndirectGroupNum)
+{
+    assert(groupIndex != 0 || isIndirectGroupNum);
+    assert(groupIndex < 128);
+    assert(imageIndexInGroup < 4096);
+    TargetSymbolValue t;
+    t._data.group.kind                      = kindGroup;
+    t._data.group.isIndirectGroup           = isIndirectGroupNum;
+    t._data.group.groupNum                  = groupIndex;
+    t._data.group.indexInGroup              = imageIndexInGroup;
+    t._data.group.offsetInImage             = offsetInImage;
+    return t;
+}
+
+TargetSymbolValue TargetSymbolValue::makeDynamicGroupValue(uint32_t imagePathPoolOffset, uint32_t imageSymbolPoolOffset, bool weakImport)
+{
+    TargetSymbolValue t;
+    t._data.dynamicGroup.kind               = kindDynamicGroup;
+    t._data.dynamicGroup.weakImport         = weakImport;
+    t._data.dynamicGroup.imagePathOffset    = imagePathPoolOffset;
+    t._data.dynamicGroup.symbolNameOffset   = imageSymbolPoolOffset;
+    return t;
+}
+
+bool TargetSymbolValue::isSharedCacheTarget(uint64_t& offsetInCache) const
+{
+    if ( _data.sharedCache.kind != kindSharedCache )
+        return false;
+    offsetInCache = _data.sharedCache.offsetIntoCache;
+    return true;
+}
+
+bool TargetSymbolValue::isGroupImageTarget(uint32_t& groupNum, uint32_t& indexInGroup, uint64_t& offsetInImage) const
+{
+    if ( _data.sharedCache.kind != kindGroup )
+        return false;
+    // This is only used for interposing, so refuse to allow indirect for group 2
+    assert(!_data.group.isIndirectGroup);
+    groupNum      = _data.group.groupNum;
+    indexInGroup  = _data.group.indexInGroup;
+    offsetInImage = _data.group.offsetInImage;
+    return true;
+}
+
+bool TargetSymbolValue::isInvalid() const
+{
+    return (_data.raw == 0);
+}
+
+static std::string hex8(uint64_t value) {
+    char buff[64];
+    sprintf(buff, "0x%08llX", value);
+    return buff;
+}
+
+static std::string decimal(uint64_t value) {
+    char buff[64];
+    sprintf(buff, "%llu", value);
+    return buff;
+}
+
+std::string TargetSymbolValue::asString(ImageGroup group) const
+{
+    int64_t offset;
+    switch ( _data.sharedCache.kind ) {
+        case kindSharedCache:
+            if ( _data.sharedCache.offsetIntoCache == 0 )
+                return "{invalid target}";
+            else
+                return "{cache+" + hex8(_data.sharedCache.offsetIntoCache) + "}";
+        case kindAbsolute:
+            offset = (uintptr_t)_data.absolute.value;
+            // sign extend 42 bit value
+            if ( offset & 0x2000000000000000ULL )
+                offset |= 0xC000000000000000ULL;
+            return "{absolute:" + hex8(offset) + "}";
+        case kindGroup:
+            offset = _data.group.offsetInImage;
+            // sign extend 42 bit offset
+            if ( offset & 0x0000020000000000ULL )
+                offset |= 0xFFFFFC0000000000ULL;
+            if ( _data.group.groupNum == 1 )
+                return "{otherDylib[" + decimal(_data.group.indexInGroup) +"]+" + hex8(offset) + "}";
+            if ( _data.group.groupNum == 2 )
+                return "{closure[" + decimal(_data.group.indexInGroup) +"]+" + hex8(offset) + "}";
+            else {
+                uint32_t groupNum = _data.group.isIndirectGroup ? group.indirectGroupNum(_data.group.groupNum) : _data.group.groupNum;
+                return "{dlopen-group-" + decimal(groupNum-2) + "[" + decimal(_data.group.indexInGroup) +"]+" + hex8(offset) + "}";
+            }
+        case kindDynamicGroup:
+            return "{dynamic image='" + std::string(group.stringFromPool(_data.dynamicGroup.imagePathOffset))
+                 + "' symbol='" + std::string(group.stringFromPool(_data.dynamicGroup.symbolNameOffset)) + "'}";
+    }
+    assert(0 && "unreachable");
+    return "xx";
+}
+
+#endif
+
+////////////////////////////  ImageRef ////////////////////////////////////////
+
+binary_format::ImageRef binary_format::ImageRef::weakImportMissing()
+{
+    ImageRef missing(0xFFFFFFFF);
+    return missing;
+}
+
+
+////////////////////////////  Closure ////////////////////////////////////////
+
+Closure::Closure(const binary_format::Closure* closure)
+ : _binaryData(closure)
+{
+    assert(closure->magic == binary_format::Closure::magicV1);
+}
+
+size_t Closure::size() const
+{
+    return _binaryData->stringPoolOffset + _binaryData->stringPoolSize;
+}
+
+const ImageGroup Closure::group() const
+{
+    return ImageGroup(&_binaryData->group);
+}
+
+void Closure::forEachEnvVar(void (^handler)(const char* keyEqualValue, bool& stop)) const
+{
+    const uint32_t* envVarStringOffsets = (uint32_t*)((uint8_t*)_binaryData + _binaryData->dyldEnvVarsOffset);
+    const char*     stringPool          = (char*)_binaryData + _binaryData->stringPoolOffset;
+    bool            stop                = false;
+    for (uint32_t i=0; i < _binaryData->dyldEnvVarsCount; ++i) {
+        handler(&stringPool[envVarStringOffsets[i]], stop);
+        if ( stop )
+            break;
+    }
+}
+void Closure::forEachMustBeMissingFile(void (^handler)(const char* path, bool& stop)) const
+{
+    const uint16_t*     offsets     = (uint16_t*)((uint8_t*)_binaryData + _binaryData->missingFileComponentsOffset);
+    if ( *offsets == 0 )
+        return;
+    const char*         stringPool  = (char*)_binaryData + _binaryData->stringPoolOffset;
+    bool                stop        = false;
+    while ( !stop ) {
+        char path[PATH_MAX];
+        path[0] = '\0';
+        while ( *offsets != 0 ) {
+            const char* component = &stringPool[*offsets++];
+            strlcat(path, "/", PATH_MAX);
+            strlcat(path, component, PATH_MAX);
+        }
+        handler(path, stop);
+        ++offsets;  // move to next path
+        if ( *offsets == 0 )  // if no next path, then end of list of strings
+            stop = true;
+    }
+}
+
+const uuid_t* Closure::dyldCacheUUID() const
+{
+    return &(_binaryData->dyldCacheUUID);
+}
+
+
+const uint8_t* Closure::cdHash() const
+{
+    return _binaryData->mainExecutableCdHash;
+}
+
+
+uint32_t Closure::initialImageCount() const
+{
+    return _binaryData->initialImageCount;
+}
+
+
+uint32_t Closure::mainExecutableImageIndex() const
+{
+    return _binaryData->mainExecutableIndexInGroup;
+}
+
+
+uint32_t Closure::mainExecutableEntryOffset() const
+{
+    return _binaryData->mainExecutableEntryOffset;
+}
+
+bool Closure::mainExecutableUsesCRT() const
+{
+    return _binaryData->usesCRT;
+}
+
+bool Closure::isRestricted() const
+{
+    return _binaryData->isRestricted;
+}
+
+bool Closure::usesLibraryValidation() const
+{
+    return _binaryData->usesLibraryValidation;
+}
+
+uint32_t Closure::libdyldVectorOffset() const
+{
+    return _binaryData->libdyldVectorOffset;
+}
+
+const BinaryImageData* Closure::libSystem(const ImageGroupList& groups)
+{
+    return Image::resolveImageRef(groups, _binaryData->libSystemRef).binaryData();
+}
+
+const BinaryImageData* Closure::libDyld(const ImageGroupList& groups)
+{
+    return Image::resolveImageRef(groups, _binaryData->libDyldRef).binaryData();
+}
+
+
+////////////////////////////  ImageGroup ////////////////////////////////////////
+
+size_t ImageGroup::size() const
+{
+    return (_binaryData->stringsPoolOffset + _binaryData->stringsPoolSize + 3) & (-4);
+}
+
+uint32_t ImageGroup::groupNum() const
+{
+    return _binaryData->groupNum;
+}
+
+bool ImageGroup::dylibsExpectedOnDisk() const
+{
+    return _binaryData->dylibsExpectedOnDisk;
+}
+
+uint32_t ImageGroup::imageCount() const
+{
+    return _binaryData->imagesPoolCount;
+}
+
+const binary_format::Image* ImageGroup::imageBinary(uint32_t index) const
+{
+    assert(index <_binaryData->imagesPoolCount);
+    return (binary_format::Image*)((char*)_binaryData + _binaryData->imagesPoolOffset + (index * _binaryData->imagesEntrySize));
+}
+
+
+const Image ImageGroup::image(uint32_t index) const
+{
+    return Image(imageBinary(index));
+}
+
+uint32_t ImageGroup::indexInGroup(const binary_format::Image* img) const
+{
+    long delta = (char*)img - ((char*)_binaryData + _binaryData->imagesPoolOffset);
+    uint32_t index = (uint32_t)(delta  /_binaryData->imagesEntrySize);
+    assert(image(index)._binaryData == img);
+    return index;
+}
+
+const binary_format::Image* ImageGroup::findImageByPath(const char* path, uint32_t& foundIndex) const
+{
+    // check path of each image in group
+    uint32_t targetHash = hashFunction(path);
+    const uint8_t* p = (uint8_t*)_binaryData + _binaryData->imagesPoolOffset;
+    for (uint32_t i=0; i < _binaryData->imagesPoolCount; ++i) {
+        const binary_format::Image* binImage = (binary_format::Image*)p;
+        if ( binImage->pathHash == targetHash ) {
+            Image img(binImage);
+            if ( !img.isInvalid() && (strcmp(img.path(), path) == 0) ) {
+                foundIndex = i;
+                return binImage;
+            }
+        }
+        p += _binaryData->imagesEntrySize;
+    }
+    // check each alias
+    const binary_format::AliasEntry* aliasEntries =  (binary_format::AliasEntry*)((uint8_t*)_binaryData + _binaryData->imageAliasOffset);
+    for (uint32_t i=0; i < _binaryData->imageAliasCount; ++i) {
+        const char* aliasPath = stringFromPool(aliasEntries[i].aliasOffsetInStringPool);
+        if ( aliasEntries[i].aliasHash == targetHash ) {
+            if ( strcmp(aliasPath, path) == 0 ) {
+                Image img = image(aliasEntries[i].imageIndexInGroup);
+                if ( !img.isInvalid() ) {
+                    foundIndex = aliasEntries[i].imageIndexInGroup;
+                    return img.binaryData();
+                }
+            }
+        }
+    }
+    return nullptr;
+}
+
+const binary_format::Image* ImageGroup::findImageByCacheOffset(size_t cacheVmOffset, uint32_t& mhCacheOffset, uint8_t& foundPermissions) const
+{
+    assert(groupNum() == 0);
+
+    const binary_format::DyldCacheSegment* cacheSegs = (binary_format::DyldCacheSegment*)segmentPool(0);
+    const binary_format::Image* image = (binary_format::Image*)((char*)_binaryData + _binaryData->imagesPoolOffset);
+    // most address lookups are in TEXT, so just search first segment in first pass
+    for (uint32_t imageIndex=0; imageIndex < _binaryData->imagesPoolCount; ++imageIndex) {
+        const binary_format::DyldCacheSegment* segInfo = &cacheSegs[image->segmentsArrayStartIndex];
+        if ( (cacheVmOffset >= segInfo->cacheOffset) && (cacheVmOffset < (segInfo->cacheOffset + segInfo->size)) ) {
+            mhCacheOffset    = segInfo->cacheOffset;
+            foundPermissions = segInfo->permissions;
+            return image;
+        }
+        image = (binary_format::Image*)((char*)image + _binaryData->imagesEntrySize);
+    }
+    // second pass, skip TEXT segment
+    image = (binary_format::Image*)((char*)_binaryData + _binaryData->imagesPoolOffset);
+    for (uint32_t imageIndex=0; imageIndex < _binaryData->imagesPoolCount; ++imageIndex) {
+        for (uint32_t segIndex=1; segIndex < image->segmentsArrayCount; ++segIndex) {
+            const binary_format::DyldCacheSegment* segInfo = &cacheSegs[image->segmentsArrayStartIndex+segIndex];
+            if ( (cacheVmOffset >= segInfo->cacheOffset) && (cacheVmOffset < (segInfo->cacheOffset + segInfo->size)) ) {
+                mhCacheOffset    = cacheSegs[image->segmentsArrayStartIndex].cacheOffset;
+                foundPermissions = segInfo->permissions;
+                return image;
+            }
+        }
+        image = (binary_format::Image*)((char*)image + _binaryData->imagesEntrySize);
+    }
+    return nullptr;
+}
+
+void ImageGroup::forEachAliasOf(uint32_t imageIndex, void (^handler)(const char* aliasPath, uint32_t aliasPathHash, bool& stop)) const
+{
+    bool stop = false;
+    const binary_format::AliasEntry* aliasEntries =  (binary_format::AliasEntry*)((uint8_t*)_binaryData + _binaryData->imageAliasOffset);
+    for (uint32_t i=0; i < _binaryData->imageAliasCount; ++i) {
+        if ( aliasEntries[i].imageIndexInGroup ==  imageIndex ) {
+            const char* aliasPath = stringFromPool(aliasEntries[i].aliasOffsetInStringPool);
+            handler(aliasPath, aliasEntries[i].aliasHash, stop);
+            if ( stop )
+                break;
+        }
+    }
+}
+
+const char* ImageGroup::stringPool() const
+{
+    return (char*)_binaryData + _binaryData->stringsPoolOffset;
+}
+
+const char* ImageGroup::stringFromPool(uint32_t offset) const
+{
+    assert(offset < _binaryData->stringsPoolSize);
+    return (char*)_binaryData + _binaryData->stringsPoolOffset + offset;
+}
+
+uint32_t ImageGroup::stringPoolSize() const
+{
+    return _binaryData->stringsPoolSize;;
+}
+
+binary_format::ImageRef ImageGroup::dependentPool(uint32_t index) const
+{
+    assert(index < _binaryData->dependentsPoolCount);
+    const binary_format::ImageRef* depArray = (binary_format::ImageRef*)((char*)_binaryData + _binaryData->dependentsPoolOffset);
+    return depArray[index];
+}
+
+const uint64_t* ImageGroup::segmentPool(uint32_t index) const
+{
+    assert(index < _binaryData->segmentsPoolCount);
+    const uint64_t* segArray = (uint64_t*)((char*)_binaryData + _binaryData->segmentsPoolOffset);
+    return &segArray[index];
+}
+
+
+const uint32_t* ImageGroup::initializerOffsetsPool() const
+{
+    return (uint32_t*)((char*)_binaryData + _binaryData->intializerOffsetPoolOffset);
+}
+
+const uint32_t ImageGroup::initializerOffsetsCount() const
+{
+    return _binaryData->intializerOffsetPoolCount;
+}
+
+const binary_format::ImageRef* ImageGroup::intializerListPool() const
+{
+    return (binary_format::ImageRef*)((char*)_binaryData + _binaryData->intializerListPoolOffset);
+}
+
+const uint32_t ImageGroup::intializerListPoolCount() const
+{
+    return _binaryData->intializerListPoolCount;
+}
+
+const binary_format::AllFixupsBySegment* ImageGroup::fixUps(uint32_t offset) const
+{
+    return (binary_format::AllFixupsBySegment*)((char*)_binaryData + _binaryData->fixupsOffset + offset);
+}
+
+const TargetSymbolValue* ImageGroup::targetValuesArray() const
+{
+    return (TargetSymbolValue*)((char*)_binaryData + _binaryData->targetsOffset);
+}
+
+uint32_t ImageGroup::targetValuesCount() const
+{
+    return _binaryData->targetsPoolCount;
+}
+
+
+const uint32_t* ImageGroup::dofOffsetsPool() const
+{
+    return (uint32_t*)((char*)_binaryData + _binaryData->dofOffsetPoolOffset);
+}
+
+const uint32_t ImageGroup::dofOffsetsCount() const
+{
+    return _binaryData->dofOffsetPoolCount;
+}
+
+
+const uint32_t* ImageGroup::indirectGroupNumsPool() const
+{
+    return (uint32_t*)((char*)_binaryData + _binaryData->indirectGroupNumPoolOffset);
+}
+
+const uint32_t ImageGroup::indirectGroupNumsCount() const
+{
+    return _binaryData->indirectGroupNumPoolCount;
+}
+
+uint32_t ImageGroup::indirectGroupNum(uint32_t offset) const
+{
+    assert(offset < _binaryData->indirectGroupNumPoolCount);
+    return indirectGroupNumsPool()[offset];
+}
+
+uint32_t ImageGroup::hashFunction(const char* str)
+{
+    uint32_t h = 0;
+    for (const char* s=str; *s != '\0'; ++s)
+        h = h*5 + *s;
+    return h;
+}
+
+
+void ImageGroup::forEachDyldCachePatch(uint32_t patchTargetIndex, uint32_t cacheDataVmOffset, void (^handler)(uint32_t targetCacheOffset, uint32_t usePointersCacheOffset, bool hasAddend, bool& stop)) const
+{
+    assert(_binaryData->imagesEntrySize == sizeof(binary_format::CachedImage) && "only callable on group-0 in shared cache");
+    assert(patchTargetIndex < _binaryData->cachePatchTableCount);
+    const binary_format::PatchTable* patches = (binary_format::PatchTable*)((char*)_binaryData + _binaryData->cachePatchTableOffset);
+    uint32_t offsetsIndex      = patches[patchTargetIndex].offsetsStartIndex;
+    uint32_t targetCacheOffset = patches[patchTargetIndex].targetCacheOffset;
+    const binary_format::PatchOffset* patchLocationOffsets = (binary_format::PatchOffset*)((char*)_binaryData + _binaryData->cachePatchOffsetsOffset);
+    bool stop = false;
+    while ( !stop ) {
+        assert(offsetsIndex < _binaryData->cachePatchOffsetsCount);
+        binary_format::PatchOffset entry = patchLocationOffsets[offsetsIndex];
+        ++offsetsIndex;
+        handler(targetCacheOffset, cacheDataVmOffset+entry.dataRegionOffset, entry.hasAddend, stop);
+        if ( entry.last )
+            stop = true;
+    }
+}
+
+void ImageGroup::forEachImageRefOverride(void (^handler)(binary_format::ImageRef standardDylibRef, binary_format::ImageRef overrideDylibRef, bool& stop)) const
+{
+    bool stop = false;
+    const binary_format::ImageRefOverride* entries = (binary_format::ImageRefOverride*)((char*)_binaryData + _binaryData->imageOverrideTableOffset);
+    for (uint32_t i=0; (i < _binaryData->imageOverrideTableCount) && !stop; ++i) {
+        handler(entries[i].standardDylib, entries[i].overrideDylib, stop);
+    }
+}
+
+void ImageGroup::forEachImageRefOverride(const ImageGroupList& groupList, void (^handler)(Image standardDylib, Image overrideDylib, bool& stop)) const
+{
+    forEachImageRefOverride(^(binary_format::ImageRef standardDylibRef, binary_format::ImageRef overrideDylibRef, bool& stop) {
+        Image standardDylib = Image::resolveImageRef(groupList, standardDylibRef, false);
+        Image overrideDylib = Image::resolveImageRef(groupList, overrideDylibRef, false);
+        handler(standardDylib, overrideDylib, stop);
+    });
+}
+
+
+#if DYLD_IN_PROCESS
+
+void ImageGroup::forEachDyldCachePatchLocation(const void* dyldCacheLoadAddress, uint32_t patchTargetIndex, void (^handler)(uintptr_t* locationToPatch, uintptr_t addend, bool&)) const
+{
+    DyldCacheParser cacheParser((DyldSharedCache*)dyldCacheLoadAddress, false);
+    uint32_t cacheDataVmOffset = (uint32_t)cacheParser.dataRegionRuntimeVmOffset();
+    forEachDyldCachePatch(patchTargetIndex, cacheDataVmOffset, ^(uint32_t targetCacheOffset, uint32_t usePointersCacheOffset, bool hasAddend, bool& stop) {
+        uintptr_t addend = 0;
+        uintptr_t* fixupLoc = (uintptr_t*)((char*)dyldCacheLoadAddress + usePointersCacheOffset);
+        if ( hasAddend ) {
+            uintptr_t currentValue  = *fixupLoc;
+            uintptr_t expectedValue = (uintptr_t)dyldCacheLoadAddress + targetCacheOffset;
+            uintptr_t delta         = currentValue - expectedValue;
+            assert(delta < 32);
+            addend = delta;
+        }
+        handler(fixupLoc, addend, stop);
+    });
+}
+
+void ImageGroup::forEachDyldCacheSymbolOverride(void (^handler)(uint32_t patchTableIndex, const BinaryImageData* image, uint32_t imageOffset, bool& stop)) const
+{
+    bool stop = false;
+    const binary_format::DyldCacheOverride* entries = (binary_format::DyldCacheOverride*)((char*)_binaryData + _binaryData->symbolOverrideTableOffset);
+    for (uint32_t i=0; (i < _binaryData->symbolOverrideTableCount) && !stop; ++i) {
+        handler(entries[i].patchTableIndex, imageBinary(entries[i].imageIndex), entries[i].imageOffset, stop);
+    }
+}
+
+#else
+
+void ImageGroup::forEachDyldCacheSymbolOverride(void (^handler)(uint32_t patchTableIndex, uint32_t imageIndexInClosure, uint32_t imageOffset, bool& stop)) const
+{
+    bool stop = false;
+    const binary_format::DyldCacheOverride* entries = (binary_format::DyldCacheOverride*)((char*)_binaryData + _binaryData->symbolOverrideTableOffset);
+    for (uint32_t i=0; (i < _binaryData->symbolOverrideTableCount) && !stop; ++i) {
+        handler(entries[i].patchTableIndex, entries[i].imageIndex, entries[i].imageOffset, stop);
+    }
+}
+
+void ImageGroup::forEachDyldCachePatchLocation(const DyldCacheParser& cacheParser, void (^handler)(uint32_t targetCacheOffset, const std::vector<uint32_t>& usesPointersCacheOffsets, bool& stop)) const
+{
+    uint32_t cacheDataVmOffset = (uint32_t)cacheParser.dataRegionRuntimeVmOffset();
+    __block std::vector<uint32_t> pointerCacheOffsets;
+    bool stop = false;
+    for (uint32_t patchIndex=0; patchIndex < _binaryData->cachePatchTableCount; ++patchIndex) {
+        pointerCacheOffsets.clear();
+        __block uint32_t targetCacheOffset = 0;
+        forEachDyldCachePatch(patchIndex, cacheDataVmOffset, ^(uint32_t targetCacheOff, uint32_t usePointersCacheOffset, bool hasAddend, bool&) {
+            targetCacheOffset = targetCacheOff;
+            pointerCacheOffsets.push_back(usePointersCacheOffset);
+        });
+        std::sort(pointerCacheOffsets.begin(), pointerCacheOffsets.end(), [&](uint32_t a, uint32_t b) { return a < b; });
+        handler(targetCacheOffset, pointerCacheOffsets, stop);
+        if ( stop )
+            break;
+    }
+}
+
+bool ImageGroup::hasPatchTableIndex(uint32_t targetCacheOffset, uint32_t& foundIndex) const
+{
+    const binary_format::PatchTable* patches = (binary_format::PatchTable*)((char*)_binaryData + _binaryData->cachePatchTableOffset);
+    for (uint32_t i=0; i < _binaryData->cachePatchTableCount; ++i) {
+        if ( patches[i].targetCacheOffset == targetCacheOffset ) {
+            foundIndex = i;
+            return true;
+        }
+    }
+    return false;
+}
+
+#endif
+
+
+////////////////////////////  Image ////////////////////////////////////////
+
+
+
+const ImageGroup Image::group() const
+{
+    return ImageGroup((binary_format::ImageGroup*)(((char*)_binaryData) + (_binaryData->groupOffset)));
+}
+
+uint32_t Image::maxLoadCount() const
+{
+    return _binaryData->maxLoadCount;
+}
+
+const char* Image::path() const
+{
+    return group().stringFromPool(_binaryData->pathPoolOffset);
+}
+
+uint32_t Image::pathHash() const
+{
+    return _binaryData->pathHash;
+}
+
+const char* Image::leafName() const
+{
+    const char* path = group().stringFromPool(_binaryData->pathPoolOffset);
+    const char* lastSlash = strrchr(path, '/');
+    if ( lastSlash != nullptr )
+        return lastSlash+1;
+    else
+        return path;
+}
+
+const uuid_t* Image::uuid() const
+{
+    return &(_binaryData->uuid);
+}
+
+bool Image::isInvalid() const
+{
+    return (_binaryData == nullptr) || _binaryData->isInvalid;
+}
+
+bool Image::hasObjC() const
+{
+    return _binaryData->hasObjC;
+}
+
+bool Image::isBundle() const
+{
+    return _binaryData->isBundle;
+}
+
+bool Image::hasWeakDefs() const
+{
+    return _binaryData->hasWeakDefs;
+}
+
+bool Image::mayHavePlusLoads() const
+{
+    return _binaryData->mayHavePlusLoads;
+}
+
+bool Image::hasTextRelocs() const
+{
+    return _binaryData->hasTextRelocs;
+}
+
+bool Image::neverUnload() const
+{
+    return _binaryData->neverUnload;
+}
+
+bool Image::cwdMustBeThisDir() const
+{
+    return _binaryData->cwdSameAsThis;
+}
+
+bool Image::isPlatformBinary() const
+{
+    return _binaryData->isPlatformBinary;
+}
+
+bool Image::overridableDylib() const
+{
+    return _binaryData->overridableDylib;
+}
+
+void Image::forEachDependentImage(const ImageGroupList& groups, void (^handler)(uint32_t depIndex, Image depImage, LinkKind kind, bool& stop)) const
+{
+    assert(!_binaryData->isInvalid);
+    binary_format::ImageRef missingRef = binary_format::ImageRef::weakImportMissing();
+    __block bool stop = false;
+    for (uint32_t depIndex=0; (depIndex < _binaryData->dependentsArrayCount) && !stop; ++depIndex) {
+        binary_format::ImageRef ref = group().dependentPool(_binaryData->dependentsArrayStartIndex + depIndex);
+        if ( ref != missingRef ) {
+            Image depImage(resolveImageRef(groups, ref));
+            handler(depIndex, depImage, (LinkKind)ref.kind(), stop);
+        }
+    }
+}
+
+#if !DYLD_IN_PROCESS
+bool Image::recurseAllDependentImages(const ImageGroupList& groups, std::unordered_set<const BinaryImageData*>& allDependents) const
+{
+    if ( isInvalid() )
+        return false;
+    __block bool result = true;
+    forEachDependentImage(groups, ^(uint32_t depIndex, Image depImage, LinkKind kind, bool& stop) {
+        if ( allDependents.count(depImage.binaryData()) == 0 ) {
+            allDependents.insert(depImage.binaryData());
+            if ( !depImage.recurseAllDependentImages(groups, allDependents) ) {
+                result = false;
+                stop = true;
+            }
+        }
+    });
+    return result;
+}
+#endif
+
+bool Image::recurseAllDependentImages(const ImageGroupList& groups, SlowLoadSet& allDependents, bool& stopped,
+                                      void (^handler)(const dyld3::launch_cache::binary_format::Image* aBinImage, bool& stop)) const
+{
+    __block bool result = true;
+    // breadth first, add all directly dependent images
+    const dyld3::launch_cache::binary_format::Image* needToProcessArray[_binaryData->dependentsArrayCount];
+    memset((void*)needToProcessArray, 0, _binaryData->dependentsArrayCount * sizeof(*needToProcessArray));
+    const dyld3::launch_cache::binary_format::Image** const needToProcess = needToProcessArray;
+    forEachDependentImage(groups, ^(uint32_t depIndex, Image depImage, LinkKind kind, bool& stop) {
+        const dyld3::launch_cache::binary_format::Image* depImageData = depImage.binaryData();
+        if ( allDependents.contains(depImageData) ) {
+            needToProcess[depIndex] = nullptr;
+        }
+        else {
+            needToProcess[depIndex] = depImageData;
+            if ( !allDependents.add(depImageData) ) {
+                result = false;
+                stop = true;
+                return;
+            }
+            if (handler) {
+                handler(depImageData, stop);
+                if ( stop )
+                    stopped = true;
+            }
+        }
+    });
+
+    // recurse on each dependent image
+    for (int i=0; !stopped && (i < _binaryData->dependentsArrayCount); ++i) {
+        if ( const dyld3::launch_cache::binary_format::Image* depImageData = needToProcess[i] ) {
+            Image depImage(depImageData);
+            if ( !depImage.recurseAllDependentImages(groups, allDependents, stopped, handler) ) {
+                return false;
+            }
+        }
+    }
+
+    return result;
+}
+
+bool Image::recurseAllDependentImages(const ImageGroupList& groups, SlowLoadSet& allDependents,
+                                      void (^handler)(const dyld3::launch_cache::binary_format::Image* aBinImage, bool& stop)) const
+{
+    bool stopped = false;
+    return recurseAllDependentImages(groups, allDependents, stopped, handler);
+}
+
+void Image::forEachDiskSegment(void (^handler)(uint32_t segIndex, uint32_t fileOffset, uint32_t fileSize, int64_t vmOffset, uint64_t vmSize, uint8_t permissions, bool& stop)) const
+{
+    assert(isDiskImage());
+    const uint32_t                      pageSize     = (_binaryData->has16KBpages ?  0x4000 : 0x1000);
+    const uint64_t*                     rawSegs      = group().segmentPool(_binaryData->segmentsArrayStartIndex);
+    const binary_format::DiskSegment*   diskSegs     = (binary_format::DiskSegment*)rawSegs;
+    uint32_t                            segIndex     = 0;
+    uint32_t                            fileOffset   = 0;
+    int64_t                             vmOffset     = 0;
+    // decrement vmOffset by all segments before TEXT (e.g. PAGEZERO)
+    for (uint32_t i=0; i < _binaryData->segmentsArrayCount; ++i) {
+        const binary_format::DiskSegment* seg = &diskSegs[i];
+        if ( seg->filePageCount != 0 ) {
+            break;
+        }
+        vmOffset -= (uint64_t)seg->vmPageCount * pageSize;
+    }
+    // walk each segment and call handler
+    for (uint32_t i=0; i < _binaryData->segmentsArrayCount; ++i) {
+        const binary_format::DiskSegment* seg = &diskSegs[i];
+        uint64_t vmSize   = (uint64_t)seg->vmPageCount * pageSize;
+        uint32_t fileSize = seg->filePageCount * pageSize;
+        if ( !seg->paddingNotSeg ) {
+            bool     stop     = false;
+            handler(segIndex, ( fileSize == 0) ? 0 : fileOffset, fileSize, vmOffset, vmSize, seg->permissions, stop);
+            ++segIndex;
+            if ( stop )
+                break;
+        }
+        vmOffset   += vmSize;
+        fileOffset += fileSize;
+    }
+}
+
+void Image::forEachCacheSegment(void (^handler)(uint32_t segIndex, uint64_t vmOffset, uint64_t vmSize, uint8_t permissions, bool& stop)) const
+{
+    assert(!isDiskImage());
+    const uint64_t* rawSegs = group().segmentPool(_binaryData->segmentsArrayStartIndex);
+    const binary_format::DyldCacheSegment* cacheSegs = (binary_format::DyldCacheSegment*)rawSegs;
+    bool stop = false;
+    for (uint32_t i=0; i < _binaryData->segmentsArrayCount; ++i) {
+        uint64_t vmOffset    = cacheSegs[i].cacheOffset - cacheSegs[0].cacheOffset;
+        uint64_t vmSize      = cacheSegs[i].size;
+        uint8_t  permissions = cacheSegs[i].permissions;
+        handler(i, vmOffset, vmSize, permissions, stop);
+        if ( stop )
+            break;
+    }
+}
+
+bool Image::segmentHasFixups(uint32_t segIndex) const
+{
+    return (segmentFixups(segIndex) != nullptr);
+}
+
+bool Image::containsAddress(const void* addr, const void* imageLoadAddress, uint8_t* permissions) const
+{
+    if ( addr < imageLoadAddress )
+        return false;
+
+    __block bool found = false;
+    uint64_t offsetInImage = (char*)addr - (char*)imageLoadAddress;
+    if ( _binaryData->isDiskImage ) {
+        forEachDiskSegment(^(uint32_t segIterIndex, uint32_t fileOffset, uint32_t fileSize, int64_t vmOffset, uint64_t vmSize, uint8_t segPerms, bool& stop) {
+            if ( (offsetInImage >= vmOffset) && (offsetInImage < vmOffset+vmSize) ) {
+                if ( permissions != nullptr )
+                    *permissions = segPerms;
+                found = true;
+                stop = true;
+            }
+        });
+    }
+    else {
+        forEachCacheSegment(^(uint32_t segIterIndex, uint64_t vmOffset, uint64_t vmSize, uint8_t segPerms, bool& stop) {
+            if ( (offsetInImage >= vmOffset) && (offsetInImage < vmOffset+vmSize) ) {
+                if ( permissions != nullptr )
+                    *permissions = segPerms;
+                found = true;
+                stop = true;
+            }
+        });
+    }
+    return found;
+}
+
+void Image::forEachInitializer(const void* imageLoadAddress, void (^handler)(const void* initializer)) const
+{
+    const uint32_t   initCount   = _binaryData->initOffsetsArrayCount;
+    const uint32_t   startIndex  = _binaryData->initOffsetsArrayStartIndex;
+    const uint32_t*  initOffsets = group().initializerOffsetsPool();
+    assert(startIndex + initCount <= group().initializerOffsetsCount());
+    for (uint32_t i=0; i < initCount; ++i) {
+        uint32_t anOffset = initOffsets[startIndex+i];
+        const void* func = (char*)imageLoadAddress + anOffset;
+        handler(func);
+    }
+}
+
+void Image::forEachInitBefore(void (^handler)(binary_format::ImageRef imageToInit)) const
+{
+    const uint32_t                  initCount   = _binaryData->initBeforeArrayCount;
+    const uint32_t                  startIndex  = _binaryData->initBeforeArrayStartIndex;
+    const uint32_t                  endIndex    = group().intializerListPoolCount();
+    const binary_format::ImageRef*  initRefs    = group().intializerListPool();
+    assert(startIndex + initCount <= endIndex);
+    for (uint32_t i=0; i < initCount; ++i) {
+        binary_format::ImageRef ref = initRefs[startIndex+i];
+        handler(ref);
+    }
+}
+
+void Image::forEachDOF(const void* imageLoadAddress, void (^handler)(const void* section)) const
+{
+    const uint32_t   dofCount   = _binaryData->dofOffsetsArrayCount;
+    const uint32_t   startIndex  = _binaryData->dofOffsetsArrayStartIndex;
+    const uint32_t*  dofOffsets = group().dofOffsetsPool();
+    assert(startIndex + dofCount <= group().dofOffsetsCount());
+    for (uint32_t i=0; i < dofCount; ++i) {
+        uint32_t anOffset = dofOffsets[startIndex+i];
+        const void* section = (char*)imageLoadAddress + anOffset;
+        handler(section);
+    }
+}
+
+Image Image::resolveImageRef(const ImageGroupList& groups, binary_format::ImageRef ref, bool applyOverrides)
+{
+    // first look if ref image is overridden in closure
+    __block binary_format::ImageRef targetRef = ref;
+    if ( applyOverrides ) {
+        binary_format::ImageRef refToMatch = ref;
+        refToMatch.clearKind();
+        for (int i=0; i < groups.count(); ++i) {
+            ImageGroup aGroup(groups[i]);
+            if ( aGroup.groupNum() >= 2 ) {
+                aGroup.forEachImageRefOverride(^(binary_format::ImageRef standardDylibRef, binary_format::ImageRef overrideDylibRef, bool &stop) {
+                    if ( refToMatch == standardDylibRef ) {
+                        targetRef = overrideDylibRef;
+                        stop = true;
+                    }
+                });
+            }
+        }
+    }
+    // create Image object from targetRef
+    for (int i=0; i < groups.count(); ++i) {
+        ImageGroup aGroup(groups[i]);
+        if ( aGroup.groupNum() == targetRef.groupNum() ) {
+            return aGroup.image(targetRef.indexInGroup());
+        }
+    }
+    //assert(0 && "invalid ImageRef");
+    return Image(nullptr);
+}
+
+void Image::forEachInitBefore(const ImageGroupList& groups, void (^handler)(Image imageToInit)) const
+{
+    forEachInitBefore(^(binary_format::ImageRef ref) {
+        handler(resolveImageRef(groups, ref));
+    });
+}
+
+bool Image::validateUsingModTimeAndInode() const
+{
+    return !group().binaryData()->imageFileInfoIsCdHash;
+}
+
+bool Image::validateUsingCdHash() const
+{
+    // don't have cdHash info if union has modtime info in it
+    if ( !group().binaryData()->imageFileInfoIsCdHash )
+        return false;
+
+    // don't have codesign blob in dyld cache
+    if ( !_binaryData->isDiskImage )
+        return false;
+
+    // return true if image is code signed and cdHash16 is non-zero
+    const binary_format::DiskImage* diskImage = asDiskImage();
+    if ( diskImage->codeSignFileOffset == 0 )
+        return false;
+
+    uint8_t zeros[16];
+    bzero(zeros, 16);
+    return (memcmp(cdHash16(), zeros, 16) != 0);
+}
+
+const uint8_t* Image::cdHash16() const
+{
+    return _binaryData->fileInfo.cdHash16.bytes;
+}
+
+uint64_t Image::fileModTime() const
+{
+    return _binaryData->fileInfo.statInfo.mtime;
+}
+
+uint64_t Image::fileINode() const
+{
+    return _binaryData->fileInfo.statInfo.inode;
+}
+
+
+bool Image::isDiskImage() const
+{
+    return _binaryData->isDiskImage;
+}
+
+const binary_format::DiskImage* Image::asDiskImage() const
+{
+    assert(_binaryData->isDiskImage);
+    return (binary_format::DiskImage*)_binaryData;
+}
+
+const binary_format::CachedImage* Image::asCachedImage() const
+{
+    assert(!_binaryData->isDiskImage);
+    return (binary_format::CachedImage*)_binaryData;
+}
+
+uint32_t Image::pageSize() const
+{
+    return (_binaryData->has16KBpages ?  0x4000 : 0x1000);
+}
+
+uint32_t Image::cacheOffset() const
+{
+    assert(!_binaryData->isDiskImage);
+    const uint64_t* rawSegs = group().segmentPool(_binaryData->segmentsArrayStartIndex);
+    const binary_format::DyldCacheSegment* cacheSegs = (binary_format::DyldCacheSegment*)rawSegs;
+    return cacheSegs[0].cacheOffset;
+}
+
+uint32_t Image::patchStartIndex() const
+{
+    return asCachedImage()->patchStartIndex;
+}
+
+uint32_t Image::patchCount() const
+{
+    return asCachedImage()->patchCount;
+}
+
+uint64_t Image::sliceOffsetInFile() const
+{
+    return asDiskImage()->sliceOffsetIn4K * 4096;
+}
+
+bool Image::hasCodeSignature(uint32_t& fileOffset, uint32_t& size) const
+{
+    const binary_format::DiskImage* diskImage = asDiskImage();
+    if ( diskImage->codeSignFileOffset != 0 ) {
+        fileOffset = diskImage->codeSignFileOffset;
+        size       = diskImage->codeSignFileSize;
+        return true;
+    }
+    return false;
+}
+
+bool Image::isFairPlayEncrypted(uint32_t& textOffset, uint32_t& size) const
+{
+    const binary_format::DiskImage* diskImage = asDiskImage();
+    if ( diskImage->fairPlayTextPageCount != 0 ) {
+        textOffset = diskImage->fairPlayTextStartPage * pageSize();
+        size       = diskImage->fairPlayTextPageCount * pageSize();
+        return true;
+    }
+    return false;
+}
+
+uint64_t Image::vmSizeToMap() const
+{
+    return asDiskImage()->totalVmPages * pageSize();
+}
+
+void Image::forEachFixup(const uint8_t* pageFixups, const void* segContent, uint32_t& offset, uint32_t& ordinal,
+                         void (^handler)(uint32_t pageOffset, FixupKind kind, uint32_t ordinal, bool& stop))
+{
+    bool stop = false;
+    for (const uint8_t* p = pageFixups; (*p != 0) && !stop;) {
+        binary_format::FixUpOpcode fullOp = (binary_format::FixUpOpcode)(*p);
+        binary_format::FixUpOpcode majorOp = (binary_format::FixUpOpcode)(*p & 0xF0);
+        uint8_t low4 = (*p & 0x0F);
+        switch ( majorOp ) {
+            case binary_format::FixUpOpcode::done:
+                return;
+            case binary_format::FixUpOpcode::rebase32: // apply
+                switch ( fullOp ) {
+                    case binary_format::FixUpOpcode::bind64:
+                        handler(offset, FixupKind::bind64, ordinal, stop);
+                        offset += 8;
+                        ++p;
+                        break;
+                    case binary_format::FixUpOpcode::bind32:
+                        handler(offset, FixupKind::bind32, ordinal, stop);
+                        offset += 4;
+                        ++p;
+                        break;
+                    case binary_format::FixUpOpcode::rebase64:
+                        handler(offset, FixupKind::rebase64, 0, stop);
+                        offset += 8;
+                        ++p;
+                        break;
+                    case binary_format::FixUpOpcode::rebase32:
+                        handler(offset, FixupKind::rebase32, 0, stop);
+                        offset += 4;
+                        ++p;
+                        break;
+                    case binary_format::FixUpOpcode::rebaseText32:
+                        handler(offset, FixupKind::rebaseText32, 0, stop);
+                        offset += 4;
+                        ++p;
+                        break;
+                    case binary_format::FixUpOpcode::bindText32:
+                        handler(offset, FixupKind::bindText32, ordinal, stop);
+                        offset += 4;
+                        ++p;
+                        break;
+                    case binary_format::FixUpOpcode::bindTextRel32:
+                        handler(offset, FixupKind::bindTextRel32, ordinal, stop);
+                        offset += 4;
+                        ++p;
+                        break;
+                    case binary_format::FixUpOpcode::bindImportJmp32:
+                        handler(offset, FixupKind::bindImportJmp32, ordinal, stop);
+                        offset += 5;
+                        ++p;
+                        break;
+                    //case binary_format::FixUpOpcode::fixupChain64:
+                    //    assert(0 && "rebase/bind chain support not implemented yet");
+                    //    break;
+                    default:
+                        assert(0 && "bad opcode");
+                        break;
+                }
+                break;
+            case binary_format::FixUpOpcode::incPageOffset:
+                if ( low4 == 0 ) {
+                    ++p;
+                    offset += read_uleb128(p, p+8)*4;
+                }
+                else {
+                    offset += (low4*4);
+                    ++p;
+                }
+                break;
+            case binary_format::FixUpOpcode::setPageOffset:
+                if ( low4 == 0 ) {
+                    ++p;
+                    offset = (uint32_t)read_uleb128(p, p+8);
+                }
+                else {
+                    offset = low4;
+                    ++p;
+                }
+                break;
+            case binary_format::FixUpOpcode::incOrdinal:
+                if ( low4 == 0 ) {
+                    ++p;
+                    ordinal += read_uleb128(p, p+8);
+                }
+                else {
+                    ordinal += low4;
+                    ++p;
+                }
+                break;
+            case binary_format::FixUpOpcode::setOrdinal:
+                if ( low4 == 0 ) {
+                    ++p;
+                    ordinal = (uint32_t)read_uleb128(p, p+8);
+                }
+                else {
+                    ordinal = low4;
+                    ++p;
+                }
+                break;
+            case binary_format::FixUpOpcode::repeat: {
+                    ++p;
+                    uint32_t count = (uint32_t)read_uleb128(p, p+8);
+                    uint8_t pattern[32];
+                    for (int j=0; j < low4; ++j) {
+                        pattern[j] = *p++;
+                    }
+                    pattern[low4] = (uint8_t)binary_format::FixUpOpcode::done;
+                    for (int j=0; j < count; ++j) {
+                        forEachFixup(&pattern[0], segContent, offset, ordinal, handler);
+                        if ( stop )
+                            break;
+                    }
+                }
+                break;
+            default:
+                assert(0 && "bad opcode");
+                break;
+        }
+    }
+}
+
+const binary_format::SegmentFixupsByPage* Image::segmentFixups(uint32_t segIndex) const
+{
+    const binary_format::DiskImage* diskImage = asDiskImage();
+    //const BinaryImageGroupData* g =  group().binaryData();
+    uint32_t segCountWithFixups = diskImage->fixupsPoolSegCount;
+    //fprintf(stderr,"segmentFixups(binImage=%p, segIndex=%d), group=%p, segCountWithFixup=%d\n", _binaryData, segIndex, g, segCountWithFixups);
+    const binary_format::AllFixupsBySegment* allFixups = group().fixUps(diskImage->fixupsPoolOffset);
+    for (uint32_t i=0; i < segCountWithFixups; ++i) {
+        if ( allFixups[i].segIndex == segIndex ) {
+            //fprintf(stderr,"segmentFixups(binImage=%p, segIndex=%d) allFixups=%p, allFixups[%d].segIndex=%d, allFixups[%d].offset=%d\n", _binaryData, segIndex, allFixups, i, allFixups[i].segIndex, i, allFixups[i].offset);
+            return (binary_format::SegmentFixupsByPage*)((char*)allFixups + allFixups[i].offset);
+        }
+    }
+    //fprintf(stderr,"segmentFixups(binImage=%p, segIndex=%d) => nullptr\n", _binaryData, segIndex);
+    return nullptr;
+}
+
+void Image::forEachFixup(uint32_t segIndex, MemoryRange segContent, void (^handler)(uint64_t segOffset, FixupKind, TargetSymbolValue, bool& stop)) const
+{
+    const binary_format::SegmentFixupsByPage* segFixups = segmentFixups(segIndex);
+    if ( segFixups == nullptr )
+        return;
+
+    assert(segFixups->pageCount*segFixups->pageSize <= segContent.size);
+
+    const uint32_t ordinalsIndexInGroupPool = asDiskImage()->targetsArrayStartIndex;
+    const uint32_t maxOrdinal = asDiskImage()->targetsArrayCount;
+    const TargetSymbolValue* groupArray = group().targetValuesArray();
+    assert(ordinalsIndexInGroupPool < group().targetValuesCount());
+    const TargetSymbolValue* targetOrdinalArray = &groupArray[ordinalsIndexInGroupPool];
+
+    for (uint32_t pageIndex=0; pageIndex < segFixups->pageCount; ++pageIndex) {
+        const uint8_t* opcodes = (uint8_t*)(segFixups) + segFixups->pageInfoOffsets[pageIndex];
+        uint64_t pageStartOffet = pageIndex * segFixups->pageSize;
+        uint32_t curOffset = 0;
+        uint32_t curOrdinal = 0;
+        forEachFixup(opcodes, segContent.address, curOffset, curOrdinal, ^(uint32_t pageOffset, FixupKind kind, uint32_t targetOrdinal, bool& stop) {
+            assert(targetOrdinal < maxOrdinal);
+            handler(pageStartOffet + pageOffset, kind, targetOrdinalArray[targetOrdinal], stop);
+        });
+    }
+}
+
+
+} // namespace launch_cache
+} // namespace dyld3
+
+
+
diff --git a/dyld3/LaunchCacheWriter.cpp b/dyld3/LaunchCacheWriter.cpp
new file mode 100644 (file)
index 0000000..e51fbdf
--- /dev/null
@@ -0,0 +1,1285 @@
+/*
+ * Copyright (c) 2017 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 <stdint.h>
+#include <string.h>
+#include <assert.h>
+#include <uuid/uuid.h>
+#include <mach/mach.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/uio.h>
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#include <sys/resource.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <dirent.h>
+
+#include <string>
+#include <map>
+#include <list>
+#include <unordered_set>
+#include <unordered_map>
+
+#include "LaunchCacheFormat.h"
+#include "LaunchCacheWriter.h"
+#include "shared-cache/dyld_cache_format.h"
+#include "shared-cache/DyldSharedCache.h"
+#include "shared-cache/FileUtils.h"
+
+namespace std
+{
+  template <>
+  struct hash<dyld3::launch_cache::binary_format::ImageRef>
+  {
+    std::size_t operator()(const dyld3::launch_cache::binary_format::ImageRef& value) const {
+        return std::hash<uint16_t>()(value.value());
+    }
+  };
+}
+
+
+namespace dyld3 {
+namespace launch_cache {
+
+
+static uintptr_t align(uintptr_t value, uintptr_t align)
+{
+    return (value+align-1) & (-align);
+}
+
+////////////////////////////  ImageGroupWriter ////////////////////////////////////////
+
+ImageGroupWriter::ImageGroupWriter(uint32_t groupNum, bool pages16KB, bool is64, bool dylibsExpectedOnDisk, bool mtimeAndInodeAreValid)
+    : _isDiskImage(groupNum != 0), _is64(is64), _groupNum(groupNum), _pageSize(pages16KB ? 0x4000 : 0x1000),
+      _dylibsExpectedOnDisk(dylibsExpectedOnDisk), _imageFileInfoIsCdHash(!mtimeAndInodeAreValid)
+{
+}
+
+
+uint32_t ImageGroupWriter::size() const
+{
+    binary_format::ImageGroup tempGroup;
+    layoutBinary(&tempGroup);
+    return tempGroup.stringsPoolOffset + tempGroup.stringsPoolSize;
+}
+
+void ImageGroupWriter::layoutBinary(binary_format::ImageGroup* grp) const
+{
+    grp->imagesEntrySize            = _isDiskImage ? sizeof(binary_format::DiskImage) : sizeof(binary_format::CachedImage);
+    grp->groupNum                   = _groupNum;
+    grp->dylibsExpectedOnDisk       = _dylibsExpectedOnDisk;
+    grp->imageFileInfoIsCdHash      = _imageFileInfoIsCdHash;
+    grp->padding                    = 0;
+
+    grp->imagesPoolCount            = imageCount();
+    grp->imagesPoolOffset           = sizeof(binary_format::ImageGroup);
+    uint32_t imagesPoolSize         = grp->imagesEntrySize * grp->imagesPoolCount;
+
+    grp->imageAliasCount            = (uint32_t)_aliases.size();
+    grp->imageAliasOffset           = grp->imagesPoolOffset + imagesPoolSize;
+    uint32_t imageAliasSize         = grp->imageAliasCount * sizeof(binary_format::AliasEntry);
+
+    grp->segmentsPoolCount          = (uint32_t)_segmentPool.size();
+    grp->segmentsPoolOffset         = (uint32_t)align(grp->imageAliasOffset + imageAliasSize, 8);
+    uint32_t segmentsPoolSize       = grp->segmentsPoolCount * sizeof(uint64_t);
+
+    grp->dependentsPoolCount        = (uint32_t)_dependentsPool.size();
+    grp->dependentsPoolOffset       = grp->segmentsPoolOffset + segmentsPoolSize;
+    uint32_t dependentsPoolSize     = grp->dependentsPoolCount * sizeof(binary_format::ImageRef);
+
+    grp->intializerOffsetPoolCount  = (uint32_t)_initializerOffsets.size();
+    grp->intializerOffsetPoolOffset = (uint32_t)align(grp->dependentsPoolOffset + dependentsPoolSize, 4);
+    uint32_t intializerOffsetSize   = grp->intializerOffsetPoolCount * sizeof(uint32_t);
+
+    grp->intializerListPoolCount    = (uint32_t)_initializerBeforeLists.size();
+    grp->intializerListPoolOffset   = grp->intializerOffsetPoolOffset  + intializerOffsetSize;
+    uint32_t intializerListPoolSize = grp->intializerListPoolCount * sizeof(binary_format::ImageRef);
+
+    grp->targetsPoolCount           = (uint32_t)_targetsPool.size();
+    grp->targetsOffset              = (uint32_t)align(grp->intializerListPoolOffset + intializerListPoolSize, 8);
+    uint32_t targetsSize            = grp->targetsPoolCount * sizeof(TargetSymbolValue);
+
+    grp->fixupsPoolSize             = (uint32_t)_fixupsPool.size();
+    grp->fixupsOffset               = (uint32_t)align(grp->targetsOffset + targetsSize, 4);
+
+    grp->cachePatchTableCount       = (uint32_t)_patchPool.size();
+    grp->cachePatchTableOffset      = (uint32_t)align(grp->fixupsOffset + grp->fixupsPoolSize, 4);
+    uint32_t patchTableSize         = grp->cachePatchTableCount * sizeof(binary_format::PatchTable);
+
+    grp->cachePatchOffsetsCount     = (uint32_t)_patchLocationPool.size();
+    grp->cachePatchOffsetsOffset    = grp->cachePatchTableOffset + patchTableSize;
+    uint32_t patchOffsetsSize       = grp->cachePatchOffsetsCount * sizeof(binary_format::PatchOffset);
+
+    grp->symbolOverrideTableCount   = (uint32_t)_dyldCacheSymbolOverridePool.size();
+    grp->symbolOverrideTableOffset  = grp->cachePatchOffsetsOffset + patchOffsetsSize;
+    uint32_t symbolOverrideSize     = grp->symbolOverrideTableCount * sizeof(binary_format::DyldCacheOverride);
+
+    grp->imageOverrideTableCount    = (uint32_t)_imageOverridePool.size();
+    grp->imageOverrideTableOffset   = grp->symbolOverrideTableOffset + symbolOverrideSize;
+    uint32_t imageOverrideSize      = grp->imageOverrideTableCount * sizeof(binary_format::ImageRefOverride);
+
+    grp->dofOffsetPoolCount         = (uint32_t)_dofOffsets.size();
+    grp->dofOffsetPoolOffset        = grp->imageOverrideTableOffset  + imageOverrideSize;
+    uint32_t dofOffsetSize          = grp->dofOffsetPoolCount * sizeof(uint32_t);
+
+    grp->indirectGroupNumPoolCount  = (uint32_t)_indirectGroupNumPool.size();
+    grp->indirectGroupNumPoolOffset = grp->dofOffsetPoolOffset + dofOffsetSize;
+    uint32_t indirectGroupNumSize   = grp->indirectGroupNumPoolCount * sizeof(uint32_t);
+
+    grp->stringsPoolSize            = (uint32_t)_stringPool.size();
+    grp->stringsPoolOffset          = grp->indirectGroupNumPoolOffset + indirectGroupNumSize;
+}
+
+
+void ImageGroupWriter::finalizeTo(Diagnostics& diag, const std::vector<const BinaryImageGroupData*>& curGroups, binary_format::ImageGroup* grp) const
+{
+    layoutBinary(grp);
+    uint8_t* buffer = (uint8_t*)grp;
+    if ( imageCount() > 0 ) {
+        uint32_t pad1Size   = grp->segmentsPoolOffset - (grp->imageAliasOffset + grp->imageAliasCount * sizeof(binary_format::AliasEntry));
+        uint32_t pad2Size   = grp->targetsOffset - (grp->intializerListPoolOffset + grp->intializerListPoolCount * sizeof(binary_format::ImageRef));
+        memcpy(&buffer[grp->imagesPoolOffset],          &imageByIndex(0),                   grp->imagesEntrySize * grp->imagesPoolCount);
+        memcpy(&buffer[grp->imageAliasOffset],          &_aliases[0],                       grp->imageAliasCount * sizeof(binary_format::AliasEntry));
+        bzero( &buffer[grp->segmentsPoolOffset-pad1Size],                                   pad1Size);
+        memcpy(&buffer[grp->segmentsPoolOffset],        &_segmentPool[0],                   grp->segmentsPoolCount * sizeof(uint64_t));
+        memcpy(&buffer[grp->dependentsPoolOffset],      &_dependentsPool[0],                grp->dependentsPoolCount * sizeof(binary_format::ImageRef));
+        memcpy(&buffer[grp->intializerListPoolOffset],  &_initializerBeforeLists[0],        grp->intializerListPoolCount * sizeof(binary_format::ImageRef));
+        memcpy(&buffer[grp->intializerOffsetPoolOffset],&_initializerOffsets[0],            grp->intializerOffsetPoolCount * sizeof(uint32_t));
+        bzero( &buffer[grp->targetsOffset-pad2Size],                                        pad2Size);
+        memcpy(&buffer[grp->targetsOffset],             &_targetsPool[0],                   grp->targetsPoolCount * sizeof(TargetSymbolValue));
+        memcpy(&buffer[grp->fixupsOffset],               _fixupsPool.start(),               grp->fixupsPoolSize);
+        memcpy(&buffer[grp->cachePatchTableOffset],     &_patchPool[0],                     grp->cachePatchTableCount * sizeof(binary_format::PatchTable));
+        memcpy(&buffer[grp->cachePatchOffsetsOffset],   &_patchLocationPool[0],             grp->cachePatchOffsetsCount * sizeof(binary_format::PatchOffset));
+        memcpy(&buffer[grp->symbolOverrideTableOffset], &_dyldCacheSymbolOverridePool[0],   grp->symbolOverrideTableCount * sizeof(binary_format::DyldCacheOverride));
+        memcpy(&buffer[grp->imageOverrideTableOffset],  &_imageOverridePool[0],             grp->imageOverrideTableCount * sizeof(binary_format::ImageRefOverride));
+        memcpy(&buffer[grp->dofOffsetPoolOffset],       &_dofOffsets[0],                    grp->dofOffsetPoolCount * sizeof(uint32_t));
+        memcpy(&buffer[grp->indirectGroupNumPoolOffset], &_indirectGroupNumPool[0],         grp->indirectGroupNumPoolCount * sizeof(uint32_t));
+        memcpy(&buffer[grp->stringsPoolOffset],         &_stringPool[0],                    grp->stringsPoolSize);
+    }
+
+    // now that we have a real ImageGroup, we can analyze it to find max load counts for each image
+    ImageGroup imGroup(grp);
+    std::unordered_set<const BinaryImageData*> allDependents;
+    STACK_ALLOC_DYNARRAY(const binary_format::ImageGroup*, curGroups.size()+1, newGroupList);
+    for (int i=0; i < curGroups.size(); ++i)
+        newGroupList[i] = curGroups[i];
+    newGroupList[newGroupList.count()-1] = grp;
+    for (uint32_t i=0; i < grp->imagesPoolCount; ++i) {
+        Image image = imGroup.image(i);
+        if ( image.isInvalid() )
+            continue;
+        allDependents.clear();
+        allDependents.insert(image.binaryData());
+        BinaryImageData* imageData = (BinaryImageData*)(buffer + grp->imagesPoolOffset + (i * grp->imagesEntrySize));
+        if ( !image.recurseAllDependentImages(newGroupList, allDependents) ) {
+            //diag.warning("%s dependents on an invalid dylib", image.path());
+            imageData->isInvalid = true;
+        }
+        imageData->maxLoadCount = (uint32_t)allDependents.size();
+    }
+}
+
+uint32_t ImageGroupWriter::maxLoadCount(Diagnostics& diag, const std::vector<const BinaryImageGroupData*>& curGroups, binary_format::ImageGroup* grp) const
+{
+    ImageGroup imGroup(grp);
+    std::unordered_set<const BinaryImageData*> allDependents;
+    std::vector<const BinaryImageGroupData*> allGroups = curGroups;
+    if ( grp->groupNum == 2 )
+        allGroups.push_back(grp);
+    DynArray<const binary_format::ImageGroup*> groupList(allGroups);
+    for (uint32_t i=0; i < grp->imagesPoolCount; ++i) {
+        Image image = imGroup.image(i);
+        if ( image.isInvalid() )
+            continue;
+        allDependents.insert(image.binaryData());
+        BinaryImageData* imageData = (BinaryImageData*)((char*)grp + grp->imagesPoolOffset + (i * grp->imagesEntrySize));
+        if ( !image.recurseAllDependentImages(groupList, allDependents) ) {
+            //diag.warning("%s dependents on an invalid dylib", image.path());
+            imageData->isInvalid = true;
+        }
+    }
+    return (uint32_t)allDependents.size();
+}
+
+void ImageGroupWriter::setImageCount(uint32_t count)
+{
+    if ( _isDiskImage ) {
+        _diskImages.resize(count);
+        bzero(&_diskImages[0], count*sizeof(binary_format::DiskImage));
+    }
+    else {
+        _images.resize(count);
+        bzero(&_images[0], count*sizeof(binary_format::CachedImage));
+    }
+
+    int32_t offset =  0 - (int32_t)sizeof(binary_format::ImageGroup);
+    for (uint32_t i=0; i < count; ++i) {
+        binary_format::Image& img = imageByIndex(i);
+        img.isDiskImage = _isDiskImage;
+        img.has16KBpages = (_pageSize == 0x4000);
+        img.groupOffset = offset;
+        if ( _isDiskImage )
+            offset -= sizeof(binary_format::DiskImage);
+        else
+            offset -= sizeof(binary_format::CachedImage);
+    }
+}
+
+uint32_t ImageGroupWriter::imageCount() const
+{
+    if ( _isDiskImage )
+        return (uint32_t)_diskImages.size();
+    else
+        return (uint32_t)_images.size();
+}
+
+binary_format::Image& ImageGroupWriter::imageByIndex(uint32_t imageIndex)
+{
+    assert(imageIndex < imageCount());
+    if ( _isDiskImage )
+        return _diskImages[imageIndex];
+    else
+        return _images[imageIndex];
+}
+
+const binary_format::Image& ImageGroupWriter::imageByIndex(uint32_t imageIndex) const
+{
+    assert(imageIndex < imageCount());
+    if ( _isDiskImage )
+        return _diskImages[imageIndex];
+    else
+        return _images[imageIndex];
+}
+
+bool ImageGroupWriter::isInvalid(uint32_t imageIndex) const
+{
+    return imageByIndex(imageIndex).isInvalid;
+}
+
+void ImageGroupWriter::setImageInvalid(uint32_t imageIndex)
+{
+    imageByIndex(imageIndex).isInvalid = true;
+}
+
+uint32_t ImageGroupWriter::addIndirectGroupNum(uint32_t groupNum)
+{
+    auto pos = _indirectGroupNumPoolExisting.find(groupNum);
+    if ( pos != _indirectGroupNumPoolExisting.end() )
+        return pos->second;
+    uint32_t startOffset = (uint32_t)_indirectGroupNumPool.size();
+    _indirectGroupNumPool.push_back(groupNum);
+    _indirectGroupNumPoolExisting[startOffset] = groupNum;
+    return startOffset;
+}
+
+uint32_t ImageGroupWriter::addString(const char* str)
+{
+    auto pos = _stringPoolExisting.find(str);
+    if ( pos != _stringPoolExisting.end() )
+        return pos->second;
+    uint32_t startOffset = (uint32_t)_stringPool.size();
+    size_t size = strlen(str) + 1;
+    _stringPool.insert(_stringPool.end(), str, &str[size]);
+    _stringPoolExisting[str] = startOffset;
+    return startOffset;
+}
+
+void ImageGroupWriter::alignStringPool()
+{
+    while ( (_stringPool.size() % 4) != 0 )
+        _stringPool.push_back('\0');
+}
+
+void ImageGroupWriter::setImagePath(uint32_t imageIndex, const char* path)
+{
+    binary_format::Image& image = imageByIndex(imageIndex);
+    image.pathPoolOffset = addString(path);
+    image.pathHash = ImageGroup::hashFunction(path);
+}
+
+void ImageGroupWriter::addImageAliasPath(uint32_t imageIndex, const char* anAlias)
+{
+    binary_format::AliasEntry entry;
+    entry.aliasHash                 = ImageGroup::hashFunction(anAlias);
+    entry.imageIndexInGroup         = imageIndex;
+    entry.aliasOffsetInStringPool   = addString(anAlias);
+    _aliases.push_back(entry);
+}
+
+void ImageGroupWriter::ImageGroupWriter::setImageUUID(uint32_t imageIndex, const uuid_t uuid)
+{
+    memcpy(imageByIndex(imageIndex).uuid, uuid, sizeof(uuid_t));
+}
+
+void ImageGroupWriter::setImageHasObjC(uint32_t imageIndex, bool value)
+{
+    imageByIndex(imageIndex).hasObjC = value;
+}
+
+void ImageGroupWriter::setImageIsBundle(uint32_t imageIndex, bool value)
+{
+    imageByIndex(imageIndex).isBundle = value;
+}
+
+void ImageGroupWriter::setImageHasWeakDefs(uint32_t imageIndex, bool value)
+{
+    imageByIndex(imageIndex).hasWeakDefs = value;
+}
+
+void ImageGroupWriter::setImageMayHavePlusLoads(uint32_t imageIndex, bool value)
+{
+    imageByIndex(imageIndex).mayHavePlusLoads = value;
+}
+
+void ImageGroupWriter::setImageNeverUnload(uint32_t imageIndex, bool value)
+{
+    imageByIndex(imageIndex).neverUnload = value;
+}
+
+void ImageGroupWriter::setImageMustBeThisDir(uint32_t imageIndex, bool value)
+{
+    imageByIndex(imageIndex).cwdSameAsThis = value;
+}
+
+void ImageGroupWriter::setImageIsPlatformBinary(uint32_t imageIndex, bool value)
+{
+    imageByIndex(imageIndex).isPlatformBinary = value;
+}
+
+void ImageGroupWriter::setImageOverridableDylib(uint32_t imageIndex, bool value)
+{
+    imageByIndex(imageIndex).overridableDylib = value;
+}
+
+void ImageGroupWriter::setImageFileMtimeAndInode(uint32_t imageIndex, uint64_t mTime, uint64_t inode)
+{
+    imageByIndex(imageIndex).fileInfo.statInfo.mtime = mTime;
+    imageByIndex(imageIndex).fileInfo.statInfo.inode = inode;
+    assert(!_imageFileInfoIsCdHash);
+}
+
+void ImageGroupWriter::setImageCdHash(uint32_t imageIndex, uint8_t cdHash[20])
+{
+    memcpy(imageByIndex(imageIndex).fileInfo.cdHash16.bytes, cdHash, 16);
+    assert(_imageFileInfoIsCdHash);
+}
+
+void ImageGroupWriter::setImageIsEncrypted(uint32_t imageIndex, bool value)
+{
+    imageByIndex(imageIndex).isEncrypted = value;
+}
+
+void ImageGroupWriter::setImageMaxLoadCount(uint32_t imageIndex, uint32_t count)
+{
+    imageByIndex(imageIndex).maxLoadCount = count;
+}
+
+void ImageGroupWriter::setImageFairPlayRange(uint32_t imageIndex, uint32_t offset, uint32_t size)
+{
+    assert(imageIndex < imageCount());
+    assert(_isDiskImage);
+    binary_format::DiskImage& image = _diskImages[imageIndex];
+    if ( image.has16KBpages ) {
+        assert((offset & 0x3FFF) == 0);
+        assert((size & 0x3FFF) == 0);
+    }
+    else {
+        assert((offset & 0xFFF) == 0);
+        assert((size & 0xFFF) == 0);
+    }
+    assert(offset < (_pageSize*16));
+    image.fairPlayTextStartPage = offset / _pageSize;
+    image.fairPlayTextPageCount = size / _pageSize;
+}
+
+void ImageGroupWriter::setImageInitializerOffsets(uint32_t imageIndex, const std::vector<uint32_t>& offsets)
+{
+    binary_format::Image& image      = imageByIndex(imageIndex);
+    image.initOffsetsArrayStartIndex = _initializerOffsets.size();
+    image.initOffsetsArrayCount      = offsets.size();
+    _initializerOffsets.insert(_initializerOffsets.end(), offsets.begin(), offsets.end());
+}
+
+void ImageGroupWriter::setImageDOFOffsets(uint32_t imageIndex, const std::vector<uint32_t>& offsets)
+{
+    binary_format::Image& image      = imageByIndex(imageIndex);
+    image.dofOffsetsArrayStartIndex  = _dofOffsets.size();
+    image.dofOffsetsArrayCount       = offsets.size();
+    _dofOffsets.insert(_dofOffsets.end(), offsets.begin(), offsets.end());
+}
+
+uint32_t ImageGroupWriter::addUniqueInitList(const std::vector<binary_format::ImageRef>& initBefore)
+{
+    // see if this initBefore list already exists in pool
+    if ( _initializerBeforeLists.size() > initBefore.size() ) {
+        size_t cmpLen = initBefore.size()*sizeof(binary_format::ImageRef);
+        size_t end = _initializerBeforeLists.size() - initBefore.size();
+        for (uint32_t i=0; i < end; ++i) {
+            if ( memcmp(&initBefore[0], &_initializerBeforeLists[i], cmpLen) == 0 ) {
+                return i;
+            }
+        }
+    }
+    uint32_t result = (uint32_t)_initializerBeforeLists.size();
+    _initializerBeforeLists.insert(_initializerBeforeLists.end(), initBefore.begin(), initBefore.end());
+    return result;
+}
+
+void ImageGroupWriter::setImageInitBefore(uint32_t imageIndex, const std::vector<binary_format::ImageRef>& initBefore)
+{
+    binary_format::Image& image = imageByIndex(imageIndex);
+    image.initBeforeArrayStartIndex = addUniqueInitList(initBefore);
+    image.initBeforeArrayCount = initBefore.size();
+}
+
+void ImageGroupWriter::setImageSliceOffset(uint32_t imageIndex, uint64_t fileOffset)
+{
+    assert(imageIndex < imageCount());
+    assert(_isDiskImage);
+    binary_format::DiskImage& image = _diskImages[imageIndex];
+    image.sliceOffsetIn4K = (uint32_t)(fileOffset / 4096);
+}
+
+void ImageGroupWriter::setImageCodeSignatureLocation(uint32_t imageIndex, uint32_t fileOffset, uint32_t size)
+{
+    assert(imageIndex < imageCount());
+    assert(_isDiskImage);
+    binary_format::DiskImage& image = _diskImages[imageIndex];
+    image.codeSignFileOffset = fileOffset;
+    image.codeSignFileSize = size;
+}
+
+void ImageGroupWriter::setImageDependentsCount(uint32_t imageIndex, uint32_t count)
+{
+    binary_format::Image& image = imageByIndex(imageIndex);
+    image.dependentsArrayStartIndex = _dependentsPool.size();
+    image.dependentsArrayCount = count;
+    _dependentsPool.resize(_dependentsPool.size() + count);
+}
+
+void ImageGroupWriter::setImageDependent(uint32_t imageIndex, uint32_t depIndex, binary_format::ImageRef dependent)
+{
+    binary_format::Image& image = imageByIndex(imageIndex);
+    assert(depIndex < image.dependentsArrayCount);
+    _dependentsPool[image.dependentsArrayStartIndex + depIndex] = dependent;
+}
+
+uint32_t ImageGroupWriter::imageDependentsCount(uint32_t imageIndex) const
+{
+    return imageByIndex(imageIndex).dependentsArrayCount;
+}
+
+binary_format::ImageRef ImageGroupWriter::imageDependent(uint32_t imageIndex, uint32_t depIndex) const
+{
+    const binary_format::Image& image = imageByIndex(imageIndex);
+    assert(depIndex < image.dependentsArrayCount);
+    return _dependentsPool[image.dependentsArrayStartIndex + depIndex];
+}
+
+void ImageGroupWriter::setImageSegments(uint32_t imageIndex, MachOParser& imageParser, uint64_t cacheUnslideBaseAddress)
+{
+    if ( _isDiskImage ) {
+        __block uint32_t totalPageCount    = 0;
+        __block uint32_t lastFileOffsetEnd = 0;
+        __block uint64_t lastVmAddrEnd     = 0;
+        __block std::vector<binary_format::DiskSegment> diskSegments;
+        diskSegments.reserve(8);
+        imageParser.forEachSegment(^(const char* segName, uint32_t fileOffset, uint32_t fileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, bool& stop) {
+            if ( (fileOffset != 0) && (fileOffset != lastFileOffsetEnd) ) {
+                binary_format::DiskSegment filePadding;
+                filePadding.filePageCount   = (fileOffset - lastFileOffsetEnd)/_pageSize;
+                filePadding.vmPageCount     = 0;
+                filePadding.permissions     = 0;
+                filePadding.paddingNotSeg   = 1;
+                diskSegments.push_back(filePadding);
+            }
+            if ( (lastVmAddrEnd != 0) && (vmAddr != lastVmAddrEnd) ) {
+                binary_format::DiskSegment vmPadding;
+                vmPadding.filePageCount   = 0;
+                vmPadding.vmPageCount     = (vmAddr - lastVmAddrEnd)/_pageSize;
+                vmPadding.permissions     = 0;
+                vmPadding.paddingNotSeg   = 1;
+                diskSegments.push_back(vmPadding);
+                totalPageCount += vmPadding.vmPageCount;
+            }
+            {
+                binary_format::DiskSegment segInfo;
+                segInfo.filePageCount   = (fileSize+_pageSize-1)/_pageSize;
+                segInfo.vmPageCount     = (vmSize+_pageSize-1)/_pageSize;
+                segInfo.permissions     = protections & 7;
+                segInfo.paddingNotSeg   = 0;
+                diskSegments.push_back(segInfo);
+                totalPageCount   += segInfo.vmPageCount;
+                if ( fileSize != 0 )
+                    lastFileOffsetEnd = fileOffset + fileSize;
+                if ( vmSize != 0 )
+                    lastVmAddrEnd     = vmAddr + vmSize;
+            }
+        });
+        binary_format::Image& image   = imageByIndex(imageIndex);
+        image.segmentsArrayStartIndex = _segmentPool.size();
+        image.segmentsArrayCount      = diskSegments.size();
+        _segmentPool.insert(_segmentPool.end(), (uint64_t*)&diskSegments[0], (uint64_t*)&diskSegments[image.segmentsArrayCount]);
+        _diskImages[imageIndex].totalVmPages = totalPageCount;
+    }
+    else {
+        binary_format::Image& image   = imageByIndex(imageIndex);
+        image.segmentsArrayStartIndex = _segmentPool.size();
+        image.segmentsArrayCount      = imageParser.segmentCount();
+        _segmentPool.resize(_segmentPool.size() + image.segmentsArrayCount);
+        __block uint32_t segIndex = 0;
+        imageParser.forEachSegment(^(const char* segName, uint32_t fileOffset, uint32_t fileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, bool& stop) {
+            binary_format::DyldCacheSegment seg = { (uint32_t)(vmAddr-cacheUnslideBaseAddress), (uint32_t)vmSize, protections };
+            _segmentPool[image.segmentsArrayStartIndex + segIndex] = *((uint64_t*)&seg);
+            ++segIndex;
+        });
+    }
+}
+
+void ImageGroupWriter::setImagePatchLocations(uint32_t imageIndex, uint32_t funcVmOffset, const std::unordered_set<uint32_t>& patchLocations)
+{
+    assert(imageIndex < imageCount());
+    binary_format::CachedImage& image = _images[imageIndex];
+    if ( image.patchStartIndex == 0 ) {
+        image.patchStartIndex = (uint32_t)_patchPool.size();
+        image.patchCount      = 0;
+    }
+    else {
+        assert(image.patchStartIndex + image.patchCount == _patchPool.size());
+    }
+
+    binary_format::PatchTable entry = { funcVmOffset, (uint32_t)_patchLocationPool.size() };
+    for (uint32_t loc : patchLocations) {
+        _patchLocationPool.push_back(*((binary_format::PatchOffset*)&loc));
+    }
+    _patchLocationPool.back().last = true;
+    _patchPool.push_back(entry);
+    _images[imageIndex].patchCount++;
+}
+
+void ImageGroupWriter::setGroupCacheOverrides(const std::vector<binary_format::DyldCacheOverride>& cacheOverrides)
+{
+    _dyldCacheSymbolOverridePool = cacheOverrides;
+}
+
+void ImageGroupWriter::addImageIsOverride(binary_format::ImageRef standardDylibRef, binary_format::ImageRef overrideDylibRef)
+{
+    _imageOverridePool.push_back({standardDylibRef, overrideDylibRef});
+}
+
+
+class SegmentFixUpBuilder
+{
+public:
+                            SegmentFixUpBuilder(uint32_t segIndex, uint32_t dataSegPageCount, uint32_t pageSize, bool is64,
+                                                    const std::vector<ImageGroupWriter::FixUp>& fixups,
+                                                    std::vector<TargetSymbolValue>& targetsForImage, bool log);
+
+    bool                    hasFixups() { return _hasFixups; }
+    uint32_t                segIndex() { return _segIndex; }
+    void                    appendSegmentFixUpMap(ContentBuffer&);
+
+private:
+    struct TmpOpcode {
+        binary_format::FixUpOpcode    op;
+        uint8_t                       repeatOpcodeCount;
+        uint16_t                      count;
+
+         bool operator!=(const TmpOpcode& rhs) const {
+            return ((op != rhs.op) || (count != rhs.count) || (repeatOpcodeCount != rhs.repeatOpcodeCount));
+        }
+    };
+
+
+    ContentBuffer           makeFixupOpcodesForPage(uint32_t pageStartSegmentOffset, const ImageGroupWriter::FixUp* start,
+                                                    const ImageGroupWriter::FixUp* end);
+    uint32_t                getOrdinalForTarget(TargetSymbolValue);
+    void                    expandOpcodes(const std::vector<TmpOpcode>& opcodes, uint8_t page[0x4000],  uint32_t& offset, uint32_t& ordinal);
+    void                    expandOpcodes(const std::vector<TmpOpcode>& opcodes, uint8_t page[0x4000]);
+    bool                    samePageContent(const uint8_t page1[], const uint8_t page2[]);
+    void                    printOpcodes(const char* prefix, const std::vector<TmpOpcode> opcodes);
+    void                    printOpcodes(const char* prefix, bool printOffset, const TmpOpcode opcodes[], size_t opcodesLen, uint32_t& offset);
+    uint32_t                opcodeEncodingSize(const std::vector<TmpOpcode>& opcodes);
+
+    const bool                              _is64;
+    const bool                              _log;
+    bool                                    _hasFixups;
+    const uint32_t                          _segIndex;
+    const uint32_t                          _dataSegPageCount;
+    const uint32_t                          _pageSize;
+    std::vector<TargetSymbolValue>&         _targets;
+    std::vector<ContentBuffer>              _opcodesByPage;
+};
+
+
+
+
+SegmentFixUpBuilder::SegmentFixUpBuilder(uint32_t segIndex, uint32_t segPageCount, uint32_t pageSize, bool is64,
+                                         const std::vector<ImageGroupWriter::FixUp>& fixups,
+                                         std::vector<TargetSymbolValue>& targetsForImage, bool log)
+    : _is64(is64), _log(log), _hasFixups(false), _segIndex(segIndex), _dataSegPageCount(segPageCount), _pageSize(pageSize), _targets(targetsForImage)
+{
+    //fprintf(stderr, "SegmentFixUpBuilder(segIndex=%d, segPageCount=%d)\n", segIndex, segPageCount);
+    _targets.push_back(TargetSymbolValue::makeInvalid()); // ordinal zero reserved to mean "add slide"
+    _opcodesByPage.resize(segPageCount);
+    size_t startFixupIndex = 0;
+    for (uint32_t pageIndex=0; pageIndex < segPageCount; ++pageIndex) {
+        uint32_t pageStartOffset = pageIndex*_pageSize;
+        uint32_t pageEndOffset   = pageStartOffset+_pageSize;
+        // find first index in this page
+        while ( (startFixupIndex < fixups.size()) && ((fixups[startFixupIndex].segIndex != segIndex) || (fixups[startFixupIndex].segOffset < pageStartOffset))  )
+            ++startFixupIndex;
+        // find first index beyond this page
+        size_t endFixupIndex = startFixupIndex;
+        while ( (endFixupIndex < fixups.size()) && (fixups[endFixupIndex].segIndex == segIndex) && (fixups[endFixupIndex].segOffset < pageEndOffset) )
+            ++endFixupIndex;
+        // create opcodes for fixups on this pageb
+        _opcodesByPage[pageIndex] = makeFixupOpcodesForPage(pageStartOffset, &fixups[startFixupIndex], &fixups[endFixupIndex]);
+        startFixupIndex = endFixupIndex;
+    }
+}
+
+
+uint32_t SegmentFixUpBuilder::getOrdinalForTarget(TargetSymbolValue target)
+{
+    uint32_t ordinal = 0;
+    for (const TargetSymbolValue& entry : _targets) {
+        if ( entry == target )
+            return ordinal;
+        ++ordinal;
+    }
+    _targets.push_back(target);
+    return ordinal;
+}
+
+void SegmentFixUpBuilder::appendSegmentFixUpMap(ContentBuffer& buffer)
+{
+    std::vector<uint32_t> offsets;
+    uint32_t curOffset = sizeof(binary_format::SegmentFixupsByPage)-4 + _dataSegPageCount*4;
+    for (auto& opcodes : _opcodesByPage) {
+        if ( opcodes.size() == 0 )
+            offsets.push_back(0);
+        else
+            offsets.push_back(curOffset);
+        curOffset += opcodes.size();
+    }
+    uint32_t totalSize = curOffset;
+
+    // write header
+    buffer.append_uint32(totalSize);                    // SegmentFixupsByPage.size
+    buffer.append_uint32(_pageSize);                    // SegmentFixupsByPage.pageSize
+    buffer.append_uint32(_dataSegPageCount);            // SegmentFixupsByPage.pageCount
+    for (uint32_t i=0; i < _dataSegPageCount; ++i) {
+        buffer.append_uint32(offsets[i]);               // SegmentFixupsByPage.pageInfoOffsets[i]
+    }
+    // write each page's opcode stream
+    for (uint32_t i=0; i < offsets.size(); ++i) {
+        buffer.append_buffer(_opcodesByPage[i]);
+    }
+}
+
+void SegmentFixUpBuilder::expandOpcodes(const std::vector<TmpOpcode>& opcodes, uint8_t page[])
+{
+    uint32_t offset = 0;
+    uint32_t ordinal = 0;
+    bzero(page, _pageSize);
+    expandOpcodes(opcodes, page, offset, ordinal);
+}
+
+void SegmentFixUpBuilder::expandOpcodes(const std::vector<TmpOpcode>& opcodes, uint8_t page[], uint32_t& offset, uint32_t& ordinal)
+{
+    for (int i=0; i < opcodes.size(); ++i) {
+        assert(offset < _pageSize);
+        TmpOpcode tmp = opcodes[i];
+        switch ( tmp.op ) {
+            case binary_format::FixUpOpcode::bind64:
+                *(uint64_t*)(&page[offset]) = ordinal;
+                offset += 8;
+                break;
+            case binary_format::FixUpOpcode::bind32:
+                *(uint32_t*)(&page[offset]) = ordinal;
+                offset += 4;
+                break;
+            case binary_format::FixUpOpcode::rebase64:
+                *(uint64_t*)(&page[offset]) = 0x1122334455667788;
+                offset += 8;
+                break;
+            case binary_format::FixUpOpcode::rebase32:
+                *(uint32_t*)(&page[offset]) = 0x23452345;
+                offset += 4;
+                break;
+            case binary_format::FixUpOpcode::rebaseText32:
+                *(uint32_t*)(&page[offset]) = 0x56785678;
+                offset += 4;
+                break;
+            case binary_format::FixUpOpcode::bindText32:
+                *(uint32_t*)(&page[offset]) = 0x98769876;
+                offset += 4;
+                break;
+            case binary_format::FixUpOpcode::bindTextRel32:
+                *(uint32_t*)(&page[offset]) = 0x34563456;
+                offset += 4;
+                break;
+            case binary_format::FixUpOpcode::bindImportJmp32:
+                *(uint32_t*)(&page[offset]) = 0x44556677;
+                offset += 4;
+                break;
+            case binary_format::FixUpOpcode::done:
+                break;
+            case binary_format::FixUpOpcode::setPageOffset:
+                offset = tmp.count;
+                break;
+            case binary_format::FixUpOpcode::incPageOffset:
+                offset += (tmp.count*4);
+                break;
+            case binary_format::FixUpOpcode::setOrdinal:
+                ordinal = tmp.count;
+                break;
+            case binary_format::FixUpOpcode::incOrdinal:
+                ++ordinal;
+                break;
+            case binary_format::FixUpOpcode::repeat: {
+                    std::vector<TmpOpcode> pattern;
+                    for (int j=0; j < tmp.repeatOpcodeCount; ++j) {
+                        pattern.push_back(opcodes[i+j+1]);
+                    }
+                    for (int j=0; j < tmp.count; ++j) {
+                        expandOpcodes(pattern, page, offset, ordinal);
+                    }
+                    i += tmp.repeatOpcodeCount;
+                }
+                break;
+        }
+    }
+}
+
+
+
+uint32_t SegmentFixUpBuilder::opcodeEncodingSize(const std::vector<TmpOpcode>& opcodes)
+{
+    uint32_t size = 0;
+    for (int i=0; i < opcodes.size(); ++i) {
+        switch ( opcodes[i].op ) {
+            case binary_format::FixUpOpcode::bind64:
+            case binary_format::FixUpOpcode::bind32:
+            case binary_format::FixUpOpcode::rebase64:
+            case binary_format::FixUpOpcode::rebase32:
+            case binary_format::FixUpOpcode::rebaseText32:
+            case binary_format::FixUpOpcode::bindText32:
+            case binary_format::FixUpOpcode::bindTextRel32:
+            case binary_format::FixUpOpcode::bindImportJmp32:
+            case binary_format::FixUpOpcode::done:
+                ++size;
+                break;
+            case binary_format::FixUpOpcode::setPageOffset:
+            case binary_format::FixUpOpcode::incPageOffset:
+            case binary_format::FixUpOpcode::setOrdinal:
+            case binary_format::FixUpOpcode::incOrdinal:
+                ++size;
+                if ( opcodes[i].count >= 16 )
+                    size += ContentBuffer::uleb128_size(opcodes[i].count);
+                break;
+            case binary_format::FixUpOpcode::repeat: {
+                    ++size;
+                    size += ContentBuffer::uleb128_size(opcodes[i].count);
+                    std::vector<TmpOpcode> pattern;
+                    for (int j=0; j < opcodes[i].repeatOpcodeCount; ++j) {
+                        pattern.push_back(opcodes[++i]);
+                    }
+                    size += opcodeEncodingSize(pattern);
+                }
+                break;
+        }
+    }
+    return size;
+}
+
+
+bool SegmentFixUpBuilder::samePageContent(const uint8_t page1[], const uint8_t page2[])
+{
+    bool result = true;
+    if (memcmp(page1, page2, _pageSize) != 0) {
+        if ( _is64 ) {
+            const uint64_t* p1 = (uint64_t* )page1;
+            const uint64_t* p2 = (uint64_t* )page2;
+            for (int i=0; i < _pageSize/8; ++i) {
+                if ( p1[i] != p2[i] ) {
+                    fprintf(stderr, "page1[0x%03X] = 0x%016llX, page2[0x%03X] = 0x%016llX\n", i*8, p1[i], i*8, p2[i]);
+                    result = false;
+                }
+            }
+        }
+        else {
+            const uint32_t* p1 = (uint32_t* )page1;
+            const uint32_t* p2 = (uint32_t* )page2;
+            for (int i=0; i < _pageSize/4; ++i) {
+                if ( p1[i] != p2[i] ) {
+                    fprintf(stderr, "page1[0x%03X] = 0x%016X, page2[0x%03X] = 0x%016X\n", i*4, p1[i], i*4, p2[i]);
+                    result = false;
+                }
+            }
+        }
+    }
+    return result;
+}
+
+void SegmentFixUpBuilder::printOpcodes(const char* prefix, const std::vector<TmpOpcode> opcodes)
+{
+    uint32_t offset = 0;
+    printOpcodes(prefix, true, &opcodes[0], opcodes.size(), offset);
+}
+
+void SegmentFixUpBuilder::printOpcodes(const char* prefix, bool printOffset, const TmpOpcode opcodes[], size_t opcodesLen, uint32_t& offset)
+{
+    for (int i=0; i < opcodesLen; ++i) {
+        TmpOpcode tmp = opcodes[i];
+        if ( printOffset )
+            fprintf(stderr, "%s offset=0x%04X: ", prefix, offset);
+        else
+            fprintf(stderr, "%s               ", prefix);
+        switch ( tmp.op ) {
+            case binary_format::FixUpOpcode::bind64:
+                fprintf(stderr, "bind64\n");
+                offset += 8;
+                break;
+            case binary_format::FixUpOpcode::bind32:
+                fprintf(stderr, "bind32\n");
+                offset += 4;
+                break;
+            case binary_format::FixUpOpcode::rebase64:
+                fprintf(stderr, "rebase64\n");
+                offset += 8;
+                break;
+            case binary_format::FixUpOpcode::rebase32:
+                fprintf(stderr, "rebase32\n");
+                offset += 4;
+                break;
+            case binary_format::FixUpOpcode::rebaseText32:
+                fprintf(stderr, "rebaseText32\n");
+                offset += 4;
+                break;
+            case binary_format::FixUpOpcode::bindText32:
+                fprintf(stderr, "bindText32\n");
+                offset += 4;
+                break;
+            case binary_format::FixUpOpcode::bindTextRel32:
+                fprintf(stderr, "bindTextRel32\n");
+                offset += 4;
+                break;
+           case binary_format::FixUpOpcode::bindImportJmp32:
+                fprintf(stderr, "bindJmpRel32\n");
+                offset += 4;
+                break;
+            case binary_format::FixUpOpcode::done:
+                fprintf(stderr, "done\n");
+                break;
+            case binary_format::FixUpOpcode::setPageOffset:
+                fprintf(stderr, "setPageOffset(%d)\n", tmp.count);
+                offset = tmp.count;
+                break;
+            case binary_format::FixUpOpcode::incPageOffset:
+                fprintf(stderr, "incPageOffset(%d)\n", tmp.count);
+                offset += (tmp.count*4);
+                break;
+            case binary_format::FixUpOpcode::setOrdinal:
+                fprintf(stderr, "setOrdinal(%d)\n", tmp.count);
+                break;
+            case binary_format::FixUpOpcode::incOrdinal:
+                fprintf(stderr, "incOrdinal(%d)\n", tmp.count);
+                break;
+            case binary_format::FixUpOpcode::repeat: {
+                    char morePrefix[128];
+                    strcpy(morePrefix, prefix);
+                    strcat(morePrefix, "          ");
+                    uint32_t prevOffset = offset;
+                    fprintf(stderr, "repeat(%d times, next %d opcodes)\n", tmp.count, tmp.repeatOpcodeCount);
+                    printOpcodes(morePrefix, false, &opcodes[i+1], tmp.repeatOpcodeCount, offset);
+                    i += tmp.repeatOpcodeCount;
+                    uint32_t repeatDelta = (offset-prevOffset)*(tmp.count-1);
+                    offset += repeatDelta;
+                }
+                break;
+        }
+    }
+}
+
+ContentBuffer SegmentFixUpBuilder::makeFixupOpcodesForPage(uint32_t pageStartSegmentOffset, const ImageGroupWriter::FixUp* start, const ImageGroupWriter::FixUp* end)
+{
+    //fprintf(stderr, "  makeFixupOpcodesForPage(segOffset=0x%06X, startFixup=%p, endFixup=%p)\n", pageStartSegmentOffset, start, end);
+    std::vector<TmpOpcode> tmpOpcodes;
+    const uint32_t pointerSize = (_is64 ? 8 : 4);
+    uint32_t offset = pageStartSegmentOffset;
+    uint32_t ordinal = 0;
+    const ImageGroupWriter::FixUp* lastFixup = nullptr;
+    for (const ImageGroupWriter::FixUp* f=start; f < end; ++f) {
+        // ignore double bind at same address (ld64 bug)
+        if ( lastFixup && (lastFixup->segOffset == f->segOffset) )
+            continue;
+        // add opcode to adjust current offset if needed
+        if ( f->segOffset != offset ) {
+            if ( ((f->segOffset % 4) != 0) || ((offset % 4) != 0) ) {
+                // mis aligned pointers use bigger set opcode
+                tmpOpcodes.push_back({binary_format::FixUpOpcode::setPageOffset, 0, (uint16_t)(f->segOffset-pageStartSegmentOffset)});
+            }
+            else {
+                uint32_t delta4 = (uint32_t)(f->segOffset - offset)/4;
+                assert(delta4*4 < _pageSize);
+                tmpOpcodes.push_back({binary_format::FixUpOpcode::incPageOffset, 0, (uint16_t)delta4});
+            }
+            offset = (uint32_t)f->segOffset;
+        }
+        uint32_t nextOrd = 0;
+        switch ( f->type ) {
+            case ImageGroupWriter::FixupType::rebase:
+                tmpOpcodes.push_back({_is64 ? binary_format::FixUpOpcode::rebase64 : binary_format::FixUpOpcode::rebase32, 0, 0});
+                offset += pointerSize;
+                _hasFixups = true;
+                break;
+            case ImageGroupWriter::FixupType::pointerLazyBind:
+            case ImageGroupWriter::FixupType::pointerBind:
+                //assert(f->target.imageIndex == binary_format::OrdinalEntry::kImageIndexDyldSharedCache);
+                nextOrd = getOrdinalForTarget(f->target);
+                if ( nextOrd != ordinal ) {
+                    if ( (nextOrd > ordinal) && (nextOrd < (ordinal+31)) ) {
+                        tmpOpcodes.push_back({binary_format::FixUpOpcode::incOrdinal, 0, (uint16_t)(nextOrd-ordinal)});
+                    }
+                    else {
+                        tmpOpcodes.push_back({binary_format::FixUpOpcode::setOrdinal, 0, (uint16_t)nextOrd});
+                    }
+                    ordinal = nextOrd;
+                }
+                tmpOpcodes.push_back({_is64 ? binary_format::FixUpOpcode::bind64 : binary_format::FixUpOpcode::bind32, 0, 0});
+                offset += pointerSize;
+                 _hasFixups = true;
+               break;
+            case ImageGroupWriter::FixupType::rebaseText:
+                assert(!_is64);
+                tmpOpcodes.push_back({binary_format::FixUpOpcode::rebaseText32, 0, 0});
+                offset += pointerSize;
+                _hasFixups = true;
+                break;
+            case ImageGroupWriter::FixupType::bindText:
+                assert(!_is64);
+                 nextOrd = getOrdinalForTarget(f->target);
+                if ( nextOrd != ordinal ) {
+                    if ( (nextOrd > ordinal) && (nextOrd < (ordinal+31)) ) {
+                        tmpOpcodes.push_back({binary_format::FixUpOpcode::incOrdinal, 0, (uint16_t)(nextOrd-ordinal)});
+                    }
+                    else {
+                        tmpOpcodes.push_back({binary_format::FixUpOpcode::setOrdinal, 0, (uint16_t)nextOrd});
+                    }
+                    ordinal = nextOrd;
+                }
+                tmpOpcodes.push_back({binary_format::FixUpOpcode::bindText32, 0, 0});
+                offset += pointerSize;
+                _hasFixups = true;
+                break;
+            case ImageGroupWriter::FixupType::bindTextRel:
+                assert(!_is64);
+                nextOrd = getOrdinalForTarget(f->target);
+                if ( nextOrd != ordinal ) {
+                    if ( (nextOrd > ordinal) && (nextOrd < (ordinal+31)) ) {
+                        tmpOpcodes.push_back({binary_format::FixUpOpcode::incOrdinal, 0, (uint16_t)(nextOrd-ordinal)});
+                    }
+                    else {
+                        tmpOpcodes.push_back({binary_format::FixUpOpcode::setOrdinal, 0, (uint16_t)nextOrd});
+                    }
+                    ordinal = nextOrd;
+                }
+                tmpOpcodes.push_back({binary_format::FixUpOpcode::bindTextRel32, 0, 0});
+                offset += pointerSize;
+                _hasFixups = true;
+                break;
+            case ImageGroupWriter::FixupType::bindImportJmpRel:
+                assert(!_is64);
+                nextOrd = getOrdinalForTarget(f->target);
+                if ( nextOrd != ordinal ) {
+                    if ( (nextOrd > ordinal) && (nextOrd < (ordinal+31)) ) {
+                        tmpOpcodes.push_back({binary_format::FixUpOpcode::incOrdinal, 0, (uint16_t)(nextOrd-ordinal)});
+                    }
+                    else {
+                        tmpOpcodes.push_back({binary_format::FixUpOpcode::setOrdinal, 0, (uint16_t)nextOrd});
+                    }
+                    ordinal = nextOrd;
+                }
+                tmpOpcodes.push_back({binary_format::FixUpOpcode::bindImportJmp32, 0, 0});
+                offset += pointerSize;
+                _hasFixups = true;
+                break;
+            case ImageGroupWriter::FixupType::ignore:
+                assert(0 && "ignore fixup types should have been removed");
+                break;
+        }
+        lastFixup = f;
+    }
+
+    uint8_t firstExpansion[0x4010]; // larger than 16KB to handle unaligned pointers
+    expandOpcodes(tmpOpcodes, firstExpansion);
+
+    if (_log) printOpcodes("start", tmpOpcodes);
+
+
+    for (int stride=1; stride < 6; ++stride) {
+        for (int i=0; i < tmpOpcodes.size(); ++i) {
+            int j;
+            for (j=i+stride; j < tmpOpcodes.size(); j += stride) {
+                bool strideMatch = true;
+                for (int k=0; k < stride; ++k) {
+                    if ( (j+k >= tmpOpcodes.size()) || (tmpOpcodes[j+k] != tmpOpcodes[i+k]) ) {
+                        strideMatch = false;
+                        break;
+                    }
+                    if ( (tmpOpcodes[j+k].op == binary_format::FixUpOpcode::repeat) && (tmpOpcodes[j+k].repeatOpcodeCount+k >= stride) ) {
+                        strideMatch = false;
+                        break;
+                    }
+                }
+                if ( !strideMatch )
+                    break;
+            }
+            // see if same opcode repeated three or more times
+            int repeats = (j-i)/stride;
+            if ( repeats > 3 ) {
+                // replace run with repeat opcode
+                tmpOpcodes[i].op                = binary_format::FixUpOpcode::repeat;
+                tmpOpcodes[i].repeatOpcodeCount = stride;
+                tmpOpcodes[i].count             = repeats;
+                tmpOpcodes.erase(tmpOpcodes.begin()+i+1, tmpOpcodes.begin()+j-stride);
+                i += stride;
+            }
+            else {
+                // don't look for matches inside a repeat loop
+                if ( tmpOpcodes[i].op == binary_format::FixUpOpcode::repeat )
+                    i += tmpOpcodes[i].repeatOpcodeCount;
+            }
+        }
+        if (_log) {
+            char tmp[32];
+            sprintf(tmp, "stride %d", stride);
+            printOpcodes(tmp, tmpOpcodes);
+        }
+        uint8_t secondExpansion[0x4010];
+        expandOpcodes(tmpOpcodes, secondExpansion);
+        if ( !samePageContent(firstExpansion, secondExpansion) )
+            printOpcodes("opt", tmpOpcodes);
+    }
+
+    // convert temp opcodes to real opcodes
+    bool wroteDone = false;
+    ContentBuffer opcodes;
+    for (const TmpOpcode& tmp : tmpOpcodes) {
+        switch ( tmp.op ) {
+            case binary_format::FixUpOpcode::bind64:
+            case binary_format::FixUpOpcode::bind32:
+            case binary_format::FixUpOpcode::rebase64:
+            case binary_format::FixUpOpcode::rebase32:
+            case binary_format::FixUpOpcode::rebaseText32:
+            case binary_format::FixUpOpcode::bindText32:
+            case binary_format::FixUpOpcode::bindTextRel32:
+            case binary_format::FixUpOpcode::bindImportJmp32:
+                opcodes.append_byte((uint8_t)tmp.op);
+                break;
+            case binary_format::FixUpOpcode::done:
+                opcodes.append_byte((uint8_t)tmp.op);
+                wroteDone = true;
+                break;
+            case binary_format::FixUpOpcode::setPageOffset:
+            case binary_format::FixUpOpcode::incPageOffset:
+            case binary_format::FixUpOpcode::setOrdinal:
+            case binary_format::FixUpOpcode::incOrdinal:
+                if ( (tmp.count > 0) && (tmp.count < 16) ) {
+                    opcodes.append_byte((uint8_t)tmp.op | tmp.count);
+                }
+                else {
+                    opcodes.append_byte((uint8_t)tmp.op);
+                    opcodes.append_uleb128(tmp.count);
+                }
+                break;
+            case binary_format::FixUpOpcode::repeat: {
+                    const TmpOpcode* nextOpcodes = &tmp;
+                    ++nextOpcodes;
+                    std::vector<TmpOpcode> pattern;
+                    for (int i=0; i < tmp.repeatOpcodeCount; ++i) {
+                        pattern.push_back(nextOpcodes[i]);
+                    }
+                    uint32_t repeatBytes = opcodeEncodingSize(pattern);
+                    assert(repeatBytes < 15);
+                    opcodes.append_byte((uint8_t)tmp.op | repeatBytes);
+                    opcodes.append_uleb128(tmp.count);
+                }
+                break;
+        }
+    }
+
+    if ( (opcodes.size() == 0) || !wroteDone )
+        opcodes.append_byte((uint8_t)binary_format::FixUpOpcode::done);
+
+    // make opcodes streams 4-byte aligned
+    opcodes.pad_to_size(4);
+
+    //fprintf(stderr, "  makeFixupOpcodesForPage(pageStartSegmentOffset=0x%0X) result=%lu bytes\n", pageStartSegmentOffset, opcodes.size());
+
+    return opcodes;
+}
+
+
+
+
+void ImageGroupWriter::setImageFixups(Diagnostics& diag, uint32_t imageIndex, std::vector<FixUp>& fixups, bool hasTextRelocs)
+{
+    // only applicable for ImageGroup in a closure (not group of images in dyld cache)
+    assert(_isDiskImage);
+
+    // sort all rebases and binds by address
+    std::sort(fixups.begin(), fixups.end(), [](FixUp& lhs, FixUp& rhs) -> bool {
+        if ( &lhs == &rhs )
+            return false;
+        // sort by segIndex
+        if ( lhs.segIndex < rhs.segIndex )
+            return true;
+        if ( lhs.segIndex > rhs.segIndex )
+            return false;
+        // then sort by segOffset
+        if ( lhs.segOffset < rhs.segOffset )
+            return true;
+        if ( lhs.segOffset > rhs.segOffset )
+            return false;
+        // two fixups at same location
+
+        // if the same (linker bug), ignore one
+        if ( lhs.type == rhs.type ) {
+            rhs.type = FixupType::ignore;
+        }
+        // if one is rebase for lazy pointer, ignore rebase because dyld3 does not lazy bind
+        else if ( (lhs.type == FixupType::pointerLazyBind) && (rhs.type == FixupType::rebase) ) {
+            // lazy pointers have rebase and (lazy) bind at same location.  since dyld3 does not do lazy binding, we mark the rebase to be ignored later
+            rhs.type = FixupType::ignore;
+        }
+        else if ( (rhs.type == FixupType::pointerLazyBind) && (lhs.type == FixupType::rebase) ) {
+            // lazy pointers have rebase and (lazy) bind at same location.  since dyld3 does not do lazy binding, we mark the rebase to be ignored later
+            lhs.type = FixupType::ignore;
+        }
+        return (lhs.type < rhs.type);
+    });
+
+    // remove ignoreable fixups
+    fixups.erase(std::remove_if(fixups.begin(), fixups.end(),
+                                [&](const FixUp& a) {
+                                    return (a.type == FixupType::ignore);
+                                }), fixups.end());
+
+    // look for overlapping fixups
+    const uint32_t pointerSize = (_is64 ? 8 : 4);
+    const FixUp* lastFixup = nullptr;
+    for (const FixUp& fixup : fixups) {
+        if ( lastFixup != nullptr ) {
+            if ( lastFixup->segIndex == fixup.segIndex ) {
+                uint64_t increment = fixup.segOffset - lastFixup->segOffset;
+                if ( increment < pointerSize ) {
+                    if ( (increment == 0) && ((lastFixup->type == FixupType::ignore) || (fixup.type == FixupType::ignore))  ) {
+                        // allow rebase to local lazy helper and lazy bind to same location
+                    }
+                    else {
+                        diag.error("segment %d has overlapping fixups at offset 0x%0llX and 0x%0llX", fixup.segIndex, lastFixup->segOffset, fixup.segOffset);
+                        setImageInvalid(imageIndex);
+                        return;
+                    }
+                }
+            }
+        }
+        lastFixup = &fixup;
+    }
+
+    if ( hasTextRelocs )
+        _diskImages[imageIndex].hasTextRelocs = true;
+
+    // there is one ordinal table per image, shared by all segments with fixups in that image
+    std::vector<TargetSymbolValue>  targetsForImage;
+
+    const bool opcodeLogging = false;
+    // calculate SegmentFixupsByPage for each segment
+    std::vector<SegmentFixUpBuilder*> builders;
+    for (uint32_t segIndex=0, onDiskSegIndex=0; segIndex < _diskImages[imageIndex].segmentsArrayCount; ++segIndex) {
+        const binary_format::DiskSegment* diskSeg = (const binary_format::DiskSegment*)&(_segmentPool[_diskImages[imageIndex].segmentsArrayStartIndex+segIndex]);
+        SegmentFixUpBuilder* builder = nullptr;
+        if ( diskSeg->paddingNotSeg )
+            continue;
+        if ( diskSeg->filePageCount == 0 ) {
+            ++onDiskSegIndex;
+            continue;
+        }
+        if ( diskSeg->permissions & VM_PROT_WRITE ) {
+            builder = new SegmentFixUpBuilder(onDiskSegIndex, diskSeg->filePageCount, _pageSize, _is64, fixups, targetsForImage, opcodeLogging);
+        }
+        else if ( hasTextRelocs && (diskSeg->permissions == (VM_PROT_READ|VM_PROT_EXECUTE)) ) {
+            builder = new SegmentFixUpBuilder(onDiskSegIndex, diskSeg->filePageCount, _pageSize, _is64, fixups, targetsForImage, opcodeLogging);
+        }
+        if ( builder != nullptr ) {
+            if ( builder->hasFixups() )
+                builders.push_back(builder);
+            else
+                delete builder;
+        }
+        ++onDiskSegIndex;
+    }
+
+    // build AllFixupsBySegment for image
+    _fixupsPool.pad_to_size(4);
+    uint32_t startOfFixupsOffset = (uint32_t)_fixupsPool.size();
+    size_t headerSize = builders.size() * sizeof(binary_format::AllFixupsBySegment);
+    size_t offsetOfSegmentHeaderInBuffer = _fixupsPool.size();
+    for (int i=0; i < headerSize; ++i) {
+        _fixupsPool.append_byte(0);
+    }
+    uint32_t entryIndex = 0;
+    for (SegmentFixUpBuilder* builder : builders) {
+        binary_format::AllFixupsBySegment* entries = (binary_format::AllFixupsBySegment*)(_fixupsPool.start()+offsetOfSegmentHeaderInBuffer);
+        entries[entryIndex].segIndex = builder->segIndex();
+        entries[entryIndex].offset   = (uint32_t)_fixupsPool.size() - startOfFixupsOffset;
+        builder->appendSegmentFixUpMap(_fixupsPool);
+        delete builder;
+        ++entryIndex;
+    }
+    _diskImages[imageIndex].fixupsPoolOffset   = (uint32_t)offsetOfSegmentHeaderInBuffer;
+    _diskImages[imageIndex].fixupsPoolSegCount = entryIndex;
+
+    // append targetsForImage into group
+    size_t start = _targetsPool.size();
+    size_t count = targetsForImage.size();
+    _diskImages[imageIndex].targetsArrayStartIndex = (uint32_t)start;
+    _diskImages[imageIndex].targetsArrayCount      = (uint32_t)count;
+    assert(_diskImages[imageIndex].targetsArrayStartIndex == start);
+    assert(_diskImages[imageIndex].targetsArrayCount      == count);
+    _targetsPool.insert(_targetsPool.end(), targetsForImage.begin(), targetsForImage.end());
+}
+
+
+}
+}
+
+
diff --git a/dyld3/LaunchCacheWriter.h b/dyld3/LaunchCacheWriter.h
new file mode 100644 (file)
index 0000000..a00ea93
--- /dev/null
@@ -0,0 +1,213 @@
+/*
+ * Copyright (c) 2017 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 LaunchCacheWriter_h
+#define LaunchCacheWriter_h
+
+
+#include <stdint.h>
+
+#include <vector>
+#include <list>
+#include <unordered_map>
+#include <map>
+
+#include "LaunchCacheFormat.h"
+#include "LaunchCache.h"
+#include "MachOParser.h"
+#include "shared-cache/DyldSharedCache.h"
+
+
+namespace dyld3 {
+namespace launch_cache {
+
+
+
+class ContentBuffer {
+private:
+    std::vector<uint8_t>     _data;
+public:
+    std::vector<uint8_t>&    bytes()                     { return _data; }
+    unsigned long            size() const                { return _data.size(); }
+    void                     reserve(unsigned long l)    { _data.reserve(l); }
+    const uint8_t*           start() const               { return &_data[0]; }
+    const uint8_t*           end() const                 { return &_data[_data.size()]; }
+
+    void append_uleb128(uint64_t value) {
+        uint8_t byte;
+        do {
+            byte = value & 0x7F;
+            value &= ~0x7F;
+            if ( value != 0 )
+                byte |= 0x80;
+            _data.push_back(byte);
+            value = value >> 7;
+        } while( byte >= 0x80 );
+    }
+
+    void append_byte(uint8_t byte) {
+        _data.push_back(byte);
+    }
+    
+    void append_uint32(uint32_t value) {
+        for (int i=0; i < 4; ++i) {
+            _data.push_back(value & 0xFF);
+            value = (value >> 8);
+        }
+    }
+    
+    void append_uint64(uint64_t value) {
+        for (int i=0; i < 8; ++i) {
+            _data.push_back(value & 0xFF);
+            value = (value >> 8);
+        }
+    }
+
+    void append_buffer(const ContentBuffer& value) {
+        _data.insert(_data.end(), value.start(), value.end());
+    }
+
+    static unsigned int    uleb128_size(uint64_t value) {
+        uint32_t result = 0;
+        do {
+            value = value >> 7;
+            ++result;
+        } while ( value != 0 );
+        return result;
+    }
+    
+    void pad_to_size(unsigned int alignment) {
+        while ( (_data.size() % alignment) != 0 )
+            _data.push_back(0);
+    }
+};
+
+class ImageGroupWriter
+{
+public:
+                    ImageGroupWriter(uint32_t groupNum, bool pages16KB, bool is64, bool dylibsExpectedOnDisk, bool mtimeAndInodeAreValid);
+
+    enum class FixupType { rebase, pointerBind, pointerLazyBind, bindText, bindTextRel, rebaseText, bindImportJmpRel, ignore };
+    struct FixUp {
+        uint32_t             segIndex;
+        uint64_t             segOffset;
+        FixupType            type;
+        TargetSymbolValue    target;
+    };
+
+    uint32_t        size() const;
+    void            finalizeTo(Diagnostics& diag, const std::vector<const BinaryImageGroupData*>&, binary_format::ImageGroup* buffer) const;
+    uint32_t        maxLoadCount(Diagnostics& diag, const std::vector<const BinaryImageGroupData*>&, binary_format::ImageGroup* buffer) const;
+
+    bool            isInvalid(uint32_t imageIndex) const;
+
+    void            setImageCount(uint32_t);
+    void            setImageInvalid(uint32_t imageIndex);
+    void            setImagePath(uint32_t imageIndex, const char* path);
+    void            setImageUUID(uint32_t imageIndex, const uuid_t uuid);
+    void            setImageHasObjC(uint32_t imageIndex, bool value);
+    void            setImageIsBundle(uint32_t imageIndex, bool value);
+    void            setImageHasWeakDefs(uint32_t imageIndex, bool value);
+    void            setImageMayHavePlusLoads(uint32_t imageIndex, bool value);
+    void            setImageNeverUnload(uint32_t imageIndex, bool);
+    void            setImageMustBeThisDir(uint32_t imageIndex, bool value);
+    void            setImageIsPlatformBinary(uint32_t imageIndex, bool value);
+    void            setImageOverridableDylib(uint32_t imageIndex, bool value);
+    void            setImageIsEncrypted(uint32_t imageIndex, bool value);
+    void            setImageMaxLoadCount(uint32_t imageIndex, uint32_t count);
+    void            setImageFairPlayRange(uint32_t imageIndex, uint32_t offset, uint32_t size);
+    void            setImageInitializerOffsets(uint32_t imageIndex, const std::vector<uint32_t>& offsets);
+    void            setImageDOFOffsets(uint32_t imageIndex, const std::vector<uint32_t>& offsets);
+    void            setImageInitBefore(uint32_t imageIndex, const std::vector<binary_format::ImageRef>&);
+    void            setImageSliceOffset(uint32_t imageIndex, uint64_t fileOffset);
+    void            setImageFileMtimeAndInode(uint32_t imageIndex, uint64_t mTime, uint64_t inode);
+    void            setImageCdHash(uint32_t imageIndex, uint8_t cdHash[20]);
+    void            setImageCodeSignatureLocation(uint32_t imageIndex, uint32_t fileOffset, uint32_t size);
+    void            setImageDependentsCount(uint32_t imageIndex, uint32_t count);
+    void            setImageDependent(uint32_t imageIndex, uint32_t depIndex, binary_format::ImageRef dependent);
+    void            setImageSegments(uint32_t imageIndex, MachOParser& imageParser, uint64_t cacheUnslideBaseAddress);
+    void            setImageFixups(Diagnostics& diag, uint32_t imageIndex, std::vector<FixUp>& fixups, bool hasTextRelocs);
+    void            addImageAliasPath(uint32_t imageIndex, const char* anAlias);
+    void            setImagePatchLocations(uint32_t imageIndex, uint32_t funcOffset, const std::unordered_set<uint32_t>& patchLocations);
+    void            setGroupCacheOverrides(const std::vector<binary_format::DyldCacheOverride>& cacheOverrides);
+    void            addImageIsOverride(binary_format::ImageRef replacer, binary_format::ImageRef replacee);
+
+    uint32_t        addIndirectGroupNum(uint32_t groupNum);
+
+    uint32_t        addString(const char* str);
+    void            alignStringPool();
+
+    uint32_t                 imageDependentsCount(uint32_t imageIndex) const;
+    binary_format::ImageRef  imageDependent(uint32_t imageIndex, uint32_t depIndex) const;
+
+private:
+    struct InitializerInfo {
+        std::vector<uint32_t>                   offsetsInImage;
+        std::vector<binary_format::ImageRef>    initBeforeImages;
+    };
+
+    uint32_t                                    imageCount() const;
+    binary_format::Image&                       imageByIndex(uint32_t);
+    const binary_format::Image&                 imageByIndex(uint32_t) const;
+    std::vector<uint8_t>                        makeFixupOpcodes(const FixUp* start, const FixUp* end, uint32_t pageStartSegmentOffset, std::map<uint32_t, TargetSymbolValue>&);
+    void                                        makeDataFixupMapAndOrdinalTable(std::vector<uint8_t>& fixupMap, std::vector<TargetSymbolValue>& ordinalTable);
+    void                                        computeInitializerOrdering(uint32_t imageIndex);
+    uint32_t                                    addUniqueInitList(const std::vector<binary_format::ImageRef>& initBefore);
+    void                                        layoutBinary(binary_format::ImageGroup* grp) const;
+
+    const bool                                   _isDiskImage;
+    const bool                                   _is64;
+    const uint16_t                               _groupNum;
+    const uint32_t                               _pageSize;
+    bool                                         _dylibsExpectedOnDisk;
+    bool                                         _imageFileInfoIsCdHash;
+    std::vector<binary_format::CachedImage>      _images;
+    std::vector<binary_format::DiskImage>        _diskImages;
+    std::vector<binary_format::AliasEntry>       _aliases;
+    std::vector<uint64_t>                        _segmentPool;
+    std::vector<binary_format::ImageRef>         _dependentsPool;
+    std::vector<uint32_t>                        _initializerOffsets;
+    std::vector<binary_format::ImageRef>         _initializerBeforeLists;
+    std::vector<uint32_t>                        _dofOffsets;
+    std::vector<TargetSymbolValue>               _targetsPool;
+    ContentBuffer                                _fixupsPool;
+    std::vector<binary_format::PatchTable>       _patchPool;
+    std::vector<binary_format::PatchOffset>      _patchLocationPool;
+    std::vector<binary_format::DyldCacheOverride>_dyldCacheSymbolOverridePool;
+    std::vector<binary_format::ImageRefOverride> _imageOverridePool;
+    std::vector<uint32_t>                        _indirectGroupNumPool;
+    std::unordered_map<uint32_t, uint32_t>       _indirectGroupNumPoolExisting;
+    std::vector<char>                            _stringPool;
+    std::unordered_map<std::string, uint32_t>    _stringPoolExisting;
+};
+
+
+
+} //  namespace launch_cache
+} //  namespace dyld3
+
+
+#endif // LaunchCacheWriter_h
+
+
diff --git a/dyld3/Loading.cpp b/dyld3/Loading.cpp
new file mode 100644 (file)
index 0000000..50785b5
--- /dev/null
@@ -0,0 +1,804 @@
+/*
+ * Copyright (c) 2017 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 <stdint.h>
+#include <string.h>
+#include <assert.h>
+#include <uuid/uuid.h>
+#include <mach/mach.h>
+#include <sys/stat.h> 
+#include <sys/types.h>
+#include <sys/sysctl.h>
+#include <fcntl.h>
+#include <sys/dtrace.h>
+#include <sys/errno.h>
+#include <unistd.h>
+#include <System/sys/mman.h>
+#include <System/sys/csr.h>
+#include <System/machine/cpu_capabilities.h>
+#include <bootstrap.h>
+#include <CommonCrypto/CommonDigest.h>
+#include <sandbox.h>
+#include <sandbox/private.h>
+#include <dispatch/dispatch.h>
+
+#include "LaunchCache.h"
+#include "LaunchCacheFormat.h"
+#include "Logging.h"
+#include "Loading.h"
+#include "MachOParser.h"
+#include "dyld.h"
+#include "dyld_cache_format.h"
+
+extern "C" {
+    #include "closuredProtocol.h"
+}
+
+namespace dyld {
+    void log(const char* m, ...);
+}
+
+namespace dyld3 {
+namespace loader {
+
+#if DYLD_IN_PROCESS
+
+static bool sandboxBlocked(const char* path, const char* kind)
+{
+#if BUILDING_LIBDYLD || !TARGET_IPHONE_SIMULATOR
+    sandbox_filter_type filter = (sandbox_filter_type)(SANDBOX_FILTER_PATH | SANDBOX_CHECK_NO_REPORT);
+    return ( sandbox_check(getpid(), kind, filter, path) > 0 );
+#else
+    // sandbox calls not yet supported in dyld_sim
+    return false;
+#endif
+}
+
+static bool sandboxBlockedMmap(const char* path)
+{
+    return sandboxBlocked(path, "file-map-executable");
+}
+
+static bool sandboxBlockedOpen(const char* path)
+{
+    return sandboxBlocked(path, "file-read-data");
+}
+
+static bool sandboxBlockedStat(const char* path)
+{
+    return sandboxBlocked(path, "file-read-metadata");
+}
+
+#if TARGET_OS_WATCH
+static uint64_t pageAlign(uint64_t value)
+{
+       return (value + 4095) & (-4096);
+}
+#endif
+
+static void updateSliceOffset(uint64_t& sliceOffset, uint64_t codeSignEndOffset, size_t fileLen)
+{
+#if TARGET_OS_WATCH
+    if ( sliceOffset != 0 ) {
+        if ( pageAlign(codeSignEndOffset) == pageAlign(fileLen) ) {
+            // cache builder saw fat file, but file is now thin
+            sliceOffset = 0;
+            return;
+        }
+    }
+#endif
+}
+
+static const mach_header* mapImage(const dyld3::launch_cache::Image image, Diagnostics& diag, LogFunc log_loads, LogFunc log_segments)
+{
+    uint64_t       sliceOffset        = image.sliceOffsetInFile();
+    const uint64_t totalVMSize        = image.vmSizeToMap();
+    const uint32_t codeSignFileOffset = image.asDiskImage()->codeSignFileOffset;
+    const uint32_t codeSignFileSize   = image.asDiskImage()->codeSignFileSize;
+
+    // open file
+    int fd = ::open(image.path(), O_RDONLY, 0);
+    if ( fd == -1 ) {
+        int openErr = errno;
+        if ( (openErr == EPERM) && sandboxBlockedOpen(image.path()) )
+            diag.error("file system sandbox blocked open(\"%s\", O_RDONLY)", image.path());
+        else
+            diag.error("open(\"%s\", O_RDONLY) failed with errno=%d", image.path(), openErr);
+        return nullptr;
+    }
+
+    // get file info
+    struct stat statBuf;
+#if TARGET_IPHONE_SIMULATOR
+    if ( stat(image.path(), &statBuf) != 0 ) {
+#else
+    if ( fstat(fd, &statBuf) != 0 ) {
+#endif
+        int statErr = errno;
+        if ( (statErr == EPERM) && sandboxBlockedStat(image.path()) )
+            diag.error("file system sandbox blocked stat(\"%s\")", image.path());
+        else
+           diag.error("stat(\"%s\") failed with errno=%d", image.path(), statErr);
+        close(fd);
+        return nullptr;
+    }
+
+    // verify file has not changed since closure was built
+    if ( image.validateUsingModTimeAndInode() ) {
+        if ( (statBuf.st_mtime != image.fileModTime()) || (statBuf.st_ino != image.fileINode()) ) {
+            diag.error("file mtime/inode changed since closure was built for '%s'", image.path());
+            close(fd);
+            return nullptr;
+        }
+    }
+
+    // handle OS dylibs being thinned after closure was built
+    if ( image.group().groupNum() == 1 )
+        updateSliceOffset(sliceOffset, codeSignFileOffset+codeSignFileSize, (size_t)statBuf.st_size);
+
+    // register code signature
+    uint64_t coveredCodeLength  = UINT64_MAX;
+    if ( codeSignFileOffset != 0 ) {
+        fsignatures_t siginfo;
+        siginfo.fs_file_start  = sliceOffset;                           // start of mach-o slice in fat file
+        siginfo.fs_blob_start  = (void*)(long)(codeSignFileOffset);     // start of CD in mach-o file
+        siginfo.fs_blob_size   = codeSignFileSize;                      // size of CD
+        int result = fcntl(fd, F_ADDFILESIGS_RETURN, &siginfo);
+        if ( result == -1 ) {
+            int errnoCopy = errno;
+            if ( (errnoCopy == EPERM) || (errnoCopy == EBADEXEC) ) {
+                diag.error("code signature invalid (errno=%d) sliceOffset=0x%08llX, codeBlobOffset=0x%08X, codeBlobSize=0x%08X for '%s'",
+                            errnoCopy, sliceOffset, codeSignFileOffset, codeSignFileSize, image.path());
+            }
+            else {
+                diag.error("fcntl(fd, F_ADDFILESIGS_RETURN) failed with errno=%d, sliceOffset=0x%08llX, codeBlobOffset=0x%08X, codeBlobSize=0x%08X for '%s'",
+                            errnoCopy, sliceOffset, codeSignFileOffset, codeSignFileSize, image.path());
+            }
+            close(fd);
+            return nullptr;
+        }
+        coveredCodeLength = siginfo.fs_file_start;
+        if ( coveredCodeLength <  image.asDiskImage()->codeSignFileOffset ) {
+            diag.error("code signature does not cover entire file up to signature");
+            close(fd);
+            return nullptr;
+        }
+
+        // <rdar://problem/32684903> always call F_CHECK_LV to preflight
+        fchecklv checkInfo;
+        char  messageBuffer[512];
+        messageBuffer[0] = '\0';
+        checkInfo.lv_file_start = sliceOffset;
+        checkInfo.lv_error_message_size = sizeof(messageBuffer);
+        checkInfo.lv_error_message = messageBuffer;
+        int res = fcntl(fd, F_CHECK_LV, &checkInfo);
+        if ( res == -1 ) {
+             diag.error("code signature in (%s) not valid for use in process: %s", image.path(), messageBuffer);
+             close(fd);
+             return nullptr;
+        }
+    }
+
+    // reserve address range
+    vm_address_t loadAddress = 0;
+    kern_return_t r = vm_allocate(mach_task_self(), &loadAddress, (vm_size_t)totalVMSize, VM_FLAGS_ANYWHERE);
+    if ( r != KERN_SUCCESS ) {
+        diag.error("vm_allocate(size=0x%0llX) failed with result=%d", totalVMSize, r);
+        close(fd);
+        return nullptr;
+    }
+
+    if ( sliceOffset != 0 )
+        log_segments("dyld: Mapping %s (slice offset=%llu)\n", image.path(), sliceOffset);
+    else
+        log_segments("dyld: Mapping %s\n", image.path());
+
+    // map each segment
+    __block bool mmapFailure = false;
+    __block const uint8_t* codeSignatureStartAddress = nullptr;
+    __block const uint8_t* linkeditEndAddress = nullptr;
+    __block bool mappedFirstSegment = false;
+    image.forEachDiskSegment(^(uint32_t segIndex, uint32_t fileOffset, uint32_t fileSize, int64_t vmOffset, uint64_t vmSize, uint8_t permissions, bool& stop) {
+        // <rdar://problem/32363581> Mapping zero filled segments fails with mmap of size 0
+        if ( fileSize == 0 )
+            return;
+        void* segAddress = mmap((void*)(loadAddress+vmOffset), fileSize, permissions, MAP_FIXED | MAP_PRIVATE, fd, sliceOffset+fileOffset);
+        int mmapErr = errno;
+        if ( segAddress == MAP_FAILED ) {
+            if ( mmapErr == EPERM ) {
+                if ( sandboxBlockedMmap(image.path()) )
+                    diag.error("file system sandbox blocked mmap() of '%s'", image.path());
+                else
+                    diag.error("code signing blocked mmap() of '%s'", image.path());
+            }
+            else {
+                diag.error("mmap(addr=0x%0llX, size=0x%08X) failed with errno=%d for %s", loadAddress+vmOffset, fileSize, mmapErr, image.path());
+            }
+            mmapFailure = true;
+            stop = true;
+        }
+        else if ( codeSignFileOffset > fileOffset ) {
+            codeSignatureStartAddress = (uint8_t*)segAddress + (codeSignFileOffset-fileOffset);
+            linkeditEndAddress = (uint8_t*)segAddress + vmSize;
+        }
+        // sanity check first segment is mach-o header
+        if ( (segAddress != MAP_FAILED) && !mappedFirstSegment ) {
+            mappedFirstSegment = true;
+            if ( !MachOParser::isMachO(diag, segAddress, fileSize) ) {
+                mmapFailure = true;
+                stop = true;
+            }
+        }
+        if ( !mmapFailure ) {
+            MachOParser parser((mach_header*)loadAddress);
+            log_segments("%14s (%c%c%c) 0x%012lX->0x%012lX \n", parser.segmentName(segIndex),
+                         (permissions & PROT_READ) ? 'r' : '.', (permissions & PROT_WRITE) ? 'w' : '.', (permissions & PROT_EXEC) ? 'x' : '.' ,
+                         (long)segAddress, (long)segAddress+vmSize-1);
+        }
+    });
+    if ( mmapFailure ) {
+        vm_deallocate(mach_task_self(), loadAddress, (vm_size_t)totalVMSize);
+        close(fd);
+        return nullptr;
+    }
+
+    // close file
+    close(fd);
+
+ #if BUILDING_LIBDYLD
+    // verify file has not changed since closure was built by checking code signature has not changed
+    if ( image.validateUsingCdHash() ) {
+        if ( codeSignatureStartAddress == nullptr ) {
+            diag.error("code signature missing");
+        }
+        else if ( codeSignatureStartAddress+codeSignFileSize > linkeditEndAddress ) {
+            diag.error("code signature extends beyond end of __LINKEDIT");
+        }
+        else {
+            uint8_t cdHash[20];
+            if ( MachOParser::cdHashOfCodeSignature(codeSignatureStartAddress, codeSignFileSize, cdHash) ) {
+                if ( memcmp(image.cdHash16(), cdHash, 16) != 0 )
+                    diag.error("code signature changed since closure was built");
+            }
+            else{
+                diag.error("code signature format invalid");
+            }
+        }
+        if ( diag.hasError() ) {
+            vm_deallocate(mach_task_self(), loadAddress, (vm_size_t)totalVMSize);
+            return nullptr;
+        }
+    }
+#endif
+
+#if __IPHONE_OS_VERSION_MIN_REQUIRED && !TARGET_IPHONE_SIMULATOR
+    // tell kernel about fairplay encrypted regions
+    uint32_t fpTextOffset;
+    uint32_t fpSize;
+    if ( image.isFairPlayEncrypted(fpTextOffset, fpSize) ) {
+        const mach_header* mh = (mach_header*)loadAddress;
+        int result = mremap_encrypted(((uint8_t*)mh) + fpTextOffset, fpSize, 1, mh->cputype, mh->cpusubtype);
+        diag.error("could not register fairplay decryption, mremap_encrypted() => %d", result);
+        vm_deallocate(mach_task_self(), loadAddress, (vm_size_t)totalVMSize);
+        return nullptr;
+    }
+#endif
+
+    log_loads("dyld: load %s\n", image.path());
+
+    return (mach_header*)loadAddress;
+}
+
+
+void unmapImage(const launch_cache::binary_format::Image* binImage, const mach_header* loadAddress)
+{
+    assert(loadAddress != nullptr);
+    launch_cache::Image image(binImage);
+    vm_deallocate(mach_task_self(), (vm_address_t)loadAddress, (vm_size_t)(image.vmSizeToMap()));
+}
+
+
+static void applyFixupsToImage(Diagnostics& diag, const mach_header* imageMH, const launch_cache::binary_format::Image* imageData,
+                               launch_cache::TargetSymbolValue::LoadedImages& imageResolver, LogFunc log_fixups)
+{
+    launch_cache::Image image(imageData);
+    MachOParser imageParser(imageMH);
+    // Note, these are cached here to avoid recalculating them on every loop iteration
+    const launch_cache::ImageGroup& imageGroup = image.group();
+    const char* leafName = image.leafName();
+    intptr_t slide = imageParser.getSlide();
+    image.forEachDiskSegment(^(uint32_t segIndex, uint32_t fileOffset, uint32_t fileSize, int64_t vmOffset, uint64_t vmSize, uint8_t protections, bool& segStop) {
+        if ( !image.segmentHasFixups(segIndex) )
+            return;
+        const launch_cache::MemoryRange segContent = { (char*)imageMH + vmOffset, vmSize };
+    #if __i386__
+        bool textRelocs = ((protections & VM_PROT_WRITE) == 0);
+        if ( textRelocs ) {
+            kern_return_t r = vm_protect(mach_task_self(), (vm_address_t)segContent.address, (vm_size_t)segContent.size, false, VM_PROT_WRITE | VM_PROT_READ);
+            if ( r != KERN_SUCCESS ) {
+                diag.error("vm_protect() failed trying to make text segment writable, result=%d", r);
+                return;
+            }
+        }
+    #else
+        if ( (protections & VM_PROT_WRITE) == 0 ) {
+            diag.error("fixups found in non-writable segment of %s", image.path());
+            return;
+        }
+    #endif
+        image.forEachFixup(segIndex, segContent, ^(uint64_t segOffset, launch_cache::Image::FixupKind kind, launch_cache::TargetSymbolValue targetValue, bool& stop) {
+            if ( segOffset > segContent.size ) {
+                diag.error("fixup is past end of segment. segOffset=0x%0llX, segSize=0x%0llX, segIndex=%d", segOffset, segContent.size, segIndex);
+                stop = true;
+                return;
+            }
+            uintptr_t* fixUpLoc = (uintptr_t*)((char*)(segContent.address) + segOffset);
+            uintptr_t value;
+        #if __i386__
+            uint32_t rel32;
+            uint8_t* jumpSlot;
+        #endif
+            //dyld::log("fixup loc=%p\n", fixUpLoc);
+            switch ( kind ) {
+        #if __LP64__
+                case launch_cache::Image::FixupKind::rebase64:
+        #else
+                case launch_cache::Image::FixupKind::rebase32:
+        #endif
+                    *fixUpLoc += slide;
+                    log_fixups("dyld: fixup: %s:%p += %p\n", leafName, fixUpLoc, (void*)slide);
+                    break;
+        #if __LP64__
+                case launch_cache::Image::FixupKind::bind64:
+        #else
+                case launch_cache::Image::FixupKind::bind32:
+        #endif
+                    value = targetValue.resolveTarget(diag, imageGroup, imageResolver);
+                    log_fixups("dyld: fixup: %s:%p = %p\n", leafName, fixUpLoc, (void*)value);
+                    *fixUpLoc = value;
+                    break;
+        #if __i386__
+            case launch_cache::Image::FixupKind::rebaseText32:
+                log_fixups("dyld: text fixup: %s:%p += %p\n", leafName, fixUpLoc, (void*)slide);
+                *fixUpLoc += slide;
+                break;
+            case launch_cache::Image::FixupKind::bindText32:
+                value = targetValue.resolveTarget(diag, imageGroup, imageResolver);
+                log_fixups("dyld: text fixup: %s:%p = %p\n", leafName, fixUpLoc, (void*)value);
+                *fixUpLoc = value;
+                break;
+            case launch_cache::Image::FixupKind::bindTextRel32:
+                // CALL instruction uses pc-rel value
+                value = targetValue.resolveTarget(diag, imageGroup, imageResolver);
+                log_fixups("dyld: CALL fixup: %s:%p = %p (pc+0x%08X)\n", leafName, fixUpLoc, (void*)value, (value - (uintptr_t)(fixUpLoc)));
+                *fixUpLoc = (value - (uintptr_t)(fixUpLoc));
+                break;
+           case launch_cache::Image::FixupKind::bindImportJmp32:
+                // JMP instruction in __IMPORT segment uses pc-rel value
+                jumpSlot = (uint8_t*)fixUpLoc;
+                value = targetValue.resolveTarget(diag, imageGroup, imageResolver);
+                rel32 = (value - ((uintptr_t)(fixUpLoc)+5));
+                log_fixups("dyld: JMP fixup: %s:%p = %p (pc+0x%08X)\n", leafName, fixUpLoc, (void*)value, rel32);
+                jumpSlot[0] = 0xE9; // JMP rel32
+                jumpSlot[1] = rel32 & 0xFF;
+                jumpSlot[2] = (rel32 >> 8) & 0xFF;
+                jumpSlot[3] = (rel32 >> 16) & 0xFF;
+                jumpSlot[4] = (rel32 >> 24) & 0xFF;
+                break;
+        #endif
+            default:
+                diag.error("unknown fixup kind %d", kind);
+            }
+            if ( diag.hasError() )
+                stop = true;
+        });
+    #if __i386__
+        if ( textRelocs ) {
+            kern_return_t r = vm_protect(mach_task_self(), (vm_address_t)segContent.address, (vm_size_t)segContent.size, false, protections);
+            if ( r != KERN_SUCCESS ) {
+                diag.error("vm_protect() failed trying to make text segment non-writable, result=%d", r);
+                return;
+            }
+        }
+    #endif
+    });
+}
+
+
+
+class VIS_HIDDEN CurrentLoadImages : public launch_cache::TargetSymbolValue::LoadedImages
+{
+public:
+                                CurrentLoadImages(launch_cache::DynArray<ImageInfo>& images, const uint8_t* cacheAddr)
+                                    : _dyldCacheLoadAddress(cacheAddr), _images(images) { }
+
+    virtual const uint8_t*      dyldCacheLoadAddressForImage();
+    virtual const mach_header*  loadAddressFromGroupAndIndex(uint32_t groupNum, uint32_t indexInGroup);
+    virtual void                forEachImage(void (^handler)(uint32_t anIndex, const launch_cache::binary_format::Image*, const mach_header*, bool& stop));
+    virtual void                setAsNeverUnload(uint32_t anIndex) { _images[anIndex].neverUnload = true; }
+private:
+    const uint8_t*                      _dyldCacheLoadAddress;
+    launch_cache::DynArray<ImageInfo>&  _images;
+};
+
+const uint8_t* CurrentLoadImages::dyldCacheLoadAddressForImage()
+{
+    return _dyldCacheLoadAddress;
+}
+
+const mach_header* CurrentLoadImages::loadAddressFromGroupAndIndex(uint32_t groupNum, uint32_t indexInGroup)
+{
+    __block const mach_header* result = nullptr;
+    forEachImage(^(uint32_t anIndex, const launch_cache::binary_format::Image* imageData, const mach_header* mh, bool& stop) {
+        launch_cache::Image         image(imageData);
+        launch_cache::ImageGroup    imageGroup = image.group();
+        if ( imageGroup.groupNum() != groupNum )
+            return;
+        if ( imageGroup.indexInGroup(imageData) == indexInGroup ) {
+            result = mh;
+            stop = true;
+        }
+    });
+    return result;
+}
+
+void CurrentLoadImages::forEachImage(void (^handler)(uint32_t anIndex, const launch_cache::binary_format::Image*, const mach_header*, bool& stop))
+{
+    bool stop = false;
+    for (int i=0; i < _images.count(); ++i) {
+        ImageInfo& info = _images[i];
+        handler(i, info.imageData, info.loadAddress, stop);
+        if ( stop )
+            break;
+    }
+}
+
+struct DOFInfo {
+    const void*            dof;
+    const mach_header*     imageHeader;
+    const char*            imageShortName;
+};
+
+static void registerDOFs(const DOFInfo* dofs, uint32_t dofSectionCount, LogFunc log_dofs)
+{
+    if ( dofSectionCount != 0 ) {
+        int fd = open("/dev/" DTRACEMNR_HELPER, O_RDWR);
+        if ( fd < 0 ) {
+            log_dofs("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.
+                // Note, the closure marked the image as being never unload, so we don't need to keep the ID around
+                // or support unregistering it later.
+                for (unsigned int i=0; i < dofSectionCount; ++i) {
+                    log_dofs("dyld: registering DOF section %p in %s with dtrace, ID=0x%08X\n",
+                             dofs[i].dof, dofs[i].imageShortName, (int)(ioctlData->dofiod_helpers[i].dofhp_dof));
+                }
+            }
+            else {
+                //dyld::log( "dyld: ioctl to register dtrace DOF section failed\n");
+            }
+            close(fd);
+        }
+    }
+}
+
+
+void mapAndFixupImages(Diagnostics& diag, launch_cache::DynArray<ImageInfo>& images, const uint8_t* cacheLoadAddress,
+                       LogFunc log_loads, LogFunc log_segments, LogFunc log_fixups, LogFunc log_dofs)
+{
+    // scan array and map images not already loaded
+    for (int i=0; i < images.count(); ++i) {
+        ImageInfo& info = images[i];
+        const dyld3::launch_cache::Image image(info.imageData);
+        if ( info.loadAddress != nullptr ) {
+            // log main executable's segments
+            if ( (info.groupNum == 2) && (info.loadAddress->filetype == MH_EXECUTE) && !info.previouslyFixedUp ) {
+                if ( log_segments("dyld: mapped by kernel %s\n", image.path()) ) {
+                    MachOParser parser(info.loadAddress);
+                    image.forEachDiskSegment(^(uint32_t segIndex, uint32_t fileOffset, uint32_t fileSize, int64_t vmOffset, uint64_t vmSize, uint8_t permissions, bool& stop) {
+                        uint64_t start = (long)info.loadAddress + vmOffset;
+                        uint64_t end   = start+vmSize-1;
+                        if ( (segIndex == 0) && (permissions == 0) ) {
+                            start = 0;
+                        }
+                        log_segments("%14s (%c%c%c) 0x%012llX->0x%012llX \n", parser.segmentName(segIndex),
+                                    (permissions & PROT_READ) ? 'r' : '.', (permissions & PROT_WRITE) ? 'w' : '.', (permissions & PROT_EXEC) ? 'x' : '.' ,
+                                    start, end);
+                    });
+                }
+            }
+            // skip over ones already loaded
+            continue;
+        }
+        if ( image.isDiskImage() ) {
+            //dyld::log("need to load image[%d] %s\n", i, image.path());
+            info.loadAddress = mapImage(image, diag, log_loads, log_segments);
+            if ( diag.hasError() ) {
+                break; // out of for loop
+            }
+            info.justMapped = true;
+        }
+        else {
+            bool expectedOnDisk   = image.group().dylibsExpectedOnDisk();
+            bool overridableDylib = image.overridableDylib();
+            if ( expectedOnDisk || overridableDylib ) {
+                struct stat statBuf;
+                if ( ::stat(image.path(), &statBuf) == 0 ) {
+                    if ( expectedOnDisk ) {
+                        // macOS case: verify dylib file info matches what it was when cache was built
+                        if ( image.fileModTime() != statBuf.st_mtime ) {
+                            diag.error("cached dylib mod-time has changed, dylib cache has: 0x%08llX, file has: 0x%08lX, for: %s", image.fileModTime(), (long)statBuf.st_mtime, image.path());
+                            break; // out of for loop
+                        }
+                         if ( image.fileINode() != statBuf.st_ino ) {
+                            diag.error("cached dylib inode has changed, dylib cache has: 0x%08llX, file has: 0x%08llX, for: %s", image.fileINode(), statBuf.st_ino, image.path());
+                            break; // out of for loop
+                        }
+                   }
+                    else {
+                        // iOS internal: dylib override installed
+                        diag.error("cached dylib overridden: %s", image.path());
+                        break; // out of for loop
+                    }
+                }
+                else {
+                    if ( expectedOnDisk ) {
+                        // macOS case: dylib that existed when cache built no longer exists
+                        diag.error("missing cached dylib: %s", image.path());
+                        break; // out of for loop
+                    }
+                }
+            }
+            info.loadAddress = (mach_header*)(cacheLoadAddress + image.cacheOffset());
+            info.justUsedFromDyldCache = true;
+            if ( log_segments("dyld: Using from dyld cache %s\n", image.path()) ) {
+                MachOParser parser(info.loadAddress);
+                image.forEachCacheSegment(^(uint32_t segIndex, uint64_t vmOffset, uint64_t vmSize, uint8_t permissions, bool &stop) {
+                    log_segments("%14s (%c%c%c) 0x%012lX->0x%012lX \n", parser.segmentName(segIndex),
+                                    (permissions & PROT_READ) ? 'r' : '.', (permissions & PROT_WRITE) ? 'w' : '.', (permissions & PROT_EXEC) ? 'x' : '.' ,
+                                    (long)cacheLoadAddress+vmOffset, (long)cacheLoadAddress+vmOffset+vmSize-1);
+                 });
+            }
+        }
+    }
+    if ( diag.hasError() )  {
+        // back out and unmapped images all loaded so far
+        for (uint32_t j=0; j < images.count(); ++j) {
+            ImageInfo& anInfo = images[j];
+            if ( anInfo.justMapped )
+                 unmapImage(anInfo.imageData, anInfo.loadAddress);
+            anInfo.loadAddress = nullptr;
+        }
+        return;
+    }
+
+    // apply fixups
+    CurrentLoadImages fixupHelper(images, cacheLoadAddress);
+    for (int i=0; i < images.count(); ++i) {
+        ImageInfo& info = images[i];
+        // images in shared cache do not need fixups applied
+        launch_cache::Image image(info.imageData);
+        if ( !image.isDiskImage() )
+            continue;
+        // previously loaded images were previously fixed up
+        if ( info.previouslyFixedUp )
+            continue;
+        //dyld::log("apply fixups to mh=%p, path=%s\n", info.loadAddress, Image(info.imageData).path());
+        dyld3::loader::applyFixupsToImage(diag, info.loadAddress, info.imageData, fixupHelper, log_fixups);
+        if ( diag.hasError() )
+            break;
+    }
+
+    // Record dtrace DOFs
+    // if ( /* FIXME! register dofs */ )
+    {
+        __block uint32_t dofCount = 0;
+        for (int i=0; i < images.count(); ++i) {
+            ImageInfo& info = images[i];
+            launch_cache::Image image(info.imageData);
+            // previously loaded images were previously fixed up
+            if ( info.previouslyFixedUp )
+                continue;
+            image.forEachDOF(nullptr, ^(const void* section) {
+                // DOFs cause the image to be never-unload
+                assert(image.neverUnload());
+                ++dofCount;
+            });
+        }
+
+        // struct RegisteredDOF { const mach_header* mh; int registrationID; };
+        DOFInfo dofImages[dofCount];
+        __block DOFInfo* dofImagesBase = dofImages;
+        dofCount = 0;
+        for (int i=0; i < images.count(); ++i) {
+            ImageInfo& info = images[i];
+            launch_cache::Image image(info.imageData);
+            // previously loaded images were previously fixed up
+            if ( info.previouslyFixedUp )
+                continue;
+            image.forEachDOF(info.loadAddress, ^(const void* section) {
+                DOFInfo dofInfo;
+                dofInfo.dof            = section;
+                dofInfo.imageHeader    = info.loadAddress;
+                dofInfo.imageShortName = image.leafName();
+                dofImagesBase[dofCount++] = dofInfo;
+            });
+        }
+        registerDOFs(dofImages, dofCount, log_dofs);
+    }
+}
+
+#if BUILDING_DYLD
+void forEachLineInFile(const char* path, void (^lineHandler)(const char* line, bool& stop))
+{
+    int fd = dyld::my_open(path, O_RDONLY, 0);
+    if ( fd != -1 ) {
+        struct stat statBuf;
+        if ( fstat(fd, &statBuf) == 0 ) {
+            const char* lines = (const char*)mmap(nullptr, (size_t)statBuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+            if ( lines != MAP_FAILED ) {
+                bool stop = false;
+                const char* const eof = &lines[statBuf.st_size];
+                for (const char* s = lines; s < eof; ++s) {
+                    char lineBuffer[MAXPATHLEN];
+                    char* t = lineBuffer;
+                    char* tEnd = &lineBuffer[MAXPATHLEN];
+                    while ( (s < eof) && (t != tEnd) ) {
+                        if ( *s == '\n' )
+                            break;
+                        *t++ = *s++;
+                    }
+                    *t = '\0';
+                    lineHandler(lineBuffer, stop);
+                    if ( stop )
+                        break;
+                }
+                munmap((void*)lines, (size_t)statBuf.st_size);
+            }
+        }
+        close(fd);
+    }
+}
+
+
+bool internalInstall()
+{
+#if TARGET_IPHONE_SIMULATOR
+    return false;
+#elif __IPHONE_OS_VERSION_MIN_REQUIRED
+    uint32_t devFlags = *((uint32_t*)_COMM_PAGE_DEV_FIRM);
+    return ( (devFlags & 1) == 1 );
+#else
+    return ( csr_check(CSR_ALLOW_APPLE_INTERNAL) == 0 );
+#endif
+}
+
+/* Checks to see if there are any args that impact dyld. These args
+ * can be set sevaral ways. These will only be honored on development
+ * and Apple Internal builds.
+ *
+ * First the existence of a file is checked for:
+ *    /S/L/C/com.apple.dyld/dyld-bootargs
+ * If it exists it will be mapped and scanned line by line. If the executable
+ * exists in the file then the arguments on its line will be applied. "*" may
+ * be used a wildcard to represent all apps. First matching line will be used,
+ * the wild card must be one the last line. Additionally, lines must end with
+ * a "\n"
+ *
+ *
+ * SAMPLE FILE:
+
+ /bin/ls:force_dyld2=1
+ /usr/bin/sw_vers:force_dyld2=1
+*:force_dyld3=1
+EOL
+
+ If no file exists then the kernel boot-args will be scanned.
+ */
+bool bootArgsContains(const char* arg)
+{
+    //FIXME: Use strnstr(). Unfortunately we are missing an imp libc.
+#if TARGET_IPHONE_SIMULATOR
+    return false;
+#else
+    // don't check for boot-args on customer installs
+    if ( !internalInstall() )
+        return false;
+
+    char pathBuffer[MAXPATHLEN+1];
+#if __IPHONE_OS_VERSION_MIN_REQUIRED
+    strlcpy(pathBuffer, IPHONE_DYLD_SHARED_CACHE_DIR, sizeof(IPHONE_DYLD_SHARED_CACHE_DIR));
+#else
+    strlcpy(pathBuffer, MACOSX_DYLD_SHARED_CACHE_DIR, sizeof(MACOSX_DYLD_SHARED_CACHE_DIR));
+#endif
+    strlcat(pathBuffer, "dyld-bootargs", MAXPATHLEN+1);
+    __block bool result = false;
+    forEachLineInFile(pathBuffer, ^(const char* line, bool& stop) {
+        const char* delim = strchr(line, ':');
+        if ( delim == nullptr )
+            return;
+        char binary[MAXPATHLEN];
+        char options[MAXPATHLEN];
+        strlcpy(binary, line, MAXPATHLEN);
+        binary[delim-line] = '\0';
+        strlcpy(options, delim+1, MAXPATHLEN);
+        if ( (strcmp(dyld::getExecutablePath(), binary) == 0) || (strcmp("*", binary) == 0) ) {
+            result = (strstr(options, arg) != nullptr);
+            return;
+        }
+    });
+
+    // get length of full boot-args string
+    size_t len;
+    if ( sysctlbyname("kern.bootargs", NULL, &len, NULL, 0) != 0 )
+        return false;
+
+    // get copy of boot-args string
+    char bootArgsBuffer[len];
+    if ( sysctlbyname("kern.bootargs", bootArgsBuffer, &len, NULL, 0) != 0 )
+        return false;
+
+    // return true if 'arg' is a sub-string of boot-args
+    return (strstr(bootArgsBuffer, arg) != nullptr);
+#endif
+}
+#endif
+
+#if BUILDING_LIBDYLD
+// hack because libdyld.dylib should not link with libc++.dylib
+extern "C" void __cxa_pure_virtual() __attribute__((visibility("hidden")));
+void __cxa_pure_virtual()
+{
+    abort();
+}
+#endif
+
+
+#endif // DYLD_IN_PROCESS
+
+} // namespace loader
+} // namespace dyld3
+
+
+
+
+
diff --git a/dyld3/Loading.h b/dyld3/Loading.h
new file mode 100644 (file)
index 0000000..177543b
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2017 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_LOADING_H__
+#define __DYLD_LOADING_H__
+
+#include <string.h>
+#include <stdint.h>
+#include <mach/mach.h>
+#include <_simple.h>
+#include "LaunchCache.h"
+#include "LaunchCacheFormat.h"
+#include "MachOParser.h"
+#include "ClosureBuffer.h"
+
+
+
+namespace dyld3 {
+
+ClosureBuffer closured_CreateImageGroup(const ClosureBuffer& input);
+
+namespace loader {
+
+struct ImageInfo
+{
+    const launch_cache::binary_format::Image*   imageData;
+    const mach_header*                          loadAddress;
+    uint32_t                                    groupNum;
+    uint32_t                                    indexInGroup;
+    bool                                        previouslyFixedUp;
+    bool                                        justMapped;
+    bool                                        justUsedFromDyldCache;
+    bool                                        neverUnload;
+};
+
+
+#if DYLD_IN_PROCESS
+
+typedef bool (*LogFunc)(const char*, ...) __attribute__((format(printf, 1, 2)));
+
+void mapAndFixupImages(Diagnostics& diag, launch_cache::DynArray<ImageInfo>& images, const uint8_t* cacheLoadAddress,
+                       LogFunc log_loads, LogFunc log_segments, LogFunc log_fixups, LogFunc log_dofs) VIS_HIDDEN;
+
+
+void unmapImage(const launch_cache::binary_format::Image* image, const mach_header* loadAddress) VIS_HIDDEN;
+
+#if BUILDING_DYLD
+bool bootArgsContains(const char* arg) VIS_HIDDEN;
+bool internalInstall();
+void forEachLineInFile(const char* path, void (^lineHandler)(const char* line, bool& stop));
+#endif
+#endif
+
+} // namespace loader
+} // namespace dyld3
+
+
+#endif // __DYLD_LOADING_H__
+
+
diff --git a/dyld3/Logging.cpp b/dyld3/Logging.cpp
new file mode 100644 (file)
index 0000000..593f3c7
--- /dev/null
@@ -0,0 +1,188 @@
+/*
+ * Copyright (c) 2017 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 <string.h>
+#include <stdint.h>
+#include <_simple.h>
+
+#include "Logging.h"
+
+
+namespace dyld3 {
+
+
+static bool sVerboseLoading         = false;
+static bool sVerboseInitializers    = false;
+static bool sVerboseSegments        = false;
+static bool sVerboseAPIs            = false;
+static bool sVerboseNotifications   = false;
+static bool sVerboseFixups          = false;
+static bool sVerboseDOFs          = false;
+
+static void vlog_default(const char* format, va_list list)
+{
+    _simple_vdprintf(2, format, list);
+}
+
+static void (*sLogFunction)(const char* format, va_list list) = &vlog_default;
+static void (*sHaltFunction)(const char* message) __attribute__((noreturn)) = nullptr;
+
+void halt(const char* message)
+{
+    (*sHaltFunction)(message);
+}
+
+static void vlog(const char* format, va_list list)
+{
+    (*sLogFunction)(format, list);
+}
+
+bool log_loads(const char* format, ...)
+{
+    if ( !sVerboseLoading )
+        return false;
+    va_list  list;
+    va_start(list, format);
+    vlog(format, list);
+    va_end(list);
+    return true;
+}
+
+bool log_segments(const char* format, ...)
+{
+    if ( !sVerboseSegments )
+        return false;
+    va_list  list;
+    va_start(list, format);
+    vlog(format, list);
+    va_end(list);
+    return true;
+}
+
+bool log_fixups(const char* format, ...)
+{
+    if ( !sVerboseFixups )
+        return false;
+    va_list    list;
+    va_start(list, format);
+    vlog(format, list);
+    va_end(list);
+    return true;
+}
+
+bool log_initializers(const char* format, ...)
+{
+    if ( !sVerboseInitializers )
+        return false;
+    va_list  list;
+    va_start(list, format);
+    vlog(format, list);
+    va_end(list);
+    return true;
+}
+
+bool log_apis(const char* format, ...)
+{
+    if ( !sVerboseAPIs )
+        return false;
+    va_list  list;
+    va_start(list, format);
+    vlog(format, list);
+    va_end(list);
+    return true;
+}
+
+bool log_notifications(const char* format, ...)
+{
+    if ( !sVerboseNotifications )
+        return false;
+    va_list  list;
+    va_start(list, format);
+    vlog(format, list);
+    va_end(list);
+    return true;
+}
+
+bool log_dofs(const char* format, ...)
+{
+    if ( !sVerboseDOFs )
+        return false;
+    va_list    list;
+    va_start(list, format);
+    vlog(format, list);
+    va_end(list);
+    return true;
+}
+
+
+
+void setLoggingFromEnvs(const char* envp[])
+{
+    for(const char** p = envp; *p != NULL; p++) {
+        const char* keyEqualsValue = *p;
+        const char* equals = strchr(keyEqualsValue, '=');
+        if ( equals != NULL ) {
+            //const char* value = &equals[1];
+            const size_t keyLen = equals-keyEqualsValue;
+            char key[keyLen+1];
+            strncpy(key, keyEqualsValue, keyLen);
+            key[keyLen] = '\0';
+            if ( strcmp(key, "DYLD_PRINT_LIBRARIES") == 0 ) {
+                sVerboseLoading = true;
+            }
+            else if ( strcmp(key, "DYLD_PRINT_SEGMENTS") == 0 ) {
+                sVerboseSegments = true;
+            }
+            else if ( strcmp(key, "DYLD_PRINT_INITIALIZERS") == 0 ) {
+                sVerboseInitializers = true;
+            }
+            else if ( strcmp(key, "DYLD_PRINT_APIS") == 0 ) {
+                sVerboseAPIs = true;
+            }
+            else if ( strcmp(key, "DYLD_PRINT_NOTIFICATIONS") == 0 ) {
+                sVerboseNotifications = true;
+            }
+            else if ( strcmp(key, "DYLD_PRINT_BINDINGS") == 0 ) {
+                sVerboseFixups = true;
+            }
+            else if ( strcmp(key, "DYLD_PRINT_DOFS") == 0 ) {
+                sVerboseDOFs = true;
+            }
+        }
+    }
+}
+
+void setLoggingFunction(void (*func)(const char* format, va_list list))
+{
+    sLogFunction = func;
+}
+
+void setHaltFunction(void (*func)(const char* message) __attribute__((noreturn)))
+{
+    sHaltFunction = func;
+}
+
+
+} // namespace dyld3
+
diff --git a/dyld3/Logging.h b/dyld3/Logging.h
new file mode 100644 (file)
index 0000000..f6b82fb
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2017 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_LOGGING_H__
+#define __DYLD_LOGGING_H__
+
+#include <string.h>
+#include <stdint.h>
+#include <stdarg.h>
+
+#define VIS_HIDDEN __attribute__((visibility("hidden")))
+
+
+namespace dyld3 {
+
+
+bool log_loads(const char* format, ...)         __attribute__((format(printf, 1, 2))) VIS_HIDDEN;
+bool log_apis(const char* format, ...)          __attribute__((format(printf, 1, 2))) VIS_HIDDEN;
+bool log_segments(const char* format, ...)      __attribute__((format(printf, 1, 2))) VIS_HIDDEN;
+bool log_initializers(const char* format, ...)  __attribute__((format(printf, 1, 2))) VIS_HIDDEN;
+bool log_fixups(const char* format, ...)        __attribute__((format(printf, 1, 2))) VIS_HIDDEN;
+bool log_notifications(const char* format, ...) __attribute__((format(printf, 1, 2))) VIS_HIDDEN;
+bool log_dofs(const char* format, ...)        __attribute__((format(printf, 1, 2))) VIS_HIDDEN;
+
+void halt(const char* message) __attribute((noreturn)) VIS_HIDDEN ;
+
+
+// only called during libdyld set up
+void setLoggingFromEnvs(const char* envp[]) VIS_HIDDEN ;
+void setLoggingFunction(void (*func)(const char* format, va_list list)) VIS_HIDDEN;
+void setHaltFunction(void (*func)(const char* message) __attribute__((noreturn))) VIS_HIDDEN;
+
+
+
+
+} // namespace dyld3
+
+#endif // __DYLD_LOGGING_H__
+
+
diff --git a/dyld3/MachOParser.cpp b/dyld3/MachOParser.cpp
new file mode 100644 (file)
index 0000000..27bf3ba
--- /dev/null
@@ -0,0 +1,3509 @@
+/*
+ * Copyright (c) 2017 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 <stdint.h>
+#include <string.h>
+#include <assert.h>
+#include <uuid/uuid.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/uio.h>
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#include <sys/resource.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <rootless.h>
+#include <dirent.h>
+#include <mach/mach.h>
+#include <mach/machine.h>
+#include <mach-o/loader.h>
+#include <mach-o/nlist.h>
+#include <mach-o/fat.h>
+#include <mach-o/reloc.h>
+#include <mach-o/dyld_priv.h>
+#include <CommonCrypto/CommonDigest.h>
+
+#if !DYLD_IN_PROCESS
+#include <dlfcn.h>
+#endif
+
+#include "MachOParser.h"
+#include "Logging.h"
+#include "CodeSigningTypes.h"
+#include "DyldSharedCache.h"
+#include "Trie.hpp"
+
+#if DYLD_IN_PROCESS
+    #include "APIs.h"
+#else
+    #include "StringUtils.h"
+#endif
+
+
+
+#ifndef EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE
+    #define EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE 0x02
+#endif
+
+#ifndef CPU_SUBTYPE_ARM64_E
+    #define CPU_SUBTYPE_ARM64_E    2
+#endif
+
+#ifndef LC_BUILD_VERSION
+    #define LC_BUILD_VERSION 0x32 /* build for platform min OS version */
+
+    /*
+     * The build_version_command contains the min OS version on which this
+     * binary was built to run for its platform.  The list of known platforms and
+     * tool values following it.
+     */
+    struct build_version_command {
+        uint32_t    cmd;        /* LC_BUILD_VERSION */
+        uint32_t    cmdsize;    /* sizeof(struct build_version_command) plus */
+        /* ntools * sizeof(struct build_tool_version) */
+        uint32_t    platform;   /* platform */
+        uint32_t    minos;      /* X.Y.Z is encoded in nibbles xxxx.yy.zz */
+        uint32_t    sdk;        /* X.Y.Z is encoded in nibbles xxxx.yy.zz */
+        uint32_t    ntools;     /* number of tool entries following this */
+    };
+
+    struct build_tool_version {
+        uint32_t    tool;       /* enum for the tool */
+        uint32_t    version;    /* version number of the tool */
+    };
+
+    /* Known values for the platform field above. */
+    #define PLATFORM_MACOS      1
+    #define PLATFORM_IOS        2
+    #define PLATFORM_TVOS       3
+    #define PLATFORM_WATCHOS    4
+    #define PLATFORM_BRIDGEOS   5
+
+    /* Known values for the tool field above. */
+    #define TOOL_CLANG    1
+    #define TOOL_SWIFT    2
+    #define TOOL_LD       3
+#endif
+
+
+namespace dyld3 {
+
+
+bool FatUtil::isFatFile(const void* fileStart)
+{
+    const fat_header* fileStartAsFat = (fat_header*)fileStart;
+    return ( fileStartAsFat->magic == OSSwapBigToHostInt32(FAT_MAGIC) );
+}
+
+/// Returns true if (addLHS + addRHS) > b, or if the add overflowed
+template<typename T>
+static bool greaterThanAddOrOverflow(uint32_t addLHS, uint32_t addRHS, T b) {
+    return (addLHS > b) || (addRHS > (b-addLHS));
+}
+
+/// Returns true if (addLHS + addRHS) > b, or if the add overflowed
+template<typename T>
+static bool greaterThanAddOrOverflow(uint64_t addLHS, uint64_t addRHS, T b) {
+    return (addLHS > b) || (addRHS > (b-addLHS));
+}
+
+void FatUtil::forEachSlice(Diagnostics& diag, const void* fileContent, size_t fileLen, void (^callback)(uint32_t sliceCpuType, uint32_t sliceCpuSubType, const void* sliceStart, size_t sliceSize, bool& stop))
+{
+    const fat_header* fh = (fat_header*)fileContent;
+    if ( fh->magic != OSSwapBigToHostInt32(FAT_MAGIC) ) {
+        diag.error("not a fat file");
+        return;
+    }
+
+    if ( OSSwapBigToHostInt32(fh->nfat_arch) > ((4096 - sizeof(fat_header)) / sizeof(fat_arch)) ) {
+        diag.error("fat header too large: %u entries", OSSwapBigToHostInt32(fh->nfat_arch));
+    }
+    const fat_arch* const archs = (fat_arch*)(((char*)fh)+sizeof(fat_header));
+    bool stop = false;
+    for (uint32_t i=0; i < OSSwapBigToHostInt32(fh->nfat_arch); ++i) {
+        uint32_t cpuType    = OSSwapBigToHostInt32(archs[i].cputype);
+        uint32_t cpuSubType = OSSwapBigToHostInt32(archs[i].cpusubtype);
+        uint32_t offset     = OSSwapBigToHostInt32(archs[i].offset);
+        uint32_t len        = OSSwapBigToHostInt32(archs[i].size);
+        if (greaterThanAddOrOverflow(offset, len, fileLen)) {
+            diag.error("slice %d extends beyond end of file", i);
+            return;
+        }
+        callback(cpuType, cpuSubType, (uint8_t*)fileContent+offset, len, stop);
+        if ( stop )
+            break;
+    }
+}
+
+#if !DYLD_IN_PROCESS
+bool FatUtil::isFatFileWithSlice(Diagnostics& diag, const void* fileContent, size_t fileLen, const std::string& archName, size_t& sliceOffset, size_t& sliceLen, bool& missingSlice)
+{
+    missingSlice = false;
+    if ( !isFatFile(fileContent) )
+        return false;
+
+    __block bool found = false;
+    forEachSlice(diag, fileContent, fileLen, ^(uint32_t sliceCpuType, uint32_t sliceCpuSubType, const void* sliceStart, size_t sliceSize, bool& stop) {
+        std::string sliceArchName = MachOParser::archName(sliceCpuType, sliceCpuSubType);
+        if ( sliceArchName == archName ) {
+            sliceOffset = (char*)sliceStart - (char*)fileContent;
+            sliceLen    = sliceSize;
+            found       = true;
+            stop        = true;
+        }
+    });
+    if ( diag.hasError() )
+        return false;
+
+    if ( !found )
+        missingSlice = true;
+
+    // when looking for x86_64h fallback to x86_64
+    if ( !found && (archName == "x86_64h") )
+        return isFatFileWithSlice(diag, fileContent, fileLen, "x86_64", sliceOffset, sliceLen, missingSlice);
+
+    return found;
+}
+
+#endif
+
+MachOParser::MachOParser(const mach_header* mh, bool dyldCacheIsRaw)
+{
+#if DYLD_IN_PROCESS
+    // assume all in-process mach_headers are real loaded images
+    _data = (long)mh;
+#else
+    if (mh == nullptr)
+        return;
+    _data = (long)mh;
+    if ( (mh->flags & 0x80000000) == 0 ) {
+        // asssume out-of-process mach_header not in a dyld cache are raw mapped files
+        _data |= 1;
+    }
+    else {
+        // out-of-process mach_header in a dyld cache are not raw, but cache may be raw
+        if ( dyldCacheIsRaw )
+            _data |= 2;
+    }
+#endif
+}
+
+const mach_header* MachOParser::header() const
+{
+    return (mach_header*)(_data & -4);
+}
+
+// "raw" means the whole mach-o file was mapped as one contiguous region
+// not-raw means the the mach-o file was mapped like dyld does - with zero fill expansion
+bool MachOParser::isRaw() const
+{
+    return (_data & 1);
+}
+
+// A raw dyld cache is when the whole dyld cache file is mapped in one contiguous region
+// not-raw manes the dyld cache was mapped as it is at runtime with padding between regions
+bool MachOParser::inRawCache() const
+{
+    return (_data & 2);
+}
+
+uint32_t MachOParser::fileType() const
+{
+    return header()->filetype;
+}
+
+bool MachOParser::inDyldCache() const
+{
+    return (header()->flags & 0x80000000);
+}
+
+bool MachOParser::hasThreadLocalVariables() const
+{
+    return (header()->flags & MH_HAS_TLV_DESCRIPTORS);
+}
+
+Platform MachOParser::platform() const
+{
+    Platform platform;
+    uint32_t minOS;
+    uint32_t sdk;
+    if ( getPlatformAndVersion(&platform, &minOS, &sdk) )
+        return platform;
+
+    // old binary with no explict load command to mark platform, look at arch
+    switch ( header()->cputype ) {
+        case CPU_TYPE_X86_64:
+        case CPU_TYPE_I386:
+            return Platform::macOS;
+        case CPU_TYPE_ARM64:
+        case CPU_TYPE_ARM:
+            return Platform::iOS;
+    }
+    return Platform::macOS;
+}
+
+
+#if !DYLD_IN_PROCESS
+
+const MachOParser::ArchInfo MachOParser::_s_archInfos[] = {
+    { "x86_64", CPU_TYPE_X86_64, CPU_SUBTYPE_X86_64_ALL },
+    { "x86_64h", CPU_TYPE_X86_64, CPU_SUBTYPE_X86_64_H },
+    { "i386", CPU_TYPE_I386, CPU_SUBTYPE_I386_ALL },
+    { "arm64", CPU_TYPE_ARM64, CPU_SUBTYPE_ARM64_ALL },
+    { "arm64e", CPU_TYPE_ARM64, CPU_SUBTYPE_ARM64_E },
+    { "armv7k", CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7K },
+    { "armv7s", CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7S },
+    { "armv7", CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7 }
+};
+
+bool MachOParser::isValidMachO(Diagnostics& diag, const std::string& archName, Platform platform, const void* fileContent, size_t fileLength, const std::string& pathOpened, bool ignoreMainExecutables)
+{
+    // must start with mach-o magic value
+    const mach_header* mh = (const mach_header*)fileContent;
+    if ( (mh->magic != MH_MAGIC) && (mh->magic != MH_MAGIC_64) ) {
+        diag.warning("could not use '%s' because it is not a mach-o file", pathOpened.c_str());
+        return false;
+    }
+
+    // must match requested architecture if specified
+    if (!archName.empty() && !isArch(mh, archName)) {
+        // except when looking for x86_64h, fallback to x86_64
+        if ( (archName != "x86_64h") || !isArch(mh, "x86_64") ) {
+            diag.warning("could not use '%s' because it does not contain required architecture %s", pathOpened.c_str(), archName.c_str());
+            return false;
+        }
+    }
+
+    // must be a filetype dyld can load
+    switch ( mh->filetype ) {
+        case MH_EXECUTE:
+            if ( ignoreMainExecutables )
+                return false;
+            break;
+        case MH_DYLIB:
+        case MH_BUNDLE:
+            break;
+        default:
+            diag.warning("could not use '%s' because it is not a dylib, bundle, or executable", pathOpened.c_str());
+            return false;
+    }
+
+    // must be from a file - not in the dyld shared cache
+    if ( mh->flags & 0x80000000 ) {
+        diag.warning("could not use '%s' because the high bit of mach_header flags is reserved for images in dyld cache", pathOpened.c_str());
+        return false;
+    }
+
+    // validate load commands structure
+    MachOParser parser(mh);
+    if ( !parser.validLoadCommands(diag, fileLength) )
+        return false;
+
+    // must match requested platform
+    if ( parser.platform() != platform ) {
+        diag.warning("could not use '%s' because it was built for a different platform", pathOpened.c_str());
+        return false;
+    }
+
+    // cannot be a static executable
+    if ( (mh->filetype == MH_EXECUTE) && !parser.isDynamicExecutable() ) {
+        diag.warning("could not use '%s' because it is a static executable", pathOpened.c_str());
+        return false;
+    }
+
+    // validate dylib loads
+    if ( !parser.validEmbeddedPaths(diag) )
+        return false;
+
+    // validate segments
+    if ( !parser.validSegments(diag, fileLength) )
+        return false;
+
+    // validate LINKEDIT layout
+    if ( !parser.validLinkeditLayout(diag) )
+        return false;
+
+     return true;
+}
+
+
+bool MachOParser::validLoadCommands(Diagnostics& diag, size_t fileLen)
+{
+    // check load command don't exceed file length
+    if ( header()->sizeofcmds + sizeof(mach_header_64) > fileLen ) {
+        diag.warning("load commands exceed length of file");
+        return false;
+    }
+    // walk all load commands and sanity check them
+    Diagnostics walkDiag;
+    LinkEditInfo lePointers;
+    getLinkEditLoadCommands(walkDiag, lePointers);
+    if ( walkDiag.hasError() ) {
+        diag.warning("%s", walkDiag.errorMessage().c_str());
+        return false;
+    }
+
+    // check load commands fit in TEXT segment
+    __block bool overflowText = false;
+    forEachSegment(^(const char* segName, uint32_t segFileOffset, uint32_t segFileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, bool& stop) {
+        if ( strcmp(segName, "__TEXT") == 0 ) {
+            if ( header()->sizeofcmds + sizeof(mach_header_64) > segFileSize ) {
+                diag.warning("load commands exceed length of __TEXT segment");
+                overflowText = true;
+            }
+            stop = true;
+        }
+    });
+    if ( overflowText )
+        return false;
+
+    return true;
+}
+
+bool MachOParser::validEmbeddedPaths(Diagnostics& diag)
+{
+    __block int index = 1;
+    __block bool allGood = true;
+    __block bool foundInstallName = false;
+    forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
+        const dylib_command* dylibCmd;
+        const rpath_command* rpathCmd;
+        switch ( cmd->cmd ) {
+            case LC_ID_DYLIB:
+                foundInstallName = true;
+                // fall through
+            case LC_LOAD_DYLIB:
+            case LC_LOAD_WEAK_DYLIB:
+            case LC_REEXPORT_DYLIB:
+            case LC_LOAD_UPWARD_DYLIB:
+                dylibCmd = (dylib_command*)cmd;
+                if ( dylibCmd->dylib.name.offset > cmd->cmdsize ) {
+                    diag.warning("load command #%d name offset (%u) outside its size (%u)", index, dylibCmd->dylib.name.offset, cmd->cmdsize);
+                    stop = true;
+                    allGood = false;
+                }
+                else {
+                    bool foundEnd = false;
+                    const char* start = (char*)dylibCmd + dylibCmd->dylib.name.offset;
+                    const char* end   = (char*)dylibCmd + cmd->cmdsize;
+                    for (const char* s=start; s < end; ++s) {
+                        if ( *s == '\0' ) {
+                            foundEnd = true;
+                            break;
+                        }
+                    }
+                    if ( !foundEnd ) {
+                        diag.warning("load command #%d string extends beyond end of load command", index);
+                        stop = true;
+                        allGood = false;
+                    }
+                }
+                break;
+            case LC_RPATH:
+                rpathCmd = (rpath_command*)cmd;
+                if ( rpathCmd->path.offset > cmd->cmdsize ) {
+                    diag.warning("load command #%d path offset (%u) outside its size (%u)", index, rpathCmd->path.offset, cmd->cmdsize);
+                    stop = true;
+                    allGood = false;
+                }
+                else {
+                    bool foundEnd = false;
+                    const char* start = (char*)rpathCmd + rpathCmd->path.offset;
+                    const char* end   = (char*)rpathCmd + cmd->cmdsize;
+                    for (const char* s=start; s < end; ++s) {
+                        if ( *s == '\0' ) {
+                            foundEnd = true;
+                            break;
+                        }
+                    }
+                    if ( !foundEnd ) {
+                        diag.warning("load command #%d string extends beyond end of load command", index);
+                        stop = true;
+                        allGood = false;
+                    }
+                }
+                break;
+        }
+        ++index;
+    });
+
+    if ( header()->filetype == MH_DYLIB ) {
+        if ( !foundInstallName ) {
+            diag.warning("MH_DYLIB is missing LC_ID_DYLIB");
+            allGood = false;
+        }
+    }
+    else {
+        if ( foundInstallName ) {
+            diag.warning("LC_ID_DYLIB found in non-MH_DYLIB");
+            allGood = false;
+        }
+    }
+
+    return allGood;
+}
+
+bool MachOParser::validSegments(Diagnostics& diag, size_t fileLen)
+{
+    // check segment load command size
+    __block bool badSegmentLoadCommand = false;
+    forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
+        if ( cmd->cmd == LC_SEGMENT_64 ) {
+            const segment_command_64* seg = (segment_command_64*)cmd;
+            int32_t sectionsSpace = cmd->cmdsize - sizeof(segment_command_64);
+            if ( sectionsSpace < 0 ) {
+               diag.warning("load command size too small for LC_SEGMENT_64");
+               badSegmentLoadCommand = true;
+               stop = true;
+            }
+            else if ( (sectionsSpace % sizeof(section_64)) != 0 ) {
+               diag.warning("segment load command size 0x%X will not fit whole number of sections", cmd->cmdsize);
+               badSegmentLoadCommand = true;
+               stop = true;
+            }
+            else if ( sectionsSpace != (seg->nsects * sizeof(section_64)) ) {
+               diag.warning("load command size 0x%X does not match nsects %d", cmd->cmdsize, seg->nsects);
+               badSegmentLoadCommand = true;
+               stop = true;
+            } else if (greaterThanAddOrOverflow(seg->fileoff, seg->filesize, fileLen)) {
+                diag.warning("segment load command content extends beyond end of file");
+                badSegmentLoadCommand = true;
+                stop = true;
+            } else if ( (seg->filesize > seg->vmsize) && ((seg->vmsize != 0) || ((seg->flags & SG_NORELOC) == 0)) ) {
+                // <rdar://problem/19986776> dyld should support non-allocatable __LLVM segment
+                diag.warning("segment filesize exceeds vmsize");
+                badSegmentLoadCommand = true;
+                stop = true;
+            }
+       }
+        else if ( cmd->cmd == LC_SEGMENT ) {
+            const segment_command* seg = (segment_command*)cmd;
+            int32_t sectionsSpace = cmd->cmdsize - sizeof(segment_command);
+            if ( sectionsSpace < 0 ) {
+               diag.warning("load command size too small for LC_SEGMENT");
+               badSegmentLoadCommand = true;
+               stop = true;
+            }
+            else if ( (sectionsSpace % sizeof(section)) != 0 ) {
+               diag.warning("segment load command size 0x%X will not fit whole number of sections", cmd->cmdsize);
+               badSegmentLoadCommand = true;
+               stop = true;
+            }
+            else if ( sectionsSpace != (seg->nsects * sizeof(section)) ) {
+               diag.warning("load command size 0x%X does not match nsects %d", cmd->cmdsize, seg->nsects);
+               badSegmentLoadCommand = true;
+               stop = true;
+            } else if ( (seg->filesize > seg->vmsize) && ((seg->vmsize != 0) || ((seg->flags & SG_NORELOC) == 0)) ) {
+                // <rdar://problem/19986776> dyld should support non-allocatable __LLVM segment
+                diag.warning("segment filesize exceeds vmsize");
+                badSegmentLoadCommand = true;
+                stop = true;
+            }
+        }
+    });
+     if ( badSegmentLoadCommand )
+         return false;
+
+    // check mapping permissions of segments
+    __block bool badPermissions = false;
+    __block bool badSize        = false;
+    __block bool hasTEXT        = false;
+    __block bool hasLINKEDIT    = false;
+    forEachSegment(^(const char* segName, uint32_t segFileOffset, uint32_t segFileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, bool& stop) {
+        if ( strcmp(segName, "__TEXT") == 0 ) {
+            if ( protections != (VM_PROT_READ|VM_PROT_EXECUTE) ) {
+                diag.warning("__TEXT segment permissions is not 'r-x'");
+                badPermissions = true;
+                stop = true;
+            }
+            hasTEXT = true;
+        }
+        else if ( strcmp(segName, "__LINKEDIT") == 0 ) {
+            if ( protections != VM_PROT_READ ) {
+                diag.warning("__LINKEDIT segment permissions is not 'r--'");
+                badPermissions = true;
+                stop = true;
+            }
+            hasLINKEDIT = true;
+        }
+        else if ( (protections & 0xFFFFFFF8) != 0 ) {
+            diag.warning("%s segment permissions has invalid bits set", segName);
+            badPermissions = true;
+            stop = true;
+        }
+        if (greaterThanAddOrOverflow(segFileOffset, segFileSize, fileLen)) {
+            diag.warning("%s segment content extends beyond end of file", segName);
+            badSize = true;
+            stop = true;
+        }
+        if ( is64() ) {
+            if ( vmAddr+vmSize < vmAddr ) {
+                diag.warning("%s segment vm range wraps", segName);
+                badSize = true;
+                stop = true;
+            }
+       }
+       else {
+            if ( (uint32_t)(vmAddr+vmSize) < (uint32_t)(vmAddr) ) {
+                diag.warning("%s segment vm range wraps", segName);
+                badSize = true;
+                stop = true;
+            }
+       }
+    });
+    if ( badPermissions || badSize )
+        return false;
+    if ( !hasTEXT ) {
+       diag.warning("missing __TEXT segment");
+       return false;
+    }
+    if ( !hasLINKEDIT ) {
+       diag.warning("missing __LINKEDIT segment");
+       return false;
+    }
+
+    // check for overlapping segments
+    __block bool badSegments = false;
+    forEachSegment(^(const char* seg1Name, uint32_t seg1FileOffset, uint32_t seg1FileSize, uint64_t seg1vmAddr, uint64_t seg1vmSize, uint8_t seg1Protections, uint32_t seg1Index, uint64_t seg1SizeOfSections, uint8_t seg1Align, bool& stop1) {
+        uint64_t seg1vmEnd   = seg1vmAddr + seg1vmSize;
+        uint32_t seg1FileEnd = seg1FileOffset + seg1FileSize;
+        forEachSegment(^(const char* seg2Name, uint32_t seg2FileOffset, uint32_t seg2FileSize, uint64_t seg2vmAddr, uint64_t seg2vmSize, uint8_t seg2Protections, uint32_t seg2Index, uint64_t seg2SizeOfSections, uint8_t seg2Align, bool& stop2) {
+            if ( seg1Index == seg2Index )
+                return;
+            uint64_t seg2vmEnd   = seg2vmAddr + seg2vmSize;
+            uint32_t seg2FileEnd = seg2FileOffset + seg2FileSize;
+            if ( ((seg2vmAddr <= seg1vmAddr) && (seg2vmEnd > seg1vmAddr) && (seg1vmEnd > seg1vmAddr)) || ((seg2vmAddr >= seg1vmAddr) && (seg2vmAddr < seg1vmEnd) && (seg2vmEnd > seg2vmAddr)) ) {
+                diag.warning("segment %s vm range overlaps segment %s", seg1Name, seg2Name);
+                badSegments = true;
+                stop1 = true;
+                stop2 = true;
+            }
+             if ( ((seg2FileOffset <= seg1FileOffset) && (seg2FileEnd > seg1FileOffset) && (seg1FileEnd > seg1FileOffset)) || ((seg2FileOffset >= seg1FileOffset) && (seg2FileOffset < seg1FileEnd) && (seg2FileEnd > seg2FileOffset)) ) {
+                diag.warning("segment %s file content overlaps segment %s", seg1Name, seg2Name);
+                badSegments = true;
+                stop1 = true;
+                stop2 = true;
+            }
+            // check for out of order segments
+            if ( (seg1Index < seg2Index) && !stop1 ) {
+                if ( (seg1vmAddr > seg2vmAddr) || ((seg1FileOffset > seg2FileOffset) && (seg1FileOffset != 0) && (seg2FileOffset != 0)) ){
+                    diag.warning("segment load commands out of order with respect to layout for %s and %s", seg1Name, seg2Name);
+                    badSegments = true;
+                    stop1 = true;
+                    stop2 = true;
+                }
+            }
+        });
+    });
+    if ( badSegments )
+        return false;
+
+    // check sections are within segment
+    __block bool badSections = false;
+    forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
+        if ( cmd->cmd == LC_SEGMENT_64 ) {
+            const segment_command_64* seg = (segment_command_64*)cmd;
+            const section_64* const sectionsStart = (section_64*)((char*)seg + sizeof(struct segment_command_64));
+            const section_64* const sectionsEnd   = &sectionsStart[seg->nsects];
+            for (const section_64* sect=sectionsStart; (sect < sectionsEnd); ++sect) {
+                if ( (int64_t)(sect->size) < 0 ) {
+                    diag.warning("section %s size too large 0x%llX", sect->sectname, sect->size);
+                    badSections = true;
+                }
+                else if ( sect->addr < seg->vmaddr ) {
+                    diag.warning("section %s start address 0x%llX is before containing segment's address 0x%0llX",  sect->sectname, sect->addr, seg->vmaddr);
+                    badSections = true;
+                }
+                else if ( sect->addr+sect->size > seg->vmaddr+seg->vmsize ) {
+                    diag.warning("section %s end address 0x%llX is beyond containing segment's end address 0x%0llX", sect->sectname, sect->addr+sect->size, seg->vmaddr+seg->vmsize);
+                    badSections = true;
+                }
+            }
+        }
+        else if ( cmd->cmd == LC_SEGMENT ) {
+            const segment_command* seg = (segment_command*)cmd;
+            const section* const sectionsStart = (section*)((char*)seg + sizeof(struct segment_command));
+            const section* const sectionsEnd   = &sectionsStart[seg->nsects];
+            for (const section* sect=sectionsStart; !stop && (sect < sectionsEnd); ++sect) {
+               if ( (int64_t)(sect->size) < 0 ) {
+                    diag.warning("section %s size too large 0x%X", sect->sectname, sect->size);
+                    badSections = true;
+                }
+                else if ( sect->addr < seg->vmaddr ) {
+                    diag.warning("section %s start address 0x%X is before containing segment's address 0x%0X",  sect->sectname, sect->addr, seg->vmaddr);
+                    badSections = true;
+                }
+                else if ( sect->addr+sect->size > seg->vmaddr+seg->vmsize ) {
+                    diag.warning("section %s end address 0x%X is beyond containing segment's end address 0x%0X", sect->sectname, sect->addr+sect->size, seg->vmaddr+seg->vmsize);
+                    badSections = true;
+                }
+            }
+        }
+    });
+
+    return !badSections;
+}
+
+struct LinkEditContent
+{
+    const char* name;
+    uint32_t    stdOrder;
+    uint32_t    fileOffsetStart;
+    uint32_t    size;
+};
+
+
+
+bool MachOParser::validLinkeditLayout(Diagnostics& diag)
+{
+    LinkEditInfo leInfo;
+    getLinkEditPointers(diag, leInfo);
+    if ( diag.hasError() )
+        return false;
+    const bool     is64Bit = is64();
+    const uint32_t pointerSize = (is64Bit ? 8 : 4);
+
+    // build vector of all blobs in LINKEDIT
+    std::vector<LinkEditContent> blobs;
+    if ( leInfo.dyldInfo != nullptr ) {
+        if ( leInfo.dyldInfo->rebase_size != 0 )
+            blobs.push_back({"rebase opcodes",         1, leInfo.dyldInfo->rebase_off, leInfo.dyldInfo->rebase_size});
+        if ( leInfo.dyldInfo->bind_size != 0 )
+            blobs.push_back({"bind opcodes",           2, leInfo.dyldInfo->bind_off, leInfo.dyldInfo->bind_size});
+        if ( leInfo.dyldInfo->weak_bind_size != 0 )
+            blobs.push_back({"weak bind opcodes",      3, leInfo.dyldInfo->weak_bind_off, leInfo.dyldInfo->weak_bind_size});
+        if ( leInfo.dyldInfo->lazy_bind_size != 0 )
+            blobs.push_back({"lazy bind opcodes",      4, leInfo.dyldInfo->lazy_bind_off, leInfo.dyldInfo->lazy_bind_size});
+        if ( leInfo.dyldInfo->export_size!= 0 )
+            blobs.push_back({"exports trie",           5, leInfo.dyldInfo->export_off, leInfo.dyldInfo->export_size});
+    }
+    if ( leInfo.dynSymTab != nullptr ) {
+        if ( leInfo.dynSymTab->nlocrel != 0 )
+            blobs.push_back({"local relocations",      6, leInfo.dynSymTab->locreloff, static_cast<uint32_t>(leInfo.dynSymTab->nlocrel*sizeof(relocation_info))});
+        if ( leInfo.dynSymTab->nextrel != 0 )
+            blobs.push_back({"external relocations",  11, leInfo.dynSymTab->extreloff, static_cast<uint32_t>(leInfo.dynSymTab->nextrel*sizeof(relocation_info))});
+        if ( leInfo.dynSymTab->nindirectsyms != 0 )
+            blobs.push_back({"indirect symbol table", 12, leInfo.dynSymTab->indirectsymoff, leInfo.dynSymTab->nindirectsyms*4});
+    }
+    if ( leInfo.splitSegInfo != nullptr ) {
+        if ( leInfo.splitSegInfo->datasize != 0 )
+            blobs.push_back({"shared cache info",      6, leInfo.splitSegInfo->dataoff, leInfo.splitSegInfo->datasize});
+    }
+    if ( leInfo.functionStarts != nullptr ) {
+        if ( leInfo.functionStarts->datasize != 0 )
+            blobs.push_back({"function starts",        7, leInfo.functionStarts->dataoff, leInfo.functionStarts->datasize});
+    }
+    if ( leInfo.dataInCode != nullptr ) {
+        if ( leInfo.dataInCode->datasize != 0 )
+            blobs.push_back({"data in code",           8, leInfo.dataInCode->dataoff, leInfo.dataInCode->datasize});
+    }
+    if ( leInfo.symTab != nullptr ) {
+        if ( leInfo.symTab->nsyms != 0 )
+            blobs.push_back({"symbol table",         10, leInfo.symTab->symoff, static_cast<uint32_t>(leInfo.symTab->nsyms*(is64Bit ? sizeof(nlist_64) : sizeof(struct nlist)))});
+        if ( leInfo.symTab->strsize != 0 )
+            blobs.push_back({"symbol table strings", 20, leInfo.symTab->stroff, leInfo.symTab->strsize});
+    }
+    if ( leInfo.codeSig != nullptr ) {
+        if ( leInfo.codeSig->datasize != 0 )
+            blobs.push_back({"code signature",       21, leInfo.codeSig->dataoff, leInfo.codeSig->datasize});
+    }
+
+    // check for bad combinations
+    if ( (leInfo.dyldInfo != nullptr) && (leInfo.dyldInfo->cmd == LC_DYLD_INFO_ONLY) && (leInfo.dynSymTab != nullptr) ) {
+        if ( leInfo.dynSymTab->nlocrel != 0 ) {
+            diag.error("malformed mach-o contains LC_DYLD_INFO_ONLY and local relocations");
+            return false;
+        }
+        if ( leInfo.dynSymTab->nextrel != 0 ) {
+            diag.error("malformed mach-o contains LC_DYLD_INFO_ONLY and external relocations");
+            return false;
+        }
+    }
+    if ( (leInfo.dyldInfo == nullptr) && (leInfo.dynSymTab == nullptr) ) {
+        diag.error("malformed mach-o misssing LC_DYLD_INFO and LC_DYSYMTAB");
+        return false;
+    }
+    if ( blobs.empty() ) {
+        diag.error("malformed mach-o misssing LINKEDIT");
+        return false;
+    }
+
+    // sort vector by file offset and error on overlaps
+    std::sort(blobs.begin(), blobs.end(), [&](const LinkEditContent& a, const LinkEditContent& b) {
+        return a.fileOffsetStart < b.fileOffsetStart;
+    });
+    uint32_t     prevEnd = (uint32_t)(leInfo.layout.segments[leInfo.layout.linkeditSegIndex].fileOffset);
+    const char*  prevName = "start of LINKEDIT";
+    for (const LinkEditContent& blob : blobs) {
+        if ( blob.fileOffsetStart < prevEnd ) {
+            diag.error("LINKEDIT overlap of %s and %s", prevName, blob.name);
+            return false;
+        }
+        prevEnd  = blob.fileOffsetStart + blob.size;
+        prevName = blob.name;
+    }
+    const LinkEditContent& lastBlob = blobs.back();
+    uint32_t linkeditFileEnd = (uint32_t)(leInfo.layout.segments[leInfo.layout.linkeditSegIndex].fileOffset + leInfo.layout.segments[leInfo.layout.linkeditSegIndex].fileSize);
+    if (greaterThanAddOrOverflow(lastBlob.fileOffsetStart, lastBlob.size, linkeditFileEnd)) {
+        diag.error("LINKEDIT content '%s' extends beyond end of segment", lastBlob.name);
+        return false;
+    }
+
+    // sort vector by order and warn on non standard order or mis-alignment
+    std::sort(blobs.begin(), blobs.end(), [&](const LinkEditContent& a, const LinkEditContent& b) {
+        return a.stdOrder < b.stdOrder;
+    });
+    prevEnd = (uint32_t)(leInfo.layout.segments[leInfo.layout.linkeditSegIndex].fileOffset);
+    prevName = "start of LINKEDIT";
+    for (const LinkEditContent& blob : blobs) {
+        if ( ((blob.fileOffsetStart & (pointerSize-1)) != 0) && (blob.stdOrder != 20) )  // ok for "symbol table strings" to be mis-aligned
+            diag.warning("mis-aligned LINKEDIT content '%s'", blob.name);
+        if ( blob.fileOffsetStart < prevEnd ) {
+            diag.warning("LINKEDIT out of order %s", blob.name);
+        }
+        prevEnd  = blob.fileOffsetStart;
+        prevName = blob.name;
+    }
+
+    // Check for invalid symbol table sizes
+    if ( leInfo.symTab != nullptr ) {
+        if ( leInfo.symTab->nsyms > 0x10000000 ) {
+            diag.error("malformed mach-o image: symbol table too large");
+            return false;
+        }
+        if ( leInfo.dynSymTab != nullptr ) {
+            // validate indirect symbol table
+            if ( leInfo.dynSymTab->nindirectsyms != 0 ) {
+                if ( leInfo.dynSymTab->nindirectsyms > 0x10000000 ) {
+                    diag.error("malformed mach-o image: indirect symbol table too large");
+                    return false;
+                }
+            }
+            if ( (leInfo.dynSymTab->nlocalsym > leInfo.symTab->nsyms) || (leInfo.dynSymTab->ilocalsym > leInfo.symTab->nsyms) ) {
+                diag.error("malformed mach-o image: indirect symbol table local symbol count exceeds total symbols");
+                return false;
+            }
+            if ( leInfo.dynSymTab->ilocalsym + leInfo.dynSymTab->nlocalsym < leInfo.dynSymTab->ilocalsym  ) {
+                diag.error("malformed mach-o image: indirect symbol table local symbol count wraps");
+                return false;
+            }
+            if ( (leInfo.dynSymTab->nextdefsym > leInfo.symTab->nsyms) || (leInfo.dynSymTab->iextdefsym > leInfo.symTab->nsyms) ) {
+                diag.error("malformed mach-o image: indirect symbol table extern symbol count exceeds total symbols");
+                return false;
+            }
+            if ( leInfo.dynSymTab->iextdefsym + leInfo.dynSymTab->nextdefsym < leInfo.dynSymTab->iextdefsym  ) {
+                diag.error("malformed mach-o image: indirect symbol table extern symbol count wraps");
+                return false;
+            }
+            if ( (leInfo.dynSymTab->nundefsym > leInfo.symTab->nsyms) || (leInfo.dynSymTab->iundefsym > leInfo.symTab->nsyms) ) {
+                diag.error("malformed mach-o image: indirect symbol table undefined symbol count exceeds total symbols");
+                return false;
+            }
+            if ( leInfo.dynSymTab->iundefsym + leInfo.dynSymTab->nundefsym < leInfo.dynSymTab->iundefsym  ) {
+                diag.error("malformed mach-o image: indirect symbol table undefined symbol count wraps");
+                return false;
+            }
+        }
+    }
+
+    return true;
+}
+
+bool MachOParser::isArch(const mach_header* mh, const std::string& archName)
+{
+    for (const ArchInfo& info : _s_archInfos) {
+        if ( archName == info.name ) {
+            return ( (mh->cputype == info.cputype) && ((mh->cpusubtype & ~CPU_SUBTYPE_MASK) == info.cpusubtype) );
+        }
+    }
+    return false;
+}
+
+
+std::string MachOParser::archName(uint32_t cputype, uint32_t cpusubtype)
+{
+    for (const ArchInfo& info : _s_archInfos) {
+        if ( (cputype == info.cputype) && ((cpusubtype & ~CPU_SUBTYPE_MASK) == info.cpusubtype) ) {
+            return info.name;
+        }
+    }
+    return "unknown";
+}
+
+uint32_t MachOParser::cpuTypeFromArchName(const std::string& archName)
+{
+    for (const ArchInfo& info : _s_archInfos) {
+        if ( archName == info.name ) {
+            return info.cputype;
+        }
+    }
+    return 0;
+}
+
+uint32_t MachOParser::cpuSubtypeFromArchName(const std::string& archName)
+{
+    for (const ArchInfo& info : _s_archInfos) {
+        if ( archName == info.name ) {
+            return info.cpusubtype;
+        }
+    }
+    return 0;
+}
+
+std::string MachOParser::archName() const
+{
+    return archName(header()->cputype, header()->cpusubtype);
+}
+
+std::string MachOParser::platformName(Platform platform)
+{
+    switch ( platform ) {
+        case Platform::unknown:
+            return "unknown";
+        case Platform::macOS:
+            return "macOS";
+        case Platform::iOS:
+            return "iOS";
+        case Platform::tvOS:
+            return "tvOS";
+        case Platform::watchOS:
+            return "watchOS";
+        case Platform::bridgeOS:
+            return "bridgeOS";
+    }
+    return "unknown platform";
+}
+
+std::string MachOParser::versionString(uint32_t packedVersion)
+{
+    char buff[64];
+    sprintf(buff, "%d.%d.%d", (packedVersion >> 16), ((packedVersion >> 8) & 0xFF), (packedVersion & 0xFF));
+    return buff;
+}
+
+#else
+
+bool MachOParser::isMachO(Diagnostics& diag, const void* fileContent, size_t mappedLength)
+{
+    // sanity check length
+    if ( mappedLength < 4096 ) {
+        diag.error("file too short");
+        return false;
+    }
+
+    // must start with mach-o magic value
+    const mach_header* mh = (const mach_header*)fileContent;
+#if __LP64__
+    const uint32_t requiredMagic = MH_MAGIC_64;
+#else
+    const uint32_t requiredMagic = MH_MAGIC;
+#endif
+   if ( mh->magic != requiredMagic ) {
+        diag.error("not a mach-o file");
+        return false;
+    }
+
+#if __x86_64__
+    const uint32_t requiredCPU = CPU_TYPE_X86_64;
+#elif __i386__
+    const uint32_t requiredCPU = CPU_TYPE_I386;
+#elif __arm__
+    const uint32_t requiredCPU = CPU_TYPE_ARM;
+#elif __arm64__
+    const uint32_t requiredCPU = CPU_TYPE_ARM64;
+#else
+    #error unsupported architecture
+#endif
+    if ( mh->cputype != requiredCPU ) {
+        diag.error("wrong cpu type");
+        return false;
+    }
+
+    return true;
+}
+
+bool MachOParser::wellFormedMachHeaderAndLoadCommands(const mach_header* mh)
+{
+    const load_command* startCmds = nullptr;
+    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;
+        }
+        cmd = nextCmd;
+    }
+    return true;
+}
+
+#endif
+
+Platform MachOParser::currentPlatform()
+{
+#if TARGET_OS_BRIDGE
+    return Platform::bridgeOS;
+#elif TARGET_OS_WATCH
+    return Platform::watchOS;
+#elif TARGET_OS_TV
+    return Platform::tvOS;
+#elif TARGET_OS_IOS
+    return Platform::iOS;
+#elif TARGET_OS_MAC
+    return Platform::macOS;
+#else
+    #error unknown platform
+#endif
+}
+
+
+bool MachOParser::valid(Diagnostics& diag)
+{
+#if DYLD_IN_PROCESS
+    // only images loaded by dyld to be parsed
+    const mach_header* inImage = dyld3::dyld_image_header_containing_address(header());
+    if ( inImage != header() ) {
+        diag.error("only dyld loaded images can be parsed by MachOParser");
+        return false;
+    }
+#else
+
+#endif
+    return true;
+}
+
+
+void MachOParser::forEachLoadCommand(Diagnostics& diag, void (^callback)(const load_command* cmd, bool& stop)) const
+{
+    bool stop = false;
+    const load_command* startCmds = nullptr;
+    if ( header()->magic == MH_MAGIC_64 )
+        startCmds = (load_command*)((char *)header() + sizeof(mach_header_64));
+    else if ( header()->magic == MH_MAGIC )
+        startCmds = (load_command*)((char *)header() + sizeof(mach_header));
+    else {
+        diag.error("file does not start with MH_MAGIC[_64]");
+        return;  // not a mach-o file, or wrong endianness
+    }
+    const load_command* const cmdsEnd = (load_command*)((char*)startCmds + header()->sizeofcmds);
+    const load_command* cmd = startCmds;
+    for(uint32_t i = 0; i < header()->ncmds; ++i) {
+        const load_command* nextCmd = (load_command*)((char *)cmd + cmd->cmdsize);
+        if ( cmd->cmdsize < 8 ) {
+            diag.error("malformed load command #%d, size too small %d", i, cmd->cmdsize);
+            return;
+        }
+        if ( (nextCmd > cmdsEnd) || (nextCmd < startCmds) ) {
+            diag.error("malformed load command #%d, size too large 0x%X", i, cmd->cmdsize);
+            return;
+        }
+        callback(cmd, stop);
+        if ( stop )
+            return;
+        cmd = nextCmd;
+    }
+}
+
+UUID MachOParser::uuid() const
+{
+    uuid_t uuid;
+    getUuid(uuid);
+    return uuid;
+}
+
+bool MachOParser::getUuid(uuid_t uuid) const
+{
+    Diagnostics diag;
+    __block bool found = false;
+    forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
+        if ( cmd->cmd == LC_UUID ) {
+            const uuid_command* uc = (const uuid_command*)cmd;
+            memcpy(uuid, uc->uuid, sizeof(uuid_t));
+            found = true;
+            stop = true;
+        }
+    });
+    diag.assertNoError();   // any malformations in the file should have been caught by earlier validate() call
+    if ( !found )
+        bzero(uuid, sizeof(uuid_t));
+    return found;
+}
+
+uint64_t MachOParser::preferredLoadAddress() const
+{
+    __block uint64_t result = 0;
+    forEachSegment(^(const char* segName, uint32_t fileOffset, uint32_t fileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, bool& stop) {
+        if ( strcmp(segName, "__TEXT") == 0 ) {
+            result = vmAddr;
+            stop = true;
+        }
+    });
+    return result;
+}
+
+bool MachOParser::getPlatformAndVersion(Platform* platform, uint32_t* minOS, uint32_t* sdk) const
+{
+    Diagnostics diag;
+    __block bool found = false;
+    forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
+        const version_min_command* versCmd;
+        switch ( cmd->cmd ) {
+            case LC_VERSION_MIN_IPHONEOS:
+                versCmd       = (version_min_command*)cmd;
+                *platform     = Platform::iOS;
+                *minOS        = versCmd->version;
+                *sdk          = versCmd->sdk;
+                found = true;
+                stop = true;
+                break;
+           case LC_VERSION_MIN_MACOSX:
+                 versCmd       = (version_min_command*)cmd;
+                *platform     = Platform::macOS;
+                *minOS        = versCmd->version;
+                *sdk          = versCmd->sdk;
+                found = true;
+                stop = true;
+                break;
+           case LC_VERSION_MIN_TVOS:
+                 versCmd       = (version_min_command*)cmd;
+                *platform     = Platform::tvOS;
+                *minOS        = versCmd->version;
+                *sdk          = versCmd->sdk;
+                found = true;
+                stop = true;
+                break;
+           case LC_VERSION_MIN_WATCHOS:
+                versCmd       = (version_min_command*)cmd;
+                *platform     = Platform::watchOS;
+                *minOS        = versCmd->version;
+                *sdk          = versCmd->sdk;
+                found = true;
+                stop = true;
+                break;
+            case LC_BUILD_VERSION: {
+                const build_version_command* buildCmd = (build_version_command *)cmd;
+                *minOS        = buildCmd->minos;
+                *sdk          = buildCmd->sdk;
+
+                switch(buildCmd->platform) {
+                        /* Known values for the platform field above. */
+                    case PLATFORM_MACOS:
+                        *platform = Platform::macOS;
+                        break;
+                    case PLATFORM_IOS:
+                        *platform = Platform::iOS;
+                        break;
+                    case PLATFORM_TVOS:
+                        *platform = Platform::tvOS;
+                        break;
+                    case PLATFORM_WATCHOS:
+                        *platform = Platform::watchOS;
+                        break;
+                    case PLATFORM_BRIDGEOS:
+                        *platform = Platform::bridgeOS;
+                        break;
+                }
+                found = true;
+                stop = true;
+            } break;
+        }
+    });
+    diag.assertNoError();   // any malformations in the file should have been caught by earlier validate() call
+    return found;
+}
+
+
+bool MachOParser::isSimulatorBinary() const
+{
+    Platform platform;
+    uint32_t minOS;
+    uint32_t sdk;
+    switch ( header()->cputype ) {
+        case CPU_TYPE_I386:
+        case CPU_TYPE_X86_64:
+            if ( getPlatformAndVersion(&platform, &minOS, &sdk) ) {
+                return (platform != Platform::macOS);
+            }
+            break;
+    }
+    return false;
+}
+
+
+bool MachOParser::getDylibInstallName(const char** installName, uint32_t* compatVersion, uint32_t* currentVersion) const
+{
+    Diagnostics diag;
+    __block bool found = false;
+    forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
+        if ( cmd->cmd == LC_ID_DYLIB ) {
+            const dylib_command*  dylibCmd = (dylib_command*)cmd;
+            *compatVersion  = dylibCmd->dylib.compatibility_version;
+            *currentVersion = dylibCmd->dylib.current_version;
+            *installName    = (char*)dylibCmd + dylibCmd->dylib.name.offset;
+            found = true;
+            stop = true;
+        }
+    });
+    diag.assertNoError();   // any malformations in the file should have been caught by earlier validate() call
+    return found;
+}
+
+const char* MachOParser::installName() const
+{
+    assert(header()->filetype == MH_DYLIB);
+    const char* result;
+    uint32_t    ignoreVersion;
+    assert(getDylibInstallName(&result, &ignoreVersion, &ignoreVersion));
+    return result;
+}
+
+
+uint32_t MachOParser::dependentDylibCount() const
+{
+    __block uint32_t count = 0;
+    forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool& stop) {
+        ++count;
+    });
+    return count;
+}
+
+const char* MachOParser::dependentDylibLoadPath(uint32_t depIndex) const
+{
+    __block const char* foundLoadPath = nullptr;
+    __block uint32_t curDepIndex = 0;
+    forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool& stop) {
+        if ( curDepIndex == depIndex ) {
+            foundLoadPath = loadPath;
+            stop = true;
+        }
+        ++curDepIndex;
+    });
+    return foundLoadPath;
+}
+
+
+void MachOParser::forEachDependentDylib(void (^callback)(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool& stop)) const
+{
+    Diagnostics diag;
+    forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
+         switch ( cmd->cmd ) {
+            case LC_LOAD_DYLIB:
+            case LC_LOAD_WEAK_DYLIB:
+            case LC_REEXPORT_DYLIB:
+            case LC_LOAD_UPWARD_DYLIB: {
+                const dylib_command* dylibCmd = (dylib_command*)cmd;
+                assert(dylibCmd->dylib.name.offset < cmd->cmdsize);
+                const char* loadPath = (char*)dylibCmd + dylibCmd->dylib.name.offset;
+                callback(loadPath, (cmd->cmd == LC_LOAD_WEAK_DYLIB), (cmd->cmd == LC_REEXPORT_DYLIB), (cmd->cmd == LC_LOAD_UPWARD_DYLIB),
+                                    dylibCmd->dylib.compatibility_version, dylibCmd->dylib.current_version, stop);
+            }
+            break;
+        }
+    });
+    diag.assertNoError();   // any malformations in the file should have been caught by earlier validate() call
+}
+
+void MachOParser::forEachRPath(void (^callback)(const char* rPath, bool& stop)) const
+{
+    Diagnostics diag;
+    forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
+         if ( cmd->cmd == LC_RPATH ) {
+            const char* rpath = (char*)cmd + ((struct rpath_command*)cmd)->path.offset;
+            callback(rpath, stop);
+        }
+    });
+    diag.assertNoError();   // any malformations in the file should have been caught by earlier validate() call
+}
+
+/*
+   struct LayoutInfo {
+#if DYLD_IN_PROCESS
+        uintptr_t    slide;
+        uintptr_t    textUnslidVMAddr;
+        uintptr_t    linkeditUnslidVMAddr;
+        uint32_t     linkeditFileOffset;
+#else
+        uint32_t     segmentCount;
+        uint32_t     linkeditSegIndex;
+        struct {
+            uint64_t    mappingOffset;
+            uint64_t    fileOffset;
+            uint64_t    segUnslidAddress;
+            uint64_t    segSize;
+        }            segments[16];
+#endif
+    };
+*/
+
+#if !DYLD_IN_PROCESS
+const uint8_t* MachOParser::getContentForVMAddr(const LayoutInfo& info, uint64_t addr) const
+{
+    for (uint32_t i=0; i < info.segmentCount; ++i) {
+        if ( (addr >= info.segments[i].segUnslidAddress) && (addr < (info.segments[i].segUnslidAddress+info.segments[i].segSize)) )
+            return (uint8_t*)header() + info.segments[i].mappingOffset + (addr - info.segments[i].segUnslidAddress);
+    }
+    // value is outside this image.  could be pointer into another image
+    if ( inDyldCache() ) {
+        return (uint8_t*)header() + info.segments[0].mappingOffset + (addr - info.segments[0].segUnslidAddress);
+    }
+    assert(0 && "address not found in segment");
+    return nullptr;
+}
+#endif
+
+const uint8_t* MachOParser::getLinkEditContent(const LayoutInfo& info, uint32_t fileOffset) const
+{
+#if DYLD_IN_PROCESS
+    uint32_t offsetInLinkedit   = fileOffset - info.linkeditFileOffset;
+    uintptr_t linkeditStartAddr = info.linkeditUnslidVMAddr + info.slide;
+    return (uint8_t*)(linkeditStartAddr + offsetInLinkedit);
+#else
+    uint32_t offsetInLinkedit    = fileOffset - (uint32_t)(info.segments[info.linkeditSegIndex].fileOffset);
+    const uint8_t* linkeditStart = (uint8_t*)header() + info.segments[info.linkeditSegIndex].mappingOffset;
+    return linkeditStart + offsetInLinkedit;
+#endif
+}
+
+
+void MachOParser::getLayoutInfo(LayoutInfo& result) const
+{
+#if DYLD_IN_PROCESS
+    // image loaded by dyld, just record the addr and file offset of TEXT and LINKEDIT segments
+    result.slide = getSlide();
+    forEachSegment(^(const char* segName, uint32_t fileOffset, uint32_t fileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, bool& stop) {
+        if ( strcmp(segName, "__TEXT") == 0 ) {
+            result.textUnslidVMAddr = (uintptr_t)vmAddr;
+        }
+        else if ( strcmp(segName, "__LINKEDIT") == 0 ) {
+            result.linkeditUnslidVMAddr = (uintptr_t)vmAddr;
+            result.linkeditFileOffset   = fileOffset;
+        }
+    });
+#else
+    bool inCache = inDyldCache();
+    bool intel32 = (header()->cputype == CPU_TYPE_I386);
+    result.segmentCount = 0;
+    result.linkeditSegIndex = 0xFFFFFFFF;
+    __block uint64_t textSegAddr = 0;
+    __block uint64_t textSegFileOffset = 0;
+    forEachSegment(^(const char* segName, uint32_t fileOffset, uint32_t fileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, bool& stop) {
+        auto& segInfo = result.segments[result.segmentCount];
+        if ( strcmp(segName, "__TEXT") == 0 ) {
+            textSegAddr       = vmAddr;
+            textSegFileOffset = fileOffset;
+        }
+        __block bool textRelocsAllowed = false;
+        if ( intel32 ) {
+            forEachSection(^(const char* curSegName, uint32_t segIndex, uint64_t segVMAddr, const char* sectionName, uint32_t sectFlags,
+                             uint64_t sectAddr, uint64_t size, uint32_t alignP2, uint32_t reserved1, uint32_t reserved2, bool illegalSectionSize, bool& sectStop) {
+                if ( strcmp(curSegName, segName) == 0 ) {
+                    if ( sectFlags & (S_ATTR_EXT_RELOC|S_ATTR_LOC_RELOC) ) {
+                        textRelocsAllowed = true;
+                        sectStop = true;
+                    }
+                }
+            });
+        }
+        if ( inCache ) {
+            if ( inRawCache() ) {
+                // whole cache file mapped somewhere (padding not expanded)
+                // vmaddrs are useless. only file offset make sense
+                segInfo.mappingOffset = fileOffset - textSegFileOffset;
+            }
+            else {
+                // cache file was loaded by dyld into shared region
+                // vmaddrs of segments are correct except for ASLR slide
+                segInfo.mappingOffset = vmAddr - textSegAddr;
+           }
+        }
+        else {
+            // individual mach-o file mapped in one region, so mappingOffset == fileOffset
+            segInfo.mappingOffset    = fileOffset;
+        }
+        segInfo.fileOffset        = fileOffset;
+        segInfo.fileSize          = fileSize;
+        segInfo.segUnslidAddress  = vmAddr;
+        segInfo.segSize           = vmSize;
+        segInfo.writable          = ((protections & VM_PROT_WRITE)   == VM_PROT_WRITE);
+        segInfo.executable        = ((protections & VM_PROT_EXECUTE) == VM_PROT_EXECUTE);
+        segInfo.textRelocsAllowed = textRelocsAllowed;
+        if ( strcmp(segName, "__LINKEDIT") == 0 ) {
+            result.linkeditSegIndex = result.segmentCount;
+        }
+        ++result.segmentCount;
+        if ( result.segmentCount > 127 )
+            stop = true;
+    });
+#endif
+}
+
+
+void MachOParser::forEachSection(void (^callback)(const char* segName, const char* sectionName, uint32_t flags,
+                                                  const void* content, size_t size, bool illegalSectionSize, bool& stop)) const
+{
+    forEachSection(^(const char* segName, const char* sectionName, uint32_t flags, uint64_t addr,
+                     const void* content, uint64_t size, uint32_t alignP2, uint32_t reserved1, uint32_t reserved2, bool illegalSectionSize, bool& stop) {
+        callback(segName, sectionName, flags, content, (size_t)size, illegalSectionSize, stop);
+    });
+}
+
+void MachOParser::forEachSection(void (^callback)(const char* segName, const char* sectionName, uint32_t flags, uint64_t addr,
+                                                  const void* content, uint64_t size, uint32_t alignP2, uint32_t reserved1, uint32_t reserved2,
+                                                  bool illegalSectionSize, bool& stop)) const
+{
+    Diagnostics diag;
+    //fprintf(stderr, "forEachSection() mh=%p\n", header());
+    LayoutInfo layout;
+    getLayoutInfo(layout);
+    forEachSection(^(const char* segName, uint32_t segIndex, uint64_t segVMAddr, const char* sectionName, uint32_t sectFlags,
+                      uint64_t sectAddr, uint64_t sectSize, uint32_t alignP2, uint32_t reserved1, uint32_t reserved2, bool illegalSectionSize, bool& stop) {
+    #if DYLD_IN_PROCESS
+        const uint8_t* segContentStart = (uint8_t*)(segVMAddr + layout.slide);
+    #else
+        const uint8_t* segContentStart = (uint8_t*)header() + layout.segments[segIndex].mappingOffset;
+    #endif
+        const void* contentAddr = segContentStart + (sectAddr - segVMAddr);
+        callback(segName, sectionName, sectFlags, sectAddr, contentAddr, sectSize, alignP2, reserved1, reserved2, illegalSectionSize, stop);
+    });
+
+}
+
+// this iterator just walks the segment/section array.  It does interpret addresses
+void MachOParser::forEachSection(void (^callback)(const char* segName, uint32_t segIndex, uint64_t segVMAddr, const char* sectionName, uint32_t sectFlags,
+                                 uint64_t sectAddr, uint64_t size, uint32_t alignP2, uint32_t reserved1, uint32_t reserved2, bool illegalSectionSize, bool& stop)) const
+{
+    Diagnostics diag;
+    //fprintf(stderr, "forEachSection() mh=%p\n", header());
+    __block uint32_t segIndex = 0;
+    forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
+        if ( cmd->cmd == LC_SEGMENT_64 ) {
+            const segment_command_64* seg = (segment_command_64*)cmd;
+            const section_64* const sectionsStart = (section_64*)((char*)seg + sizeof(struct segment_command_64));
+            const section_64* const sectionsEnd   = &sectionsStart[seg->nsects];
+            for (const section_64* sect=sectionsStart; !stop && (sect < sectionsEnd); ++sect) {
+                const char* sectName = sect->sectname;
+                char sectNameCopy[20];
+                if ( sectName[15] != '\0' ) {
+                    strlcpy(sectNameCopy, sectName, 17);
+                    sectName = sectNameCopy;
+                }
+                bool illegalSectionSize = (sect->addr < seg->vmaddr) || greaterThanAddOrOverflow(sect->addr, sect->size, seg->vmaddr + seg->filesize);
+                callback(seg->segname, segIndex, seg->vmaddr, sectName, sect->flags, sect->addr, sect->size, sect->align, sect->reserved1, sect->reserved2, illegalSectionSize, stop);
+            }
+            ++segIndex;
+        }
+        else if ( cmd->cmd == LC_SEGMENT ) {
+            const segment_command* seg = (segment_command*)cmd;
+            const section* const sectionsStart = (section*)((char*)seg + sizeof(struct segment_command));
+            const section* const sectionsEnd   = &sectionsStart[seg->nsects];
+            for (const section* sect=sectionsStart; !stop && (sect < sectionsEnd); ++sect) {
+                const char* sectName = sect->sectname;
+                char sectNameCopy[20];
+                if ( sectName[15] != '\0' ) {
+                    strlcpy(sectNameCopy, sectName, 17);
+                    sectName = sectNameCopy;
+                }
+                bool illegalSectionSize = (sect->addr < seg->vmaddr) || greaterThanAddOrOverflow(sect->addr, sect->size, seg->vmaddr + seg->filesize);
+                callback(seg->segname, segIndex, seg->vmaddr, sectName, sect->flags, sect->addr, sect->size, sect->align, sect->reserved1, sect->reserved2, illegalSectionSize, stop);
+            }
+            ++segIndex;
+        }
+    });
+    diag.assertNoError();   // any malformations in the file should have been caught by earlier validate() call
+}
+
+void MachOParser::forEachGlobalSymbol(Diagnostics& diag, void (^callback)(const char* symbolName, uint64_t n_value, uint8_t n_type, uint8_t n_sect, uint16_t n_desc, bool& stop)) const
+{
+    LinkEditInfo leInfo;
+    getLinkEditPointers(diag, leInfo);
+    if ( diag.hasError() )
+        return;
+
+    const bool is64Bit = is64();
+    if ( leInfo.symTab != nullptr ) {
+        uint32_t globalsStartIndex = 0;
+        uint32_t globalsCount      = leInfo.symTab->nsyms;
+        if ( leInfo.dynSymTab != nullptr ) {
+            globalsStartIndex = leInfo.dynSymTab->iextdefsym;
+            globalsCount      = leInfo.dynSymTab->nextdefsym;
+        }
+        uint32_t               maxStringOffset  = leInfo.symTab->strsize;
+        const char*            stringPool       =             (char*)getLinkEditContent(leInfo.layout, leInfo.symTab->stroff);
+        const struct nlist*    symbols          = (struct nlist*)   (getLinkEditContent(leInfo.layout, leInfo.symTab->symoff));
+        const struct nlist_64* symbols64        = (struct nlist_64*)(getLinkEditContent(leInfo.layout, leInfo.symTab->symoff));
+        bool                   stop             = false;
+        for (uint32_t i=0; (i < globalsCount) && !stop; ++i) {
+            if ( is64Bit ) {
+                const struct nlist_64& sym = symbols64[globalsStartIndex+i];
+                if ( sym.n_un.n_strx > maxStringOffset )
+                    continue;
+                if ( (sym.n_type & N_EXT) && ((sym.n_type & N_TYPE) == N_SECT) && ((sym.n_type & N_STAB) == 0) )
+                    callback(&stringPool[sym.n_un.n_strx], sym.n_value, sym.n_type, sym.n_sect, sym.n_desc, stop);
+            }
+            else {
+                const struct nlist& sym = symbols[globalsStartIndex+i];
+                if ( sym.n_un.n_strx > maxStringOffset )
+                    continue;
+                if ( (sym.n_type & N_EXT) && ((sym.n_type & N_TYPE) == N_SECT) && ((sym.n_type & N_STAB) == 0) )
+                    callback(&stringPool[sym.n_un.n_strx], sym.n_value, sym.n_type, sym.n_sect, sym.n_desc, stop);
+            }
+        }
+    }
+}
+
+void MachOParser::forEachLocalSymbol(Diagnostics& diag, void (^callback)(const char* symbolName, uint64_t n_value, uint8_t n_type, uint8_t n_sect, uint16_t n_desc, bool& stop)) const
+{
+    LinkEditInfo leInfo;
+    getLinkEditPointers(diag, leInfo);
+    if ( diag.hasError() )
+        return;
+
+    const bool is64Bit = is64();
+    if ( leInfo.symTab != nullptr ) {
+        uint32_t localsStartIndex = 0;
+        uint32_t localsCount      = leInfo.symTab->nsyms;
+        if ( leInfo.dynSymTab != nullptr ) {
+            localsStartIndex = leInfo.dynSymTab->ilocalsym;
+            localsCount      = leInfo.dynSymTab->nlocalsym;
+        }
+        uint32_t               maxStringOffset  = leInfo.symTab->strsize;
+        const char*            stringPool       =             (char*)getLinkEditContent(leInfo.layout, leInfo.symTab->stroff);
+        const struct nlist*    symbols          = (struct nlist*)   (getLinkEditContent(leInfo.layout, leInfo.symTab->symoff));
+        const struct nlist_64* symbols64        = (struct nlist_64*)(getLinkEditContent(leInfo.layout, leInfo.symTab->symoff));
+        bool                   stop             = false;
+        for (uint32_t i=0; (i < localsCount) && !stop; ++i) {
+            if ( is64Bit ) {
+                const struct nlist_64& sym = symbols64[localsStartIndex+i];
+                if ( sym.n_un.n_strx > maxStringOffset )
+                    continue;
+                if ( ((sym.n_type & N_EXT) == 0) && ((sym.n_type & N_TYPE) == N_SECT) && ((sym.n_type & N_STAB) == 0) )
+                    callback(&stringPool[sym.n_un.n_strx], sym.n_value, sym.n_type, sym.n_sect, sym.n_desc, stop);
+            }
+            else {
+                const struct nlist& sym = symbols[localsStartIndex+i];
+                if ( sym.n_un.n_strx > maxStringOffset )
+                    continue;
+                if ( ((sym.n_type & N_EXT) == 0) && ((sym.n_type & N_TYPE) == N_SECT) && ((sym.n_type & N_STAB) == 0) )
+                    callback(&stringPool[sym.n_un.n_strx], sym.n_value, sym.n_type, sym.n_sect, sym.n_desc, stop);
+            }
+        }
+    }
+}
+
+
+bool MachOParser::findExportedSymbol(Diagnostics& diag, const char* symbolName, void* extra, FoundSymbol& foundInfo, DependentFinder findDependent) const
+{
+    LinkEditInfo leInfo;
+    getLinkEditPointers(diag, leInfo);
+    if ( diag.hasError() )
+        return false;
+    if ( leInfo.dyldInfo != nullptr ) {
+        const uint8_t* trieStart    = getLinkEditContent(leInfo.layout, leInfo.dyldInfo->export_off);
+        const uint8_t* trieEnd      = trieStart + leInfo.dyldInfo->export_size;
+        const uint8_t* node         = trieWalk(diag, trieStart, trieEnd, symbolName);
+        if ( node == nullptr ) {
+            // symbol not exported from this image. Seach any re-exported dylibs
+            __block unsigned        depIndex = 0;
+            __block bool            foundInReExportedDylib = false;
+            forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool& stop) {
+                if ( isReExport && findDependent ) {
+                    const mach_header*  depMH;
+                    void*               depExtra;
+                    if ( findDependent(depIndex, loadPath, extra, &depMH, &depExtra) ) {
+                        bool depInRawCache = inRawCache() && (depMH->flags & 0x80000000);
+                        MachOParser dep(depMH, depInRawCache);
+                        if ( dep.findExportedSymbol(diag, symbolName, depExtra, foundInfo, findDependent) ) {
+                            stop = true;
+                            foundInReExportedDylib = true;
+                        }
+                    }
+                    else {
+                        fprintf(stderr, "could not find re-exported dylib %s\n", loadPath);
+                    }
+                }
+                ++depIndex;
+            });
+            return foundInReExportedDylib;
+        }
+        const uint8_t* p = node;
+        const uint64_t flags = read_uleb128(diag, p, trieEnd);
+        if ( flags & EXPORT_SYMBOL_FLAGS_REEXPORT ) {
+            if ( !findDependent )
+                return false;
+            // re-export from another dylib, lookup there
+            const uint64_t ordinal = read_uleb128(diag, p, trieEnd);
+            const char* importedName = (char*)p;
+            if ( importedName[0] == '\0' )
+                importedName = symbolName;
+            assert(ordinal >= 1);
+            if (ordinal > dependentDylibCount()) {
+                diag.error("ordinal %lld out of range for %s", ordinal, symbolName);
+                return false;
+            }
+            uint32_t depIndex = (uint32_t)(ordinal-1);
+            const mach_header*  depMH;
+            void*               depExtra;
+            if ( findDependent(depIndex, dependentDylibLoadPath(depIndex), extra, &depMH, &depExtra) ) {
+                bool depInRawCache = inRawCache() && (depMH->flags & 0x80000000);
+                MachOParser depParser(depMH, depInRawCache);
+                return depParser.findExportedSymbol(diag, importedName, depExtra, foundInfo, findDependent);
+            }
+            else {
+                diag.error("dependent dylib %lld not found for re-exported symbol %s", ordinal, symbolName);
+                return false;
+            }
+        }
+        foundInfo.kind               = FoundSymbol::Kind::headerOffset;
+        foundInfo.isThreadLocal      = false;
+        foundInfo.foundInDylib       = header();
+        foundInfo.foundExtra         = extra;
+        foundInfo.value              = read_uleb128(diag, p, trieEnd);
+        foundInfo.resolverFuncOffset = 0;
+        foundInfo.foundSymbolName    = symbolName;
+        if ( diag.hasError() )
+            return false;
+        switch ( flags & EXPORT_SYMBOL_FLAGS_KIND_MASK ) {
+            case EXPORT_SYMBOL_FLAGS_KIND_REGULAR:
+                if ( flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER ) {
+                    foundInfo.kind = FoundSymbol::Kind::headerOffset;
+                    foundInfo.resolverFuncOffset = (uint32_t)read_uleb128(diag, p, trieEnd);
+                }
+                else {
+                    foundInfo.kind = FoundSymbol::Kind::headerOffset;
+                }
+                break;
+            case EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL:
+                foundInfo.isThreadLocal = true;
+                break;
+            case EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE:
+                foundInfo.kind = FoundSymbol::Kind::absolute;
+                break;
+            default:
+                diag.error("unsupported exported symbol kind. flags=%llu at node offset=0x%0lX", flags, (long)(node-trieStart));
+                return false;
+        }
+        return true;
+    }
+    else {
+        // this is an old binary (before macOS 10.6), scan the symbol table
+        foundInfo.foundInDylib = nullptr;
+        uint64_t baseAddress = preferredLoadAddress();
+        forEachGlobalSymbol(diag, ^(const char* aSymbolName, uint64_t n_value, uint8_t n_type, uint8_t n_sect, uint16_t n_desc, bool& stop) {
+            if ( strcmp(aSymbolName, symbolName) == 0 ) {
+                foundInfo.kind               = FoundSymbol::Kind::headerOffset;
+                foundInfo.isThreadLocal      = false;
+                foundInfo.foundInDylib       = header();
+                foundInfo.foundExtra         = extra;
+                foundInfo.value              = n_value - baseAddress;
+                foundInfo.resolverFuncOffset = 0;
+                foundInfo.foundSymbolName    = symbolName;
+                stop = true;
+            }
+        });
+        return (foundInfo.foundInDylib != nullptr);
+    }
+}
+
+
+void MachOParser::getLinkEditLoadCommands(Diagnostics& diag, LinkEditInfo& result) const
+{
+    result.dyldInfo       = nullptr;
+    result.symTab         = nullptr;
+    result.dynSymTab      = nullptr;
+    result.splitSegInfo   = nullptr;
+    result.functionStarts = nullptr;
+    result.dataInCode     = nullptr;
+    result.codeSig        = nullptr;
+    __block bool hasUUID    = false;
+    __block bool hasVersion = false;
+    __block bool hasEncrypt = false;
+    forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
+        switch ( cmd->cmd ) {
+            case LC_DYLD_INFO:
+            case LC_DYLD_INFO_ONLY:
+                if ( cmd->cmdsize != sizeof(dyld_info_command) )
+                    diag.error("LC_DYLD_INFO load command size wrong");
+                else if ( result.dyldInfo != nullptr )
+                    diag.error("multiple LC_DYLD_INFO load commands");
+                result.dyldInfo = (dyld_info_command*)cmd;
+                break;
+            case LC_SYMTAB:
+                if ( cmd->cmdsize != sizeof(symtab_command) )
+                    diag.error("LC_SYMTAB load command size wrong");
+                else if ( result.symTab != nullptr )
+                    diag.error("multiple LC_SYMTAB load commands");
+                result.symTab = (symtab_command*)cmd;
+                break;
+            case LC_DYSYMTAB:
+                if ( cmd->cmdsize != sizeof(dysymtab_command) )
+                    diag.error("LC_DYSYMTAB load command size wrong");
+                else if ( result.dynSymTab != nullptr )
+                    diag.error("multiple LC_DYSYMTAB load commands");
+                result.dynSymTab = (dysymtab_command*)cmd;
+                break;
+            case LC_SEGMENT_SPLIT_INFO:
+                if ( cmd->cmdsize != sizeof(linkedit_data_command) )
+                    diag.error("LC_SEGMENT_SPLIT_INFO load command size wrong");
+                else if ( result.splitSegInfo != nullptr )
+                    diag.error("multiple LC_SEGMENT_SPLIT_INFO load commands");
+                result.splitSegInfo = (linkedit_data_command*)cmd;
+                break;
+            case LC_FUNCTION_STARTS:
+                if ( cmd->cmdsize != sizeof(linkedit_data_command) )
+                    diag.error("LC_FUNCTION_STARTS load command size wrong");
+                else if ( result.functionStarts != nullptr )
+                    diag.error("multiple LC_FUNCTION_STARTS load commands");
+                result.functionStarts = (linkedit_data_command*)cmd;
+                break;
+            case LC_DATA_IN_CODE:
+                if ( cmd->cmdsize != sizeof(linkedit_data_command) )
+                    diag.error("LC_DATA_IN_CODE load command size wrong");
+                else if ( result.dataInCode != nullptr )
+                    diag.error("multiple LC_DATA_IN_CODE load commands");
+                result.dataInCode = (linkedit_data_command*)cmd;
+                break;
+            case LC_CODE_SIGNATURE:
+                if ( cmd->cmdsize != sizeof(linkedit_data_command) )
+                    diag.error("LC_CODE_SIGNATURE load command size wrong");
+                else if ( result.codeSig != nullptr )
+                     diag.error("multiple LC_CODE_SIGNATURE load commands");
+                result.codeSig = (linkedit_data_command*)cmd;
+                break;
+            case LC_UUID:
+                if ( cmd->cmdsize != sizeof(uuid_command) )
+                    diag.error("LC_UUID load command size wrong");
+                else if ( hasUUID )
+                     diag.error("multiple LC_UUID load commands");
+                hasUUID = true;
+                break;
+            case LC_VERSION_MIN_IPHONEOS:
+            case LC_VERSION_MIN_MACOSX:
+            case LC_VERSION_MIN_TVOS:
+            case LC_VERSION_MIN_WATCHOS:
+                if ( cmd->cmdsize != sizeof(version_min_command) )
+                    diag.error("LC_VERSION_* load command size wrong");
+                 else if ( hasVersion )
+                     diag.error("multiple LC_VERSION_MIN_* load commands");
+                hasVersion = true;
+                break;
+            case LC_BUILD_VERSION:
+                if ( cmd->cmdsize != (sizeof(build_version_command) + ((build_version_command*)cmd)->ntools * sizeof(build_tool_version)) )
+                    diag.error("LC_BUILD_VERSION load command size wrong");
+                else if ( hasVersion )
+                     diag.error("multiple LC_BUILD_VERSION load commands");
+                hasVersion = true;
+                break;
+            case LC_ENCRYPTION_INFO:
+                if ( cmd->cmdsize != sizeof(encryption_info_command) )
+                    diag.error("LC_ENCRYPTION_INFO load command size wrong");
+                else if ( hasEncrypt )
+                     diag.error("multiple LC_ENCRYPTION_INFO load commands");
+                else if ( is64() )
+                      diag.error("LC_ENCRYPTION_INFO found in 64-bit mach-o");
+                hasEncrypt = true;
+                break;
+            case LC_ENCRYPTION_INFO_64:
+                if ( cmd->cmdsize != sizeof(encryption_info_command_64) )
+                    diag.error("LC_ENCRYPTION_INFO_64 load command size wrong");
+                else if ( hasEncrypt )
+                     diag.error("multiple LC_ENCRYPTION_INFO_64 load commands");
+                else if ( !is64() )
+                      diag.error("LC_ENCRYPTION_INFO_64 found in 32-bit mach-o");
+                hasEncrypt = true;
+                break;
+        }
+    });
+    if ( diag.noError() && (result.dynSymTab != nullptr) && (result.symTab == nullptr) )
+        diag.error("LC_DYSYMTAB but no LC_SYMTAB load command");
+
+}
+
+void MachOParser::getLinkEditPointers(Diagnostics& diag, LinkEditInfo& result) const
+{
+    getLinkEditLoadCommands(diag, result);
+    if ( diag.noError() )
+        getLayoutInfo(result.layout);
+}
+
+void MachOParser::forEachSegment(void (^callback)(const char* segName, uint32_t fileOffset, uint32_t fileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, bool& stop)) const
+{
+    Diagnostics diag;
+    forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
+        if ( cmd->cmd == LC_SEGMENT_64 ) {
+            const segment_command_64* seg = (segment_command_64*)cmd;
+            callback(seg->segname, (uint32_t)seg->fileoff, (uint32_t)seg->filesize, seg->vmaddr, seg->vmsize, seg->initprot, stop);
+        }
+        else if ( cmd->cmd == LC_SEGMENT ) {
+            const segment_command* seg = (segment_command*)cmd;
+            callback(seg->segname, seg->fileoff, seg->filesize, seg->vmaddr, seg->vmsize, seg->initprot, stop);
+        }
+    });
+    diag.assertNoError();   // any malformations in the file should have been caught by earlier validate() call
+}
+
+const uint8_t* MachOParser::trieWalk(Diagnostics& diag, const uint8_t* start, const uint8_t* end, const char* symbol)
+{
+    uint32_t visitedNodeOffsets[128];
+    int visitedNodeOffsetCount = 0;
+    visitedNodeOffsets[visitedNodeOffsetCount++] = 0;
+    const uint8_t* p = start;
+    while ( p < end ) {
+        uint64_t terminalSize = *p++;
+        if ( terminalSize > 127 ) {
+            // except for re-export-with-rename, all terminal sizes fit in one byte
+            --p;
+            terminalSize = read_uleb128(diag, p, end);
+            if ( diag.hasError() )
+                return nullptr;
+        }
+        if ( (*symbol == '\0') && (terminalSize != 0) ) {
+            return p;
+        }
+        const uint8_t* children = p + terminalSize;
+        if ( children > end ) {
+            diag.error("malformed trie node, terminalSize=0x%llX extends past end of trie\n", terminalSize);
+            return nullptr;
+        }
+        uint8_t childrenRemaining = *children++;
+        p = children;
+        uint64_t nodeOffset = 0;
+        for (; childrenRemaining > 0; --childrenRemaining) {
+            const char* ss = symbol;
+            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 ) {
+                    diag.error("malformed trie node, child node extends past end of trie\n");
+                    return nullptr;
+                }
+            }
+            else {
+                 // the symbol so far matches this edge (child)
+                // so advance to the child's node
+                ++p;
+                nodeOffset = read_uleb128(diag, p, end);
+                if ( diag.hasError() )
+                    return nullptr;
+                if ( (nodeOffset == 0) || ( &start[nodeOffset] > end) ) {
+                    diag.error("malformed trie child, nodeOffset=0x%llX out of range\n", nodeOffset);
+                    return nullptr;
+                }
+                symbol = ss;
+                break;
+            }
+        }
+        if ( nodeOffset != 0 ) {
+            if ( nodeOffset > (end-start) ) {
+                diag.error("malformed trie child, nodeOffset=0x%llX out of range\n", nodeOffset);
+               return nullptr;
+            }
+            for (int i=0; i < visitedNodeOffsetCount; ++i) {
+                if ( visitedNodeOffsets[i] == nodeOffset ) {
+                    diag.error("malformed trie child, cycle to nodeOffset=0x%llX\n", nodeOffset);
+                    return nullptr;
+                }
+            }
+            visitedNodeOffsets[visitedNodeOffsetCount++] = (uint32_t)nodeOffset;
+            if ( visitedNodeOffsetCount >= 128 ) {
+                diag.error("malformed trie too deep\n");
+                return nullptr;
+            }
+            p = &start[nodeOffset];
+        }
+        else
+            p = end;
+    }
+    return nullptr;
+}
+
+
+uint64_t MachOParser::read_uleb128(Diagnostics& diag, const uint8_t*& p, const uint8_t* end)
+{
+    uint64_t result = 0;
+    int         bit = 0;
+    do {
+        if ( p == end ) {
+            diag.error("malformed uleb128");
+            break;
+        }
+        uint64_t slice = *p & 0x7f;
+
+        if ( bit > 63 ) {
+            diag.error("uleb128 too big for uint64");
+            break;
+        }
+        else {
+            result |= (slice << bit);
+            bit += 7;
+        }
+    }
+    while (*p++ & 0x80);
+    return result;
+}
+
+
+int64_t MachOParser::read_sleb128(Diagnostics& diag, const uint8_t*& p, const uint8_t* end)
+{
+    int64_t  result = 0;
+    int      bit = 0;
+    uint8_t  byte = 0;
+    do {
+        if ( p == end ) {
+            diag.error("malformed sleb128");
+            break;
+        }
+        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;
+}
+
+bool MachOParser::is64() const
+{
+#if DYLD_IN_PROCESS
+    return (sizeof(void*) == 8);
+#else
+    return (header()->magic == MH_MAGIC_64);
+#endif
+}
+
+
+
+
+bool MachOParser::findClosestSymbol(uint64_t targetUnslidAddress, const char** symbolName, uint64_t* symbolUnslidAddr) const
+{
+    Diagnostics diag;
+    __block uint64_t    closestNValueSoFar = 0;
+    __block const char* closestNameSoFar   = nullptr;
+    forEachGlobalSymbol(diag, ^(const char* aSymbolName, uint64_t n_value, uint8_t n_type, uint8_t n_sect, uint16_t n_desc, bool& stop) {
+        if ( n_value <= targetUnslidAddress ) {
+            if ( (closestNameSoFar == nullptr) || (closestNValueSoFar < n_value) ) {
+                closestNValueSoFar = n_value;
+                closestNameSoFar   = aSymbolName;
+            }
+        }
+    });
+    forEachLocalSymbol(diag, ^(const char* aSymbolName, uint64_t n_value, uint8_t n_type, uint8_t n_sect, uint16_t n_desc, bool& stop) {
+        if ( n_value <= targetUnslidAddress ) {
+            if ( (closestNameSoFar == nullptr) || (closestNValueSoFar < n_value) ) {
+                closestNValueSoFar = n_value;
+                closestNameSoFar   = aSymbolName;
+            }
+        }
+    });
+    if ( closestNameSoFar == nullptr ) {
+        return false;
+    }
+
+    *symbolName       = closestNameSoFar;
+    *symbolUnslidAddr = closestNValueSoFar;
+    return true;
+}
+
+
+#if DYLD_IN_PROCESS
+
+bool MachOParser::findClosestSymbol(const void* addr, const char** symbolName, const void** symbolAddress) const
+{
+    uint64_t slide = getSlide();
+    uint64_t symbolUnslidAddr;
+    if ( findClosestSymbol((uint64_t)addr - slide, symbolName, &symbolUnslidAddr) ) {
+        *symbolAddress = (const void*)(long)(symbolUnslidAddr + slide);
+        return true;
+    }
+    return false;
+}
+
+intptr_t MachOParser::getSlide() const
+{
+    Diagnostics diag;
+    __block intptr_t slide = 0;
+    forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
+#if __LP64__
+        if ( cmd->cmd == LC_SEGMENT_64 ) {
+            const segment_command_64* seg = (segment_command_64*)cmd;
+            if ( strcmp(seg->segname, "__TEXT") == 0 ) {
+                slide = ((uint64_t)header()) - seg->vmaddr;
+                stop = true;
+            }
+        }
+#else
+        if ( cmd->cmd == LC_SEGMENT ) {
+            const segment_command* seg = (segment_command*)cmd;
+            if ( strcmp(seg->segname, "__TEXT") == 0 ) {
+                slide = ((uint32_t)header()) - seg->vmaddr;
+                stop = true;
+            }
+        }
+#endif
+    });
+    diag.assertNoError();   // any malformations in the file should have been caught by earlier validate() call
+    return slide;
+}
+
+// this is only used by dlsym() at runtime.  All other binding is done when the closure is built.
+bool MachOParser::hasExportedSymbol(const char* symbolName, DependentFinder finder, void** result) const
+{
+    typedef void* (*ResolverFunc)(void);
+    ResolverFunc resolver;
+    Diagnostics diag;
+    FoundSymbol foundInfo;
+    if ( findExportedSymbol(diag, symbolName, (void*)header(), foundInfo, finder) ) {
+        switch ( foundInfo.kind ) {
+            case FoundSymbol::Kind::headerOffset:
+                *result = (uint8_t*)foundInfo.foundInDylib + foundInfo.value;
+                break;
+            case FoundSymbol::Kind::absolute:
+                *result = (void*)(long)foundInfo.value;
+                break;
+            case FoundSymbol::Kind::resolverOffset:
+                // foundInfo.value contains "stub".
+                // in dlsym() we want to call resolver function to get final function address
+                resolver = (ResolverFunc)((uint8_t*)foundInfo.foundInDylib + foundInfo.resolverFuncOffset);
+                *result = (*resolver)();
+                break;
+        }
+        return true;
+    }
+    return false;
+}
+
+const char* MachOParser::segmentName(uint32_t targetSegIndex) const
+{
+    __block const char* result = nullptr;
+    __block uint32_t segIndex  = 0;
+    forEachSegment(^(const char* segName, uint32_t fileOffset, uint32_t fileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, bool& stop) {
+        if ( segIndex == targetSegIndex ) {
+            result = segName;
+            stop = true;
+        }
+        ++segIndex;
+    });
+    return result;
+}
+
+#else 
+
+
+bool MachOParser::uses16KPages() const
+{
+    return (header()->cputype == CPU_TYPE_ARM64);
+}
+
+
+bool MachOParser::isEncrypted() const
+{
+    __block bool result = false;
+    Diagnostics diag;
+    forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
+        if ( cmd->cmd == LC_SEGMENT_64 ) {
+            const segment_command_64* segCmd = (segment_command_64*)cmd;
+            if ( segCmd->flags & SG_PROTECTED_VERSION_1 ) {
+                result = true;
+                stop = true;
+            }
+        }
+        else if ( cmd->cmd == LC_SEGMENT ) {
+            const segment_command* segCmd = (segment_command*)cmd;
+            if ( segCmd->flags & SG_PROTECTED_VERSION_1 ) {
+                result = true;
+                stop = true;
+            }
+        }
+        else if ( (cmd->cmd == LC_ENCRYPTION_INFO) || (cmd->cmd == LC_ENCRYPTION_INFO_64) ) {
+            const encryption_info_command* encCmd = (encryption_info_command*)cmd;
+            if ( encCmd->cryptid != 0 ) {
+                result = true;
+                stop = true;
+            }
+        }
+    });
+    return result;
+}
+
+bool MachOParser::hasWeakDefs() const
+{
+    return (header()->flags & (MH_WEAK_DEFINES|MH_BINDS_TO_WEAK));
+}
+
+bool MachOParser::hasObjC() const
+{
+    __block bool result = false;
+    forEachSection(^(const char* segmentName, const char* sectionName, uint32_t flags, const void* content, size_t size, bool illegalSectionSize, bool& stop) {
+        if ( (strncmp(sectionName, "__objc_imageinfo", 16) == 0) && (strncmp(segmentName, "__DATA", 6) == 0) ) {
+            result = true;
+            stop = true;
+        }
+        if ( (header()->cputype == CPU_TYPE_I386) && (strcmp(sectionName, "__image_info") == 0) && (strcmp(segmentName, "__OBJC") == 0) ) {
+            result = true;
+            stop = true;
+        }
+    });
+    return result;
+}
+
+bool MachOParser::hasPlusLoadMethod(Diagnostics& diag) const
+{
+#if 1
+    __block bool result = false;
+    forEachSection(^(const char* segmentName, const char* sectionName, uint32_t flags, uint64_t addr, const void* content, uint64_t size, uint32_t alignP2, uint32_t reserved1, uint32_t reserved2, bool illegalSectionSize, bool& stop) {
+        if ( ( (flags & SECTION_TYPE) == S_CSTRING_LITERALS ) ) {
+            if (illegalSectionSize) {
+                diag.error("cstring section %s/%s extends beyond the end of the segment", segmentName, sectionName);
+                return;
+            }
+            const char* s   = (char*)content;
+            const char* end = s + size;
+            while ( s < end ) {
+                if ( strcmp(s, "load") == 0 ) {
+                    result = true;
+                    stop = true;
+                    return;
+                }
+                while (*s != '\0' )
+                    ++s;
+                ++s;
+            }
+        }
+    });
+    return result;
+#else
+    LayoutInfo layout;
+    getLayoutInfo(layout);
+
+    __block bool        hasSwift            = false;
+    __block const void* classList           = nullptr;
+    __block size_t      classListSize       = 0;
+    __block const void* objcData            = nullptr;
+    __block size_t      objcDataSize        = 0;
+    __block const void* objcConstData       = nullptr;
+    __block size_t      objcConstDataSize   = 0;
+    forEachSection(^(const char* segmentName, const char* sectionName, uint32_t flags, uint64_t addr, const void* content, uint64_t size, uint32_t alignP2, uint32_t reserved1, uint32_t reserved2, bool& stop) {
+        if ( (strcmp(sectionName, "__objc_classlist") == 0) && (strncmp(segmentName, "__DATA", 6) == 0) ) {
+            classList     = content;
+            classListSize = size;
+        }
+        if ( (strcmp(sectionName, "__objc_imageinfo") == 0) && (strncmp(segmentName, "__DATA", 6) == 0) ) {
+            const uint32_t* info = (uint32_t*)content;
+            uint8_t swiftVersion = (info[1] >> 8) & 0xFF;
+            if ( swiftVersion != 0 )
+                hasSwift = true;
+        }
+    });
+    if ( classList == nullptr )
+        return false;
+    // FIXME: might be objc and swift intermixed
+    if ( hasSwift )
+        return true;
+    const bool      p64            = is64();
+    const uint32_t  pointerSize    = (p64 ? 8 : 4);
+    const uint64_t* classArray64   = (uint64_t*)classList;
+    const uint32_t* classArray32   = (uint32_t*)classList;
+    const uint32_t  classListCount = (uint32_t)(classListSize/pointerSize);
+    for (uint32_t i=0; i < classListCount; ++i) {
+        if ( p64 ) {
+            uint64_t classObjAddr = classArray64[i];
+            const uint64_t* classObjContent = (uint64_t*)getContentForVMAddr(layout, classObjAddr);
+            uint64_t classROAddr = classObjContent[4];
+            uint64_t metaClassObjAddr = classObjContent[0];
+            const uint64_t* metaClassObjContent = (uint64_t*)getContentForVMAddr(layout, metaClassObjAddr);
+            uint64_t metaClassROObjAddr = metaClassObjContent[4];
+            const uint64_t* metaClassROObjContent = (uint64_t*)getContentForVMAddr(layout, metaClassROObjAddr);
+            uint64_t metaClassMethodListAddr = metaClassROObjContent[4];
+            if ( metaClassMethodListAddr != 0 ) {
+                const uint64_t* metaClassMethodListContent = (uint64_t*)getContentForVMAddr(layout, metaClassMethodListAddr);
+                const uint32_t methodListCount = ((uint32_t*)metaClassMethodListContent)[1];
+                for (uint32_t m=0; m < methodListCount; ++m) {
+                    uint64_t methodNameAddr = metaClassMethodListContent[m*3+1];
+                    const char* methodNameContent = (char*)getContentForVMAddr(layout, methodNameAddr);
+                    if ( strcmp(methodNameContent, "load") == 0 ) {
+                        return true;
+                    }
+                }
+            }
+        }
+        else {
+
+        }
+    }
+
+    return false;
+#endif
+}
+
+bool MachOParser::getCDHash(uint8_t cdHash[20])
+{
+    Diagnostics diag;
+    LinkEditInfo leInfo;
+    getLinkEditPointers(diag, leInfo);
+    if ( diag.hasError() || (leInfo.codeSig == nullptr) )
+        return false;
+
+    return cdHashOfCodeSignature(getLinkEditContent(leInfo.layout, leInfo.codeSig->dataoff), leInfo.codeSig->datasize, cdHash);
+ }
+
+bool MachOParser::usesLibraryValidation() const
+{
+    Diagnostics diag;
+    LinkEditInfo leInfo;
+    getLinkEditPointers(diag, leInfo);
+    if ( diag.hasError() || (leInfo.codeSig == nullptr) )
+        return false;
+
+    const CS_CodeDirectory* cd = (const CS_CodeDirectory*)findCodeDirectoryBlob(getLinkEditContent(leInfo.layout, leInfo.codeSig->dataoff), leInfo.codeSig->datasize);
+    if ( cd == nullptr )
+        return false;
+
+    // check for CS_REQUIRE_LV in CS_CodeDirectory.flags
+    return (htonl(cd->flags) & CS_REQUIRE_LV);
+ }
+
+
+bool MachOParser::isRestricted() const
+{
+    __block bool result = false;
+    forEachSection(^(const char* segName, const char* sectionName, uint32_t flags, const void* content, size_t size, bool illegalSectionSize, bool& stop) {
+        if ( (strcmp(segName, "__RESTRICT") == 0) && (strcmp(sectionName, "__restrict") == 0) ) {
+            result = true;
+            stop = true;
+        }
+
+    });
+    return result;
+}
+
+bool MachOParser::hasCodeSignature(uint32_t& fileOffset, uint32_t& size)
+{
+    fileOffset = 0;
+    size = 0;
+
+       // <rdar://problem/13622786> ignore code signatures in macOS binaries built with pre-10.9 tools
+    Platform platform;
+    uint32_t minOS;
+    uint32_t sdk;
+    if ( getPlatformAndVersion(&platform, &minOS, &sdk) ) {
+        // if have LC_VERSION_MIN_MACOSX and it says SDK < 10.9, so ignore code signature
+        if ( (platform == Platform::macOS) && (sdk < 0x000A0900) )
+            return false;
+    }
+    else {
+        switch ( header()->cputype ) {
+            case CPU_TYPE_I386:
+            case CPU_TYPE_X86_64:
+                // old binary with no LC_VERSION_*, assume intel binaries are old macOS binaries (ignore code signature)
+                return false;
+        }
+    }
+
+    Diagnostics diag;
+    forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
+        if ( cmd->cmd == LC_CODE_SIGNATURE ) {
+            const linkedit_data_command* sigCmd = (linkedit_data_command*)cmd;
+            fileOffset = sigCmd->dataoff;
+            size       = sigCmd->datasize;
+            stop = true;
+        }
+    });
+    diag.assertNoError();   // any malformations in the file should have been caught by earlier validate() call
+    return (fileOffset != 0);
+}
+
+bool MachOParser::getEntry(uint32_t& offset, bool& usesCRT)
+{
+    Diagnostics diag;
+    offset = 0;
+    forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
+        if ( cmd->cmd == LC_MAIN ) {
+            entry_point_command* mainCmd = (entry_point_command*)cmd;
+            usesCRT = false;
+            offset = (uint32_t)mainCmd->entryoff;
+            stop = true;
+        }
+        else if ( cmd->cmd == LC_UNIXTHREAD ) {
+            stop = true;
+            usesCRT = true;
+            const uint32_t* regs32 = (uint32_t*)(((char*)cmd) + 16);
+            const uint64_t* regs64 = (uint64_t*)(((char*)cmd) + 16);
+            uint64_t startAddress = 0;
+            switch ( header()->cputype ) {
+                case CPU_TYPE_I386:
+                    startAddress = regs32[10]; // i386_thread_state_t.eip
+                    break;
+                case CPU_TYPE_X86_64:
+                    startAddress = regs64[16]; // x86_thread_state64_t.rip
+                    break;
+                case CPU_TYPE_ARM:
+                    startAddress = regs32[15]; // arm_thread_state_t.__pc
+                    break;
+                case CPU_TYPE_ARM64:
+                    startAddress = regs64[32]; // arm_thread_state64_t.__pc
+                    break;
+            }
+            offset = (uint32_t)(startAddress - preferredLoadAddress());
+        }
+    });
+    diag.assertNoError();   // any malformations in the file should have been caught by earlier validate() call
+    // FIXME: validate offset is into executable segment
+    return (offset != 0);
+}
+
+bool MachOParser::canBePlacedInDyldCache(const std::string& path) const {
+    std::set<std::string> reasons;
+    return canBePlacedInDyldCache(path, reasons);
+}
+
+bool MachOParser::canBePlacedInDyldCache(const std::string& path, std::set<std::string>& reasons) const
+{
+    bool retval = true;
+    // only dylibs can go in cache
+    if ( fileType() != MH_DYLIB ) {
+        reasons.insert("Not MH_DYLIB");
+        return false; // cannot continue, installName() will assert() if not a dylib
+    }
+
+    // only dylibs built for /usr/lib or /System/Library can go in cache
+    const char* dylibName = installName();
+    if ( (strncmp(dylibName, "/usr/lib/", 9) != 0) && (strncmp(dylibName, "/System/Library/", 16) != 0) ) {
+        retval = false;
+        reasons.insert("Not in '/usr/lib/' or '/System/Library/'");
+    }
+
+    // flat namespace files cannot go in cache
+    if ( (header()->flags & MH_TWOLEVEL) == 0 ) {
+        retval = false;
+        reasons.insert("Not built with two level namespaces");
+    }
+
+    // don't put debug variants into dyld cache
+    if ( endsWith(path, "_profile.dylib") || endsWith(path, "_debug.dylib") || endsWith(path, "_profile") || endsWith(path, "_debug") || endsWith(path, "/CoreADI") ) {
+        retval = false;
+        reasons.insert("Variant image");
+    }
+
+    // dylib must have extra info for moving DATA and TEXT segments apart
+    __block bool hasExtraInfo = false;
+    __block bool hasDyldInfo = false;
+    Diagnostics diag;
+    forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
+        if ( cmd->cmd == LC_SEGMENT_SPLIT_INFO )
+            hasExtraInfo = true;
+        if ( cmd->cmd == LC_DYLD_INFO_ONLY )
+            hasDyldInfo = true;
+    });
+    if ( !hasExtraInfo ) {
+        retval = false;
+        reasons.insert("Missing split seg info");
+    }
+    if ( !hasDyldInfo ) {
+        retval = false;
+        reasons.insert("Old binary, missing dyld info");
+    }
+
+    // dylib can only depend on other dylibs in the shared cache
+    __block bool allDepPathsAreGood = true;
+    forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool& stop) {
+        if ( (strncmp(loadPath, "/usr/lib/", 9) != 0) && (strncmp(loadPath, "/System/Library/", 16) != 0) ) {
+            allDepPathsAreGood = false;
+            stop = true;
+        }
+    });
+    if ( !allDepPathsAreGood ) {
+        retval = false;
+        reasons.insert("Depends on cache inelegible dylibs");
+    }
+
+    // dylibs with interposing info cannot be in cache
+    __block bool hasInterposing = false;
+    forEachInterposingTuple(diag, ^(uint32_t segIndex, uint64_t replacementSegOffset, uint64_t replaceeSegOffset, uint64_t replacementContent, bool& stop) {
+        hasInterposing = true;
+    });
+    if ( hasInterposing ) {
+        retval = false;
+        reasons.insert("Has interposing tuples");
+    }
+
+    return retval;
+}
+
+bool MachOParser::isDynamicExecutable() const
+{
+    if ( fileType() != MH_EXECUTE )
+        return false;
+    
+    // static executables do not have dyld load command
+    __block bool hasDyldLoad = false;
+    Diagnostics diag;
+    forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
+        if ( cmd->cmd == LC_LOAD_DYLINKER ) {
+            hasDyldLoad = true;
+            stop = true;
+        }
+    });
+    return hasDyldLoad;
+}
+
+
+bool MachOParser::isSlideable() const
+{
+    if ( header()->filetype == MH_DYLIB )
+        return true;
+    if ( header()->filetype == MH_BUNDLE )
+        return true;
+    if ( (header()->filetype == MH_EXECUTE) && (header()->flags & MH_PIE) )
+        return true;
+
+    return false;
+}
+
+
+
+bool MachOParser::hasInitializer(Diagnostics& diag) const
+{
+    __block bool result = false;
+    forEachInitializer(diag, ^(uint32_t offset) {
+        result = true;
+    });
+    return result;
+}
+
+void MachOParser::forEachInitializer(Diagnostics& diag, void (^callback)(uint32_t offset)) const
+{
+    __block uint64_t textSegAddrStart = 0;
+    __block uint64_t textSegAddrEnd   = 0;
+
+    forEachSegment(^(const char* segName, uint32_t fileOffset, uint32_t fileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, bool& stop) {
+        if ( strcmp(segName, "__TEXT") == 0 ) {
+            textSegAddrStart = vmAddr;
+            textSegAddrEnd   = vmAddr + vmSize;
+            stop = true;
+        }
+    });
+    if ( textSegAddrStart == textSegAddrEnd ) {
+        diag.error("no __TEXT segment");
+        return;
+    }
+
+    // if dylib linked with -init linker option, that initializer is first
+    forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
+        if ( cmd->cmd == LC_ROUTINES ) {
+            const routines_command* routines = (routines_command*)cmd;
+            uint64_t dashInit = routines->init_address;
+            if ( (textSegAddrStart < dashInit) && (dashInit < textSegAddrEnd) )
+                callback((uint32_t)(dashInit - textSegAddrStart));
+            else
+                diag.error("-init does not point within __TEXT segment");
+        }
+        else if ( cmd->cmd == LC_ROUTINES_64 ) {
+            const routines_command_64* routines = (routines_command_64*)cmd;
+            uint64_t dashInit = routines->init_address;
+            if ( (textSegAddrStart < dashInit) && (dashInit < textSegAddrEnd) )
+                callback((uint32_t)(dashInit - textSegAddrStart));
+            else
+                diag.error("-init does not point within __TEXT segment");
+        }
+    });
+
+    // next any function pointers in mod-init section
+    bool p64 = is64();
+    unsigned pointerSize = p64 ? 8 : 4;
+    forEachSection(^(const char* segmentName, const char* sectionName, uint32_t flags, const void* content, size_t size, bool illegalSectionSize, bool& stop) {
+        if ( (flags & SECTION_TYPE) == S_MOD_INIT_FUNC_POINTERS ) {
+            if ( (size % pointerSize) != 0 ) {
+                diag.error("initializer section %s/%s has bad size", segmentName, sectionName);
+                stop = true;
+                return;
+            }
+            if ( illegalSectionSize ) {
+                diag.error("initializer section %s/%s extends beyond the end of the segment", segmentName, sectionName);
+                stop = true;
+                return;
+            }
+            if ( ((long)content % pointerSize) != 0 ) {
+                diag.error("initializer section %s/%s is not pointer aligned", segmentName, sectionName);
+                stop = true;
+                return;
+            }
+            if ( p64 ) {
+                const uint64_t* initsStart = (uint64_t*)content;
+                const uint64_t* initsEnd   = (uint64_t*)((uint8_t*)content + size);
+                for (const uint64_t* p=initsStart; p < initsEnd; ++p) {
+                    uint64_t anInit = *p;
+                    if ( (anInit <= textSegAddrStart) || (anInit > textSegAddrEnd) ) {
+                         diag.error("initializer 0x%0llX does not point within __TEXT segment", anInit);
+                         stop = true;
+                         break;
+                    }
+                    callback((uint32_t)(anInit - textSegAddrStart));
+                }
+            }
+            else {
+                const uint32_t* initsStart = (uint32_t*)content;
+                const uint32_t* initsEnd   = (uint32_t*)((uint8_t*)content + size);
+                for (const uint32_t* p=initsStart; p < initsEnd; ++p) {
+                    uint32_t anInit = *p;
+                    if ( (anInit <= textSegAddrStart) || (anInit > textSegAddrEnd) ) {
+                         diag.error("initializer 0x%0X does not point within __TEXT segment", anInit);
+                         stop = true;
+                         break;
+                    }
+                    callback(anInit - (uint32_t)textSegAddrStart);
+                }
+            }
+        }
+    });
+}
+
+void MachOParser::forEachDOFSection(Diagnostics& diag, void (^callback)(uint32_t offset)) const
+{
+    forEachSection(^(const char* segmentName, const char* sectionName, uint32_t flags, const void* content, size_t size, bool illegalSectionSize, bool& stop) {
+        if ( ( (flags & SECTION_TYPE) == S_DTRACE_DOF ) && !illegalSectionSize ) {
+            callback((uint32_t)((uintptr_t)content - (uintptr_t)header()));
+        }
+    });
+}
+
+
+uint32_t MachOParser::segmentCount() const
+{
+    __block uint32_t count   = 0;
+    forEachSegment(^(const char* segName, uint32_t fileOffset, uint32_t fileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, bool& stop) {
+        ++count;
+    });
+    return count;
+}
+
+void MachOParser::forEachSegment(void (^callback)(const char* segName, uint32_t fileOffset, uint32_t fileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, uint32_t segIndex, uint64_t sizeOfSections, uint8_t p2align, bool& stop)) const
+{
+    Diagnostics diag;
+    __block uint32_t segIndex = 0;
+    forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
+        if ( cmd->cmd == LC_SEGMENT_64 ) {
+            const segment_command_64* segCmd = (segment_command_64*)cmd;
+            uint64_t sizeOfSections = segCmd->vmsize;
+            uint8_t p2align = 0;
+            const section_64* const sectionsStart = (section_64*)((char*)segCmd + sizeof(struct segment_command_64));
+            const section_64* const sectionsEnd   = &sectionsStart[segCmd->nsects];
+            for (const section_64* sect=sectionsStart; sect < sectionsEnd; ++sect) {
+                sizeOfSections = sect->addr + sect->size - segCmd->vmaddr;
+                if ( sect->align > p2align )
+                    p2align = sect->align;
+            }
+            callback(segCmd->segname, (uint32_t)segCmd->fileoff, (uint32_t)segCmd->filesize, segCmd->vmaddr, segCmd->vmsize, segCmd->initprot, segIndex, sizeOfSections, p2align, stop);
+            ++segIndex;
+        }
+        else if ( cmd->cmd == LC_SEGMENT ) {
+            const segment_command* segCmd = (segment_command*)cmd;
+            uint64_t sizeOfSections = segCmd->vmsize;
+            uint8_t p2align = 0;
+            const section* const sectionsStart = (section*)((char*)segCmd + sizeof(struct segment_command));
+            const section* const sectionsEnd   = &sectionsStart[segCmd->nsects];
+            for (const section* sect=sectionsStart; sect < sectionsEnd; ++sect) {
+                sizeOfSections = sect->addr + sect->size - segCmd->vmaddr;
+                if ( sect->align > p2align )
+                    p2align = sect->align;
+            }
+            callback(segCmd->segname, (uint32_t)segCmd->fileoff, (uint32_t)segCmd->filesize, segCmd->vmaddr, segCmd->vmsize, segCmd->initprot, segIndex, sizeOfSections, p2align, stop);
+            ++segIndex;
+        }
+    });
+    diag.assertNoError();   // any malformations in the file should have been caught by earlier validate() call
+}
+
+void MachOParser::forEachExportedSymbol(Diagnostics diag, void (^handler)(const char* symbolName, uint64_t imageOffset, bool isReExport, bool& stop)) const
+{
+    LinkEditInfo leInfo;
+    getLinkEditPointers(diag, leInfo);
+    if ( diag.hasError() )
+        return;
+
+    if ( leInfo.dyldInfo != nullptr ) {
+        const uint8_t* trieStart    = getLinkEditContent(leInfo.layout, leInfo.dyldInfo->export_off);
+        const uint8_t* trieEnd      = trieStart + leInfo.dyldInfo->export_size;
+        std::vector<ExportInfoTrie::Entry> exports;
+        if ( !ExportInfoTrie::parseTrie(trieStart, trieEnd, exports) ) {
+            diag.error("malformed exports trie");
+            return;
+        }
+        bool stop = false;
+        for (const ExportInfoTrie::Entry& exp : exports) {
+            bool isReExport = (exp.info.flags & EXPORT_SYMBOL_FLAGS_REEXPORT);
+            handler(exp.name.c_str(), exp.info.address, isReExport, stop);
+            if ( stop )
+                break;
+        }
+    }
+}
+
+bool MachOParser::invalidRebaseState(Diagnostics& diag, const char* opcodeName, const MachOParser::LinkEditInfo& leInfo,
+                                    bool segIndexSet, uint32_t pointerSize, uint8_t segmentIndex, uint64_t segmentOffset, uint8_t type) const
+{
+    if ( !segIndexSet ) {
+        diag.error("%s missing preceding REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB", opcodeName);
+        return true;
+    }
+    if ( segmentIndex >= leInfo.layout.segmentCount )  {
+        diag.error("%s segment index %d too large", opcodeName, segmentIndex);
+        return true;
+    }
+    if ( segmentOffset > (leInfo.layout.segments[segmentIndex].segSize-pointerSize) ) {
+        diag.error("%s current segment offset 0x%08llX beyond segment size (0x%08llX)", opcodeName, segmentOffset, leInfo.layout.segments[segmentIndex].segSize);
+        return true;
+    }
+    switch ( type )  {
+        case REBASE_TYPE_POINTER:
+            if ( !leInfo.layout.segments[segmentIndex].writable ) {
+                diag.error("%s pointer rebase is in non-writable segment", opcodeName);
+                return true;
+            }
+            if ( leInfo.layout.segments[segmentIndex].executable ) {
+                diag.error("%s pointer rebase is in executable segment", opcodeName);
+                return true;
+            }
+            break;
+        case REBASE_TYPE_TEXT_ABSOLUTE32:
+        case REBASE_TYPE_TEXT_PCREL32:
+            if ( !leInfo.layout.segments[segmentIndex].textRelocsAllowed ) {
+                diag.error("%s text rebase is in segment that does not support text relocations", opcodeName);
+                return true;
+            }
+            if ( leInfo.layout.segments[segmentIndex].writable ) {
+                diag.error("%s text rebase is in writable segment", opcodeName);
+                return true;
+            }
+            if ( !leInfo.layout.segments[segmentIndex].executable ) {
+                diag.error("%s pointer rebase is in non-executable segment", opcodeName);
+                return true;
+            }
+            break;
+        default:
+            diag.error("%s unknown rebase type %d", opcodeName, type);
+            return true;
+    }
+    return false;
+}
+
+void MachOParser::forEachRebase(Diagnostics& diag, void (^handler)(uint32_t segIndex, uint64_t segOffset, uint8_t type, bool& stop)) const
+{
+    LinkEditInfo leInfo;
+    getLinkEditPointers(diag, leInfo);
+    if ( diag.hasError() )
+        return;
+
+    if ( leInfo.dyldInfo != nullptr ) {
+        // work around linker bug that laid down rebase opcodes for lazy pointer section when -bind_at_load used
+        __block int      lpSegIndex       = 0;
+        __block uint64_t lpSegOffsetStart = 0;
+        __block uint64_t lpSegOffsetEnd   = 0;
+        bool             hasWeakBinds     = (leInfo.dyldInfo->weak_bind_size != 0);
+        if ( leInfo.dyldInfo->lazy_bind_size == 0 ) {
+            __block uint64_t lpAddr = 0;
+            __block uint64_t lpSize = 0;
+            forEachSection(^(const char* segName, const char* sectionName, uint32_t flags, uint64_t addr, const void* content, uint64_t size, uint32_t alignP2, uint32_t reserved1, uint32_t reserved2, bool illegalSectionSize, bool& sectStop) {
+                if ( (flags & SECTION_TYPE) == S_LAZY_SYMBOL_POINTERS ) {
+                    lpAddr = addr;
+                    lpSize = size;
+                    sectStop =  true;
+                }
+            });
+            forEachSegment(^(const char* segName, uint32_t fileOffset, uint32_t fileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, bool& segStop) {
+                if ( (vmAddr <= lpAddr) && (vmAddr+vmSize >= lpAddr+lpSize) ) {
+                    lpSegOffsetStart = lpAddr - vmAddr;
+                    lpSegOffsetEnd   = lpSegOffsetStart + lpSize;
+                    segStop = true;
+                    return;
+                }
+                ++lpSegIndex;
+            });
+        }
+        // don't remove rebase if there is a weak-bind at pointer location
+        bool (^weakBindAt)(uint64_t segOffset) = ^(uint64_t segOffset) {
+            if ( !hasWeakBinds )
+                return false;
+            __block bool result = false;
+            Diagnostics weakDiag;
+            forEachWeakDef(weakDiag, ^(bool strongDef, uint32_t dataSegIndex, uint64_t dataSegOffset, uint64_t addend, const char* symbolName, bool& weakStop) {
+                if ( segOffset == dataSegOffset ) {
+                    result = true;
+                    weakStop = true;
+                }
+            });
+            return result;
+        };
+
+
+        const uint8_t* p    = getLinkEditContent(leInfo.layout, leInfo.dyldInfo->rebase_off);
+        const uint8_t* end  = p + leInfo.dyldInfo->rebase_size;
+        const uint32_t pointerSize = (is64() ? 8 : 4);
+        uint8_t  type = 0;
+        int      segIndex = 0;
+        uint64_t segOffset = 0;
+        uint64_t count;
+        uint64_t skip;
+        bool     segIndexSet = false;
+        bool     stop = false;
+        while ( !stop && diag.noError() && (p < end) ) {
+            uint8_t immediate = *p & REBASE_IMMEDIATE_MASK;
+            uint8_t opcode = *p & REBASE_OPCODE_MASK;
+            ++p;
+            switch (opcode) {
+                case REBASE_OPCODE_DONE:
+                    stop = true;
+                    break;
+                case REBASE_OPCODE_SET_TYPE_IMM:
+                    type = immediate;
+                    break;
+                case REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB:
+                    segIndex = immediate;
+                    segOffset = read_uleb128(diag, p, end);
+                    segIndexSet = true;
+                    break;
+                case REBASE_OPCODE_ADD_ADDR_ULEB:
+                    segOffset += read_uleb128(diag, p, end);
+                    break;
+                case REBASE_OPCODE_ADD_ADDR_IMM_SCALED:
+                    segOffset += immediate*pointerSize;
+                    break;
+                case REBASE_OPCODE_DO_REBASE_IMM_TIMES:
+                    for (int i=0; i < immediate; ++i) {
+                        if ( invalidRebaseState(diag, "REBASE_OPCODE_DO_REBASE_IMM_TIMES", leInfo, segIndexSet, pointerSize, segIndex, segOffset, type) )
+                            return;
+                        if ( (segIndex != lpSegIndex) || (segOffset > lpSegOffsetEnd) || (segOffset < lpSegOffsetStart) || weakBindAt(segOffset) )
+                            handler(segIndex, segOffset, type, stop);
+                        segOffset += pointerSize;
+                    }
+                    break;
+                case REBASE_OPCODE_DO_REBASE_ULEB_TIMES:
+                    count = read_uleb128(diag, p, end);
+                    for (uint32_t i=0; i < count; ++i) {
+                         if ( invalidRebaseState(diag, "REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB", leInfo, segIndexSet, pointerSize, segIndex, segOffset, type) )
+                            return;
+                        if ( (segIndex != lpSegIndex) || (segOffset > lpSegOffsetEnd) || (segOffset < lpSegOffsetStart) || weakBindAt(segOffset) )
+                            handler(segIndex, segOffset, type, stop);
+                        segOffset += pointerSize;
+                    }
+                    break;
+                case REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB:
+                    if ( invalidRebaseState(diag, "REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB", leInfo, segIndexSet, pointerSize, segIndex, segOffset, type) )
+                        return;
+                    handler(segIndex, segOffset, type, stop);
+                    segOffset += read_uleb128(diag, p, end) + pointerSize;
+                    break;
+                case REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB:
+                    count = read_uleb128(diag, p, end);
+                    if ( diag.hasError() )
+                        break;
+                    skip = read_uleb128(diag, p, end);
+                    for (uint32_t i=0; i < count; ++i) {
+                        if ( invalidRebaseState(diag, "REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB", leInfo, segIndexSet, pointerSize, segIndex, segOffset, type) )
+                            return;
+                        handler(segIndex, segOffset, type, stop);
+                        segOffset += skip + pointerSize;
+                    }
+                    break;
+                default:
+                    diag.error("unknown rebase opcode 0x%02X", opcode);
+            }
+        }
+    }
+    else {
+        // old binary
+        const relocation_info* const    relocsStart = (relocation_info*)getLinkEditContent(leInfo.layout, leInfo.dynSymTab->locreloff);
+        const relocation_info* const    relocsEnd   = &relocsStart[leInfo.dynSymTab->nlocrel];
+        bool                            stop = false;
+        const uint8_t                   relocSize = (is64() ? 3 : 2);
+        for (const relocation_info* reloc=relocsStart; (reloc < relocsEnd) && !stop; ++reloc) {
+            if ( reloc->r_length != relocSize ) {
+                diag.error("local relocation has wrong r_length");
+                break;
+            }
+            if ( reloc->r_type != 0 ) { // 0 == X86_64_RELOC_UNSIGNED == GENERIC_RELOC_VANILLA ==  ARM64_RELOC_UNSIGNED
+                diag.error("local relocation has wrong r_type");
+                break;
+            }
+            doLocalReloc(diag, reloc->r_address, stop, handler);
+         }
+        // then process indirect symbols
+        forEachIndirectPointer(diag, ^(uint32_t segIndex, uint64_t segOffset, bool bind, int bindLibOrdinal,
+                                       const char* bindSymbolName, bool bindWeakImport, bool bindLazy, bool selfModifyingStub, bool& indStop) {
+            if ( !bind && !bindLazy )
+                handler(segIndex, segOffset, REBASE_TYPE_POINTER, indStop);
+        });
+    }
+}
+
+bool MachOParser::doLocalReloc(Diagnostics& diag, uint32_t r_address, bool& stop, void (^handler)(uint32_t segIndex, uint64_t segOffset, uint8_t type, bool& stop)) const
+{
+    bool                firstWritable = (header()->cputype == CPU_TYPE_X86_64);
+    __block uint64_t    relocBaseAddress = 0;
+    __block bool        baseFound = false;
+    __block uint32_t    segIndex = 0;
+    forEachSegment(^(const char* segName, uint32_t fileOffset, uint32_t fileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, bool &stopSeg) {
+        if ( !baseFound ) {
+            if ( !firstWritable || (protections & VM_PROT_WRITE) ) {
+                baseFound = true;
+                relocBaseAddress = vmAddr;
+            }
+        }
+        if ( baseFound && (vmAddr < relocBaseAddress+r_address) && (relocBaseAddress+r_address < vmAddr+vmSize) ) {
+            uint8_t  type = REBASE_TYPE_POINTER;
+            uint64_t segOffset = relocBaseAddress + r_address - vmAddr;
+            handler(segIndex, segOffset, type, stop);
+            stopSeg = true;
+        }
+        ++segIndex;
+    });
+
+    return false;
+}
+
+int MachOParser::libOrdinalFromDesc(uint16_t n_desc) const
+{
+    // -flat_namespace is always flat lookup
+    if ( (header()->flags & MH_TWOLEVEL) == 0 )
+        return BIND_SPECIAL_DYLIB_FLAT_LOOKUP;
+
+    // extract byte from undefined symbol entry
+    int libIndex = GET_LIBRARY_ORDINAL(n_desc);
+    switch ( libIndex ) {
+        case SELF_LIBRARY_ORDINAL:
+            return BIND_SPECIAL_DYLIB_SELF;
+
+        case DYNAMIC_LOOKUP_ORDINAL:
+            return BIND_SPECIAL_DYLIB_FLAT_LOOKUP;
+
+        case EXECUTABLE_ORDINAL:
+            return BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE;
+    }
+
+    return libIndex;
+}
+
+bool MachOParser::doExternalReloc(Diagnostics& diag, uint32_t r_address, uint32_t r_symbolnum, LinkEditInfo& leInfo, bool& stop,
+                                    void (^handler)(uint32_t dataSegIndex, uint64_t dataSegOffset, uint8_t type, int libOrdinal,
+                                                    uint64_t addend, const char* symbolName, bool weakImport, bool lazy, bool& stop)) const
+{
+    const bool          firstWritable    = (header()->cputype == CPU_TYPE_X86_64);
+    const bool          is64Bit          = is64();
+    __block uint64_t    relocBaseAddress = 0;
+    __block bool        baseFound        = false;
+    __block uint32_t    segIndex         = 0;
+    forEachSegment(^(const char* segName, uint32_t fileOffset, uint32_t fileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, bool &stopSeg) {
+        if ( !baseFound ) {
+            if ( !firstWritable || (protections & VM_PROT_WRITE) ) {
+                baseFound = true;
+                relocBaseAddress = vmAddr;
+            }
+        }
+        if ( baseFound && (vmAddr < relocBaseAddress+r_address) && (relocBaseAddress+r_address < vmAddr+vmSize) ) {
+            uint8_t                 type        = BIND_TYPE_POINTER;
+            uint64_t                segOffset   = relocBaseAddress + r_address - vmAddr;
+            const void*             symbolTable = getLinkEditContent(leInfo.layout, leInfo.symTab->symoff);
+            const struct nlist_64*  symbols64   = (nlist_64*)symbolTable;
+            const struct nlist*     symbols32   = (struct nlist*)symbolTable;
+            const char*             stringPool  = (char*)getLinkEditContent(leInfo.layout, leInfo.symTab->stroff);
+            uint32_t                symCount    = leInfo.symTab->nsyms;
+            uint32_t                poolSize    = leInfo.symTab->strsize;
+            if ( r_symbolnum < symCount ) {
+                uint16_t n_desc = is64Bit ? symbols64[r_symbolnum].n_desc : symbols32[r_symbolnum].n_desc;
+                uint32_t libOrdinal = libOrdinalFromDesc(n_desc);
+                uint32_t strOffset = is64Bit ? symbols64[r_symbolnum].n_un.n_strx : symbols32[r_symbolnum].n_un.n_strx;
+                if ( strOffset < poolSize ) {
+                    const char* symbolName  = stringPool + strOffset;
+                    bool        weakImport  = (n_desc & N_WEAK_REF);
+                    bool        lazy        = false;
+                    uint64_t    addend      = is64Bit ? (*((uint64_t*)((char*)header()+fileOffset+segOffset))) : (*((uint32_t*)((char*)header()+fileOffset+segOffset)));
+                    handler(segIndex, segOffset, type, libOrdinal, addend, symbolName, weakImport, lazy, stop);
+                    stopSeg = true;
+                }
+            }
+        }
+        ++segIndex;
+    });
+
+    return false;
+}
+
+bool MachOParser::invalidBindState(Diagnostics& diag, const char* opcodeName, const LinkEditInfo& leInfo, bool segIndexSet, bool libraryOrdinalSet,
+                                   uint32_t dylibCount, int libOrdinal, uint32_t pointerSize, uint8_t segmentIndex, uint64_t segmentOffset, uint8_t type, const char* symbolName) const
+{
+    if ( !segIndexSet ) {
+        diag.error("%s missing preceding BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB", opcodeName);
+        return true;
+    }
+    if ( segmentIndex >= leInfo.layout.segmentCount )  {
+        diag.error("%s segment index %d too large", opcodeName, segmentIndex);
+        return true;
+    }
+    if ( segmentOffset > (leInfo.layout.segments[segmentIndex].segSize-pointerSize) ) {
+        diag.error("%s current segment offset 0x%08llX beyond segment size (0x%08llX)", opcodeName, segmentOffset, leInfo.layout.segments[segmentIndex].segSize);
+        return true;
+    }
+    if ( symbolName == NULL ) {
+        diag.error("%s missing preceding BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM", opcodeName);
+        return true;
+    }
+    if ( !libraryOrdinalSet ) {
+        diag.error("%s missing preceding BIND_OPCODE_SET_DYLIB_ORDINAL", opcodeName);
+        return true;
+    }
+    if ( libOrdinal > (int)dylibCount ) {
+        diag.error("%s has library ordinal too large (%d) max (%d)", opcodeName, libOrdinal, dylibCount);
+        return true;
+    }
+    if ( libOrdinal < -2 ) {
+        diag.error("%s has unknown library special ordinal (%d)", opcodeName, libOrdinal);
+        return true;
+    }
+    switch ( type )  {
+        case BIND_TYPE_POINTER:
+            if ( !leInfo.layout.segments[segmentIndex].writable ) {
+                diag.error("%s pointer bind is in non-writable segment", opcodeName);
+                return true;
+            }
+            if ( leInfo.layout.segments[segmentIndex].executable ) {
+                diag.error("%s pointer bind is in executable segment", opcodeName);
+                return true;
+            }
+            break;
+        case BIND_TYPE_TEXT_ABSOLUTE32:
+        case BIND_TYPE_TEXT_PCREL32:
+            if ( !leInfo.layout.segments[segmentIndex].textRelocsAllowed ) {
+                diag.error("%s text bind is in segment that does not support text relocations", opcodeName);
+                return true;
+            }
+            if ( leInfo.layout.segments[segmentIndex].writable ) {
+                diag.error("%s text bind is in writable segment", opcodeName);
+                return true;
+            }
+            if ( !leInfo.layout.segments[segmentIndex].executable ) {
+                diag.error("%s pointer bind is in non-executable segment", opcodeName);
+                return true;
+            }
+            break;
+        default:
+            diag.error("%s unknown bind type %d", opcodeName, type);
+            return true;
+    }
+    return false;
+}
+
+void MachOParser::forEachBind(Diagnostics& diag, void (^handler)(uint32_t dataSegIndex, uint64_t dataSegOffset, uint8_t type,
+                              int libOrdinal, uint64_t addend, const char* symbolName, bool weakImport, bool lazy, bool& stop)) const
+{
+    LinkEditInfo leInfo;
+    getLinkEditPointers(diag, leInfo);
+    if ( diag.hasError() )
+        return;
+    const uint32_t dylibCount = dependentDylibCount();
+
+    if ( leInfo.dyldInfo != nullptr ) {
+        // process bind opcodes
+        const uint8_t*  p    = getLinkEditContent(leInfo.layout, leInfo.dyldInfo->bind_off);
+        const uint8_t*  end  = p + leInfo.dyldInfo->bind_size;
+        const uint32_t  pointerSize = (is64() ? 8 : 4);
+        uint8_t         type = 0;
+        uint64_t        segmentOffset = 0;
+        uint8_t         segmentIndex = 0;
+        const char*     symbolName = NULL;
+        int             libraryOrdinal = 0;
+        bool            segIndexSet = false;
+        bool            libraryOrdinalSet = false;
+
+        int64_t         addend = 0;
+        uint64_t        count;
+        uint64_t        skip;
+        bool            weakImport = false;
+        bool            done = false;
+        bool            stop = false;
+        while ( !done && !stop && diag.noError() && (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 = (int)read_uleb128(diag, 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:
+                    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(diag, p, end);
+                    break;
+                case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB:
+                    segmentIndex = immediate;
+                    segmentOffset = read_uleb128(diag, p, end);
+                    segIndexSet = true;
+                    break;
+                case BIND_OPCODE_ADD_ADDR_ULEB:
+                    segmentOffset += read_uleb128(diag, p, end);
+                    break;
+                case BIND_OPCODE_DO_BIND:
+                    if ( invalidBindState(diag, "BIND_OPCODE_DO_BIND", leInfo, segIndexSet, libraryOrdinalSet, dylibCount, libraryOrdinal, pointerSize, segmentIndex, segmentOffset, type, symbolName) )
+                        return;
+                    handler(segmentIndex, segmentOffset, type, libraryOrdinal, addend, symbolName, weakImport, false, stop);
+                    segmentOffset += pointerSize;
+                    break;
+                case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB:
+                    if ( invalidBindState(diag, "BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB", leInfo, segIndexSet, libraryOrdinalSet, dylibCount, libraryOrdinal, pointerSize, segmentIndex, segmentOffset, type, symbolName) )
+                        return;
+                    handler(segmentIndex, segmentOffset, type, libraryOrdinal, addend, symbolName, weakImport, false, stop);
+                    segmentOffset += read_uleb128(diag, p, end) + pointerSize;
+                    break;
+                case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED:
+                    if ( invalidBindState(diag, "BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED", leInfo, segIndexSet, libraryOrdinalSet, dylibCount, libraryOrdinal, pointerSize, segmentIndex, segmentOffset, type, symbolName) )
+                        return;
+                    handler(segmentIndex, segmentOffset, type, libraryOrdinal, addend, symbolName, weakImport, false, stop);
+                    segmentOffset += immediate*pointerSize + pointerSize;
+                    break;
+                case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB:
+                    count = read_uleb128(diag, p, end);
+                    skip = read_uleb128(diag, p, end);
+                    for (uint32_t i=0; i < count; ++i) {
+                        if ( invalidBindState(diag, "BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB", leInfo, segIndexSet, libraryOrdinalSet, dylibCount, libraryOrdinal, pointerSize, segmentIndex, segmentOffset, type, symbolName) )
+                            return;
+                        handler(segmentIndex, segmentOffset, type, libraryOrdinal, addend, symbolName, weakImport, false, stop);
+                        segmentOffset += skip + pointerSize;
+                    }
+                    break;
+                default:
+                    diag.error("bad bind opcode 0x%02X", *p);
+            }
+        }
+        if ( diag.hasError() || stop )
+            return;
+        // process lazy bind opcodes
+        if ( leInfo.dyldInfo->lazy_bind_size != 0 ) {
+            p               = getLinkEditContent(leInfo.layout, leInfo.dyldInfo->lazy_bind_off);
+            end             = p + leInfo.dyldInfo->lazy_bind_size;
+            type            = BIND_TYPE_POINTER;
+            segmentOffset   = 0;
+            segmentIndex    = 0;
+            symbolName      = NULL;
+            libraryOrdinal  = 0;
+            segIndexSet     = false;
+            libraryOrdinalSet= false;
+            addend          = 0;
+            weakImport      = false;
+            stop            = false;
+            while ( !stop && diag.noError() && (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;
+                        libraryOrdinalSet = true;
+                        break;
+                    case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB:
+                        libraryOrdinal = (int)read_uleb128(diag, 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:
+                        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(diag, p, end);
+                        break;
+                    case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB:
+                        segmentIndex = immediate;
+                        segmentOffset = read_uleb128(diag, p, end);
+                        segIndexSet = true;
+                        break;
+                    case BIND_OPCODE_DO_BIND:
+                        if ( invalidBindState(diag, "BIND_OPCODE_DO_BIND", leInfo, segIndexSet, libraryOrdinalSet, dylibCount, libraryOrdinal, pointerSize, segmentIndex, segmentOffset, type, symbolName) )
+                            return;
+                        handler(segmentIndex, segmentOffset, type, libraryOrdinal, addend, symbolName, weakImport, true, stop);
+                        segmentOffset += pointerSize;
+                        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:
+                        diag.error("bad lazy bind opcode 0x%02X", opcode);
+                        break;
+                }
+            }
+        }
+    }
+    else {
+        // old binary, first process relocation
+        const relocation_info* const    relocsStart = (relocation_info*)getLinkEditContent(leInfo.layout, leInfo.dynSymTab->extreloff);
+        const relocation_info* const    relocsEnd   = &relocsStart[leInfo.dynSymTab->nextrel];
+        bool                            stop = false;
+        const uint8_t                   relocSize = (is64() ? 3 : 2);
+        for (const relocation_info* reloc=relocsStart; (reloc < relocsEnd) && !stop; ++reloc) {
+            if ( reloc->r_length != relocSize ) {
+                diag.error("external relocation has wrong r_length");
+                break;
+            }
+            if ( reloc->r_type != 0 ) { // 0 == X86_64_RELOC_UNSIGNED == GENERIC_RELOC_VANILLA == ARM64_RELOC_UNSIGNED
+                 diag.error("external relocation has wrong r_type");
+                break;
+            }
+            doExternalReloc(diag, reloc->r_address, reloc->r_symbolnum, leInfo, stop, handler);
+        }
+        // then process indirect symbols
+        forEachIndirectPointer(diag, ^(uint32_t segIndex, uint64_t segOffset, bool bind, int bindLibOrdinal,
+                                       const char* bindSymbolName, bool bindWeakImport, bool bindLazy, bool selfModifyingStub, bool& indStop) {
+            if ( bind )
+                handler(segIndex, segOffset, (selfModifyingStub ? BIND_TYPE_IMPORT_JMP_REL32 : BIND_TYPE_POINTER), bindLibOrdinal, 0, bindSymbolName, bindWeakImport, bindLazy, indStop);
+        });
+    }
+}
+
+
+void MachOParser::forEachWeakDef(Diagnostics& diag, void (^handler)(bool strongDef, uint32_t dataSegIndex, uint64_t dataSegOffset,
+                                                                    uint64_t addend, const char* symbolName, bool& stop)) const
+{
+    LinkEditInfo leInfo;
+    getLinkEditPointers(diag, leInfo);
+    if ( diag.hasError() )
+        return;
+
+    const uint32_t dylibCount = dependentDylibCount();
+    if ( leInfo.dyldInfo != nullptr ) {
+        // process weak bind opcodes
+        const uint8_t*  p    = getLinkEditContent(leInfo.layout, leInfo.dyldInfo->weak_bind_off);
+        const uint8_t*  end  = p + leInfo.dyldInfo->weak_bind_size;
+        const uint32_t  pointerSize = (is64() ? 8 : 4);
+        uint8_t         type = 0;
+        uint64_t        segmentOffset = 0;
+        uint8_t         segmentIndex = 0;
+        const char*     symbolName = NULL;
+        int64_t         addend = 0;
+        uint64_t        count;
+        uint64_t        skip;
+        bool            segIndexSet = false;
+        bool            done = false;
+        bool            stop = false;
+        while ( !done && !stop && diag.noError() && (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:
+                case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB:
+                case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM:
+                    diag.error("unexpected dylib ordinal in weak binding info");
+                    return;
+                case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM:
+                    symbolName = (char*)p;
+                    while (*p != '\0')
+                        ++p;
+                    ++p;
+                    if ( (immediate & BIND_SYMBOL_FLAGS_NON_WEAK_DEFINITION) != 0 )
+                        handler(true, 0, 0, 0, symbolName, stop);
+                   break;
+                case BIND_OPCODE_SET_TYPE_IMM:
+                    type = immediate;
+                    break;
+                case BIND_OPCODE_SET_ADDEND_SLEB:
+                    addend = read_sleb128(diag, p, end);
+                    break;
+                case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB:
+                    segmentIndex = immediate;
+                    segmentOffset = read_uleb128(diag, p, end);
+                    segIndexSet = true;
+                    break;
+                case BIND_OPCODE_ADD_ADDR_ULEB:
+                    segmentOffset += read_uleb128(diag, p, end);
+                    break;
+                case BIND_OPCODE_DO_BIND:
+                    if ( invalidBindState(diag, "BIND_OPCODE_DO_BIND", leInfo, segIndexSet, true, dylibCount, -2, pointerSize, segmentIndex, segmentOffset, type, symbolName) )
+                        return;
+                    handler(false, segmentIndex, segmentOffset, addend, symbolName, stop);
+                    segmentOffset += pointerSize;
+                    break;
+                case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB:
+                    if ( invalidBindState(diag, "BIND_OPCODE_DO_BIND", leInfo, segIndexSet, true, dylibCount, -2, pointerSize, segmentIndex, segmentOffset, type, symbolName) )
+                        return;
+                    handler(false, segmentIndex, segmentOffset, addend, symbolName, stop);
+                    segmentOffset += read_uleb128(diag, p, end) + pointerSize;
+                    break;
+                case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED:
+                     if ( invalidBindState(diag, "BIND_OPCODE_DO_BIND", leInfo, segIndexSet, true, dylibCount, -2, pointerSize, segmentIndex, segmentOffset, type, symbolName) )
+                        return;
+                    handler(false, segmentIndex, segmentOffset, addend, symbolName, stop);
+                    segmentOffset += immediate*pointerSize + pointerSize;
+                    break;
+                case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB:
+                    count = read_uleb128(diag, p, end);
+                    skip = read_uleb128(diag, p, end);
+                    for (uint32_t i=0; i < count; ++i) {
+                        if ( invalidBindState(diag, "BIND_OPCODE_DO_BIND", leInfo, segIndexSet, true, dylibCount, -2, pointerSize, segmentIndex, segmentOffset, type, symbolName) )
+                            return;
+                        handler(false, segmentIndex, segmentOffset, addend, symbolName, stop);
+                        segmentOffset += skip + pointerSize;
+                    }
+                    break;
+                default:
+                    diag.error("bad weak bind opcode 0x%02X", *p);
+            }
+        }
+        if ( diag.hasError() || stop )
+            return;
+     }
+    else {
+        // old binary
+        //assert(0 && "weak defs not supported for old binaries yet");
+    }
+}
+
+
+
+void MachOParser::forEachIndirectPointer(Diagnostics& diag, void (^handler)(uint32_t dataSegIndex, uint64_t dataSegOffset, bool bind, int bindLibOrdinal,
+                                                                            const char* bindSymbolName, bool bindWeakImport, bool bindLazy, bool selfModifyingStub, bool& stop)) const
+{
+    LinkEditInfo leInfo;
+    getLinkEditPointers(diag, leInfo);
+    if ( diag.hasError() )
+        return;
+
+    // find lazy and non-lazy pointer sections
+    const bool              is64Bit                  = is64();
+    const uint32_t* const   indirectSymbolTable      = (uint32_t*)getLinkEditContent(leInfo.layout, leInfo.dynSymTab->indirectsymoff);
+    const uint32_t          indirectSymbolTableCount = leInfo.dynSymTab->nindirectsyms;
+    const uint32_t          pointerSize              = is64Bit ? 8 : 4;
+    const void*             symbolTable              = getLinkEditContent(leInfo.layout, leInfo.symTab->symoff);
+    const struct nlist_64*  symbols64                = (nlist_64*)symbolTable;
+    const struct nlist*     symbols32                = (struct nlist*)symbolTable;
+    const char*             stringPool               = (char*)getLinkEditContent(leInfo.layout, leInfo.symTab->stroff);
+    uint32_t                symCount                 = leInfo.symTab->nsyms;
+    uint32_t                poolSize                 = leInfo.symTab->strsize;
+    __block bool            stop                     = false;
+    forEachSection(^(const char* segName, const char* sectionName, uint32_t flags, uint64_t addr, const void* content,
+                     uint64_t size, uint32_t alignP2, uint32_t reserved1, uint32_t reserved2, bool illegalSectionSize, bool& sectionStop) {
+        uint8_t  sectionType  = (flags & SECTION_TYPE);
+        if ( (sectionType != S_LAZY_SYMBOL_POINTERS) && (sectionType != S_NON_LAZY_SYMBOL_POINTERS) && (sectionType != S_SYMBOL_STUBS) )
+            return;
+        bool selfModifyingStub = (sectionType == S_SYMBOL_STUBS) && (flags & S_ATTR_SELF_MODIFYING_CODE) && (reserved2 == 5) && (header()->cputype == CPU_TYPE_I386);
+        if ( (flags & S_ATTR_SELF_MODIFYING_CODE) && !selfModifyingStub ) {
+            diag.error("S_ATTR_SELF_MODIFYING_CODE section type only valid in old i386 binaries");
+            sectionStop = true;
+            return;
+        }
+        uint32_t elementSize = selfModifyingStub ? reserved2 : pointerSize;
+        uint32_t elementCount = (uint32_t)(size/elementSize);
+        if (greaterThanAddOrOverflow(reserved1, elementCount, indirectSymbolTableCount)) {
+            diag.error("section %s overflows indirect symbol table", sectionName);
+            sectionStop = true;
+            return;
+        }
+        __block uint32_t index = 0;
+        __block uint32_t segIndex = 0;
+        __block uint64_t sectionSegOffset;
+        forEachSegment(^(const char* segmentName, uint32_t fileOffset, uint32_t fileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, bool &segStop) {
+            if ( (vmAddr <= addr) && (addr < vmAddr+vmSize) ) {
+                sectionSegOffset = addr - vmAddr;
+                segIndex = index;
+                segStop = true;
+            }
+            ++index;
+        });
+
+        for (int i=0; (i < elementCount) && !stop; ++i) {
+            uint32_t symNum = indirectSymbolTable[reserved1 + i];
+            if ( symNum == INDIRECT_SYMBOL_ABS )
+                continue;
+            uint64_t segOffset = sectionSegOffset+i*elementSize;
+            if ( symNum == INDIRECT_SYMBOL_LOCAL ) {
+                handler(segIndex, segOffset, false, 0, "", false, false, false, stop);
+                continue;
+            }
+            if ( symNum > symCount ) {
+                diag.error("indirect symbol[%d] = %d which is invalid symbol index", reserved1 + i, symNum);
+                sectionStop = true;
+                return;
+            }
+            uint16_t n_desc = is64Bit ? symbols64[symNum].n_desc : symbols32[symNum].n_desc;
+            uint32_t libOrdinal = libOrdinalFromDesc(n_desc);
+            uint32_t strOffset = is64Bit ? symbols64[symNum].n_un.n_strx : symbols32[symNum].n_un.n_strx;
+            if ( strOffset > poolSize ) {
+               diag.error("symbol[%d] string offset out of range", reserved1 + i);
+                sectionStop = true;
+                return;
+            }
+            const char* symbolName  = stringPool + strOffset;
+            bool        weakImport  = (n_desc & N_WEAK_REF);
+            bool        lazy        = (sectionType == S_LAZY_SYMBOL_POINTERS);
+            handler(segIndex, segOffset, true, libOrdinal, symbolName, weakImport, lazy, selfModifyingStub, stop);
+        }
+        sectionStop = stop;
+    });
+}
+
+void MachOParser::forEachInterposingTuple(Diagnostics& diag, void (^handler)(uint32_t segIndex, uint64_t replacementSegOffset, uint64_t replaceeSegOffset, uint64_t replacementContent, bool& stop)) const
+{
+    const bool     is64Bit      = is64();
+    const unsigned entrySize    = is64Bit ? 16 : 8;
+    const unsigned pointerSize  = is64Bit ?  8 : 4;
+    forEachSection(^(const char* segmentName, const char* sectionName, uint32_t flags, uint64_t addr, const void* content, uint64_t size, uint32_t alignP2, uint32_t reserved1, uint32_t reserved2, bool illegalSectionSize, bool& secStop) {
+        if ( ((flags & SECTION_TYPE) == S_INTERPOSING) || ((strcmp(sectionName, "__interpose") == 0) && (strcmp(segmentName, "__DATA") == 0)) ) {
+            if ( (size % entrySize) != 0 ) {
+                diag.error("interposing section %s/%s has bad size", segmentName, sectionName);
+                secStop = true;
+                return;
+            }
+            if ( illegalSectionSize ) {
+                diag.error("interposing section %s/%s extends beyond the end of the segment", segmentName, sectionName);
+                secStop = true;
+                return;
+            }
+            if ( ((long)content % pointerSize) != 0 ) {
+                diag.error("interposing section %s/%s is not pointer aligned", segmentName, sectionName);
+                secStop = true;
+                return;
+            }
+            __block uint32_t sectionSegIndex  = 0;
+            __block uint64_t sectionSegOffset = 0;
+            forEachSegment(^(const char* segName, uint32_t fileOffset, uint32_t fileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, uint32_t segIndex, uint64_t sizeOfSections, uint8_t p2align, bool& segStop) {
+                if ( (vmAddr <= addr) && (addr < vmAddr+vmSize) ) {
+                    sectionSegIndex  = segIndex;
+                    sectionSegOffset = addr - vmAddr;
+                    segStop          = true;
+                }
+            });
+            if ( sectionSegIndex == 0 ) {
+                diag.error("interposing section %s/%s is not in a segment", segmentName, sectionName);
+                secStop = true;
+                return;
+            }
+            uint32_t offset = 0;
+            bool tupleStop = false;
+            for (int i=0; i < (size/entrySize); ++i) {
+                uint64_t replacementContent = is64Bit ? (*(uint64_t*)((char*)content + offset)) :  (*(uint32_t*)((char*)content + offset));
+                handler(sectionSegIndex, sectionSegOffset+offset, sectionSegOffset+offset+pointerSize, replacementContent, tupleStop);
+                offset += entrySize;
+                if ( tupleStop )
+                    break;
+            }
+        }
+    });
+}
+
+
+const void* MachOParser::content(uint64_t vmOffset)
+{
+    __block const void* result = nullptr;
+    __block uint32_t firstSegFileOffset = 0;
+    __block uint64_t firstSegVmAddr = 0;
+       if ( isRaw() ) {
+        forEachSegment(^(const char* segName, uint32_t fileOffset, uint32_t fileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, uint32_t segIndex, uint64_t sizeOfSections, uint8_t p2align, bool &stop) {
+            if ( firstSegFileOffset == 0) {
+                if ( fileSize == 0 )
+                    return; // skip __PAGEZERO
+                firstSegFileOffset = fileOffset;
+                firstSegVmAddr = vmAddr;
+            }
+            uint64_t segVmOffset = vmAddr - firstSegVmAddr;
+            if ( (vmOffset >= segVmOffset) && (vmOffset < segVmOffset+vmSize) ) {
+                result = (char*)(header()) + (fileOffset - firstSegFileOffset) + (vmOffset - segVmOffset);
+                stop = true;
+            }
+        });
+       }
+    else if ( inRawCache() ) {
+        forEachSegment(^(const char* segName, uint32_t fileOffset, uint32_t fileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, uint32_t segIndex, uint64_t sizeOfSections, uint8_t p2align, bool &stop) {
+            if ( firstSegFileOffset == 0 ) {
+                firstSegFileOffset = fileOffset;
+                firstSegVmAddr = vmAddr;
+            }
+            uint64_t segVmOffset = vmAddr - firstSegVmAddr;
+            if ( (vmOffset >= segVmOffset) && (vmOffset < segVmOffset+vmSize) ) {
+                result = (char*)(header()) + (fileOffset - firstSegFileOffset) + (vmOffset - segVmOffset);
+                stop = true;
+            }
+        });
+    }
+    else {
+        // non-raw cache is easy
+        result = (char*)(header()) + vmOffset;
+    }
+       return result;
+}
+
+#endif  //  !DYLD_IN_PROCESS
+
+bool MachOParser::isFairPlayEncrypted(uint32_t& textOffset, uint32_t& size)
+{
+    textOffset = 0;
+    size = 0;
+    Diagnostics diag;
+    forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
+         if ( (cmd->cmd == LC_ENCRYPTION_INFO) || (cmd->cmd == LC_ENCRYPTION_INFO_64) ) {
+            const encryption_info_command* encCmd = (encryption_info_command*)cmd;
+            if ( encCmd->cryptid == 1 ) {
+                // Note: cryptid is 0 in just-built apps.  The iTunes App Store sets cryptid to 1
+                textOffset = encCmd->cryptoff;
+                size       = encCmd->cryptsize;
+            }
+            stop = true;
+        }
+    });
+    diag.assertNoError();   // any malformations in the file should have been caught by earlier validate() call
+    return (textOffset != 0);
+}
+
+bool MachOParser::cdHashOfCodeSignature(const void* codeSigStart, size_t codeSignLen, uint8_t cdHash[20])
+{
+    const CS_CodeDirectory* cd = (const CS_CodeDirectory*)findCodeDirectoryBlob(codeSigStart, codeSignLen);
+    if ( cd == nullptr )
+        return false;
+
+    uint32_t cdLength = htonl(cd->length);
+    if ( cd->hashType == CS_HASHTYPE_SHA256 ) {
+        uint8_t digest[CC_SHA256_DIGEST_LENGTH];
+        CC_SHA256(cd, cdLength, digest);
+        // cd-hash of sigs that use SHA256 is the first 20 bytes of the SHA256 of the code digest
+        memcpy(cdHash, digest, 20);
+        return true;
+    }
+    else if ( cd->hashType == CS_HASHTYPE_SHA1 ) {
+        // compute hash directly into return buffer
+        CC_SHA1(cd, cdLength, cdHash);
+        return true;
+    }
+
+    return false;
+}
+
+const void* MachOParser::findCodeDirectoryBlob(const void* codeSigStart, size_t codeSignLen)
+{
+    // verify min length of overall code signature
+    if ( codeSignLen < sizeof(CS_SuperBlob) )
+        return nullptr;
+
+    // verify magic at start
+    const CS_SuperBlob* codeSuperBlob = (CS_SuperBlob*)codeSigStart;
+    if ( codeSuperBlob->magic != htonl(CSMAGIC_EMBEDDED_SIGNATURE) )
+        return nullptr;
+
+    // verify count of sub-blobs not too large
+    uint32_t subBlobCount = htonl(codeSuperBlob->count);
+    if ( (codeSignLen-sizeof(CS_SuperBlob))/sizeof(CS_BlobIndex) < subBlobCount )
+        return nullptr;
+
+    // walk each sub blob, looking at ones with type CSSLOT_CODEDIRECTORY
+    for (uint32_t i=0; i < subBlobCount; ++i) {
+        if ( codeSuperBlob->index[i].type != htonl(CSSLOT_CODEDIRECTORY) )
+            continue;
+        uint32_t cdOffset = htonl(codeSuperBlob->index[i].offset);
+        // verify offset is not out of range
+        if ( cdOffset > (codeSignLen - sizeof(CS_CodeDirectory)) )
+            return nullptr;
+        const CS_CodeDirectory* cd = (CS_CodeDirectory*)((uint8_t*)codeSuperBlob + cdOffset);
+        uint32_t cdLength = htonl(cd->length);
+        // verify code directory length not out of range
+        if ( cdLength > (codeSignLen - cdOffset) )
+            return nullptr;
+        if ( cd->magic == htonl(CSMAGIC_CODEDIRECTORY) )
+            return cd;
+    }
+    return nullptr;
+}
+
+
+
+
+} // namespace dyld3
+
diff --git a/dyld3/MachOParser.h b/dyld3/MachOParser.h
new file mode 100644 (file)
index 0000000..73b5ead
--- /dev/null
@@ -0,0 +1,279 @@
+/*
+ * Copyright (c) 2017 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 MachOParser_h
+#define MachOParser_h
+
+#include <stdint.h>
+#include <uuid/uuid.h>
+#include <mach-o/loader.h>
+
+#include <array>
+#include <string>
+#include <vector>
+
+#include "Diagnostics.h"
+
+
+#define BIND_TYPE_IMPORT_JMP_REL32 4
+
+namespace dyld3 {
+
+// Note, this should make PLATFORM_* values in <mach-o/loader.h>
+enum class Platform {
+    unknown     = 0,
+    macOS       = 1,
+    iOS         = 2,
+    tvOS        = 3,
+    watchOS     = 4,
+    bridgeOS    = 5
+};
+
+struct VIS_HIDDEN UUID {
+    UUID() {}
+    UUID(const UUID& other) { uuid_copy(&_bytes[0], &other._bytes[0]); }
+    UUID(const uuid_t other_uuid) { uuid_copy(&_bytes[0], other_uuid); }
+    bool operator<(const UUID& other) const { return uuid_compare(&_bytes[0], &other._bytes[0]) < 0; }
+    bool operator==(const UUID& other) const { return uuid_compare(&_bytes[0], &other._bytes[0]) == 0; }
+    bool operator!=(const UUID& other) const { return !(*this == other); }
+
+    size_t hash() const
+    {
+        size_t retval = 0;
+        for (auto i = 0; i < 16 / sizeof(size_t); ++i) {
+            retval ^= ((size_t*)(&_bytes[0]))[i];
+        }
+        return retval;
+    }
+    const unsigned char* get() const { return &_bytes[0]; };
+private:
+    std::array<unsigned char, 16> _bytes;
+};
+
+class VIS_HIDDEN MachOParser
+{
+public:
+#if !DYLD_IN_PROCESS
+    static bool         isValidMachO(Diagnostics& diag, const std::string& archName, Platform platform, const void* fileContent, size_t fileLength, const std::string& pathOpened, bool ignoreMainExecutables);
+    static bool         isArch(const mach_header* mh, const std::string& archName);
+    static std::string  archName(uint32_t cputype, uint32_t cpusubtype);
+    static std::string  platformName(Platform platform);
+    static std::string  versionString(uint32_t packedVersion);
+    static uint32_t     cpuTypeFromArchName(const std::string& archName);
+    static uint32_t     cpuSubtypeFromArchName(const std::string& archName);
+#else
+    static bool         isMachO(Diagnostics& diag, const void* fileContent, size_t fileLength);
+    static bool         wellFormedMachHeaderAndLoadCommands(const mach_header* mh);
+#endif
+                        MachOParser(const mach_header* mh, bool dyldCacheIsRaw=false);
+    bool                valid(Diagnostics& diag);
+
+    const mach_header*  header() const;
+    uint32_t            fileType() const;
+    std::string         archName() const;
+    bool                is64() const;
+    bool                inDyldCache() const;
+    bool                hasThreadLocalVariables() const;
+    Platform            platform() const;
+    uint64_t            preferredLoadAddress() const;
+    UUID                uuid() const;
+    bool                getUuid(uuid_t uuid) const;
+    bool                getPlatformAndVersion(Platform* platform, uint32_t* minOS, uint32_t* sdk) const;
+    bool                isSimulatorBinary() const;
+    bool                getDylibInstallName(const char** installName, uint32_t* compatVersion, uint32_t* currentVersion) const;
+    const char*         installName() const;
+    uint32_t            dependentDylibCount() const;
+    const char*         dependentDylibLoadPath(uint32_t depIndex) const;
+    void                forEachDependentDylib(void (^callback)(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool& stop)) const;
+    void                forEachSection(void (^callback)(const char* segName, const char* sectionName, uint32_t flags, const void* content, size_t size, bool illegalSectionSize, bool& stop)) const;
+    void                forEachSegment(void (^callback)(const char* segName, uint32_t fileOffset, uint32_t fileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, bool& stop)) const;
+    void                forEachGlobalSymbol(Diagnostics& diag, void (^callback)(const char* symbolName, uint64_t n_value, uint8_t n_type, uint8_t n_sect, uint16_t n_desc, bool& stop)) const;
+    void                forEachLocalSymbol(Diagnostics& diag, void (^callback)(const char* symbolName, uint64_t n_value, uint8_t n_type, uint8_t n_sect, uint16_t n_desc, bool& stop)) const;
+    void                forEachRPath(void (^callback)(const char* rPath, bool& stop)) const;
+    void                forEachSection(void (^callback)(const char* segName, const char* sectionName, uint32_t flags, uint64_t addr, const void* content, 
+                                                        uint64_t size, uint32_t alignP2, uint32_t reserved1, uint32_t reserved2, bool illegalSectionSize, bool& stop)) const;
+
+    struct FoundSymbol {
+        enum class Kind { headerOffset, absolute, resolverOffset };
+        Kind                kind;
+        bool                isThreadLocal;
+        const mach_header*  foundInDylib;
+        void*               foundExtra;
+        uint64_t            value;
+        uint32_t            resolverFuncOffset;
+        const char*         foundSymbolName;
+    };
+
+    typedef bool (^DependentFinder)(uint32_t depIndex, const char* depLoadPath, void* extra, const mach_header** foundMH, void** foundExtra);
+
+    bool                findExportedSymbol(Diagnostics& diag, const char* symbolName, void* extra, FoundSymbol& foundInfo, DependentFinder finder) const;
+    bool                findClosestSymbol(uint64_t unSlidAddr, const char** symbolName, uint64_t* symbolUnslidAddr) const;
+    bool                isFairPlayEncrypted(uint32_t& textOffset, uint32_t& size);
+
+#if DYLD_IN_PROCESS
+    intptr_t            getSlide() const;
+    bool                hasExportedSymbol(const char* symbolName, DependentFinder finder, void** result) const;
+    bool                findClosestSymbol(const void* addr, const char** symbolName, const void** symbolAddress) const;
+    const char*         segmentName(uint32_t segIndex) const;
+#else
+
+    bool                uses16KPages() const;
+    bool                hasObjC() const;
+    bool                hasWeakDefs() const;
+    bool                isEncrypted() const;
+    bool                hasPlusLoadMethod(Diagnostics& diag) const;
+    bool                hasInitializer(Diagnostics& diag) const;
+    bool                getCDHash(uint8_t cdHash[20]);
+    bool                hasCodeSignature(uint32_t& fileOffset, uint32_t& size);
+    bool                usesLibraryValidation() const;
+    bool                isRestricted() const;
+    bool                getEntry(uint32_t& offset, bool& usesCRT);
+    bool                canBePlacedInDyldCache(const std::string& path) const;
+    bool                canBePlacedInDyldCache(const std::string& path, std::set<std::string>& reasons) const;
+    bool                isDynamicExecutable() const;
+    bool                isSlideable() const;
+    void                forEachInitializer(Diagnostics& diag, void (^callback)(uint32_t offset)) const;
+    void                forEachDOFSection(Diagnostics& diag, void (^callback)(uint32_t offset)) const;
+    uint32_t            segmentCount() const;
+    void                forEachExportedSymbol(Diagnostics diag, void (^callback)(const char* symbolName, uint64_t imageOffset, bool isReExport, bool& stop)) const;
+    void                forEachSegment(void (^callback)(const char* segName, uint32_t fileOffset, uint32_t fileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, uint32_t segIndex, uint64_t sizeOfSections, uint8_t p2align, bool& stop)) const;
+    void                forEachRebase(Diagnostics& diag, void (^callback)(uint32_t dataSegIndex, uint64_t dataSegOffset, uint8_t type, bool& stop)) const;
+    void                forEachBind(Diagnostics& diag, void (^callback)(uint32_t dataSegIndex, uint64_t dataSegOffset, uint8_t type, int libOrdinal,
+                                                    uint64_t addend, const char* symbolName, bool weakImport, bool lazy, bool& stop)) const;
+    void                forEachWeakDef(Diagnostics& diag, void (^callback)(bool strongDef, uint32_t dataSegIndex, uint64_t dataSegOffset,
+                                                    uint64_t addend, const char* symbolName, bool& stop)) const;
+    void                forEachIndirectPointer(Diagnostics& diag, void (^handler)(uint32_t dataSegIndex, uint64_t dataSegOffset, bool bind, int bindLibOrdinal,
+                                                                                  const char* bindSymbolName, bool bindWeakImport, bool bindLazy, bool selfModifyingStub, bool& stop)) const;
+    void                forEachInterposingTuple(Diagnostics& diag, void (^handler)(uint32_t segIndex, uint64_t replacementSegOffset, uint64_t replaceeSegOffset, uint64_t replacementContent, bool& stop)) const;
+    const void*         content(uint64_t vmOffset);
+#endif
+
+    static const uint8_t*   trieWalk(Diagnostics& diag, const uint8_t* start, const uint8_t* end, const char* symbol);
+    static uint64_t         read_uleb128(Diagnostics& diag, const uint8_t*& p, const uint8_t* end);
+    static int64_t          read_sleb128(Diagnostics& diag, const uint8_t*& p, const uint8_t* end);
+    static bool             cdHashOfCodeSignature(const void* codeSigStart, size_t codeSignLen, uint8_t cdHash[20]);
+    static Platform         currentPlatform();
+
+private:
+   struct LayoutInfo {
+#if DYLD_IN_PROCESS
+        uintptr_t    slide;
+        uintptr_t    textUnslidVMAddr;
+        uintptr_t    linkeditUnslidVMAddr;
+        uint32_t     linkeditFileOffset;
+#else
+        uint32_t     segmentCount;
+        uint32_t     linkeditSegIndex;
+        struct {
+            uint64_t    mappingOffset;
+            uint64_t    fileOffset;
+            uint64_t    fileSize;
+            uint64_t    segUnslidAddress;
+            uint64_t    writable          :  1,
+                        executable        :  1,
+                        textRelocsAllowed :  1,  // segment supports text relocs (i386 only)
+                        segSize           : 61;
+           }            segments[128];
+#endif
+    };
+
+    struct LinkEditInfo
+    {
+        const dyld_info_command*     dyldInfo;
+        const symtab_command*        symTab;
+        const dysymtab_command*      dynSymTab;
+        const linkedit_data_command* splitSegInfo;
+        const linkedit_data_command* functionStarts;
+        const linkedit_data_command* dataInCode;
+        const linkedit_data_command* codeSig;
+        LayoutInfo                   layout;
+    };
+
+    void                    getLinkEditPointers(Diagnostics& diag, LinkEditInfo&) const;
+    void                    getLinkEditLoadCommands(Diagnostics& diag, LinkEditInfo& result) const;
+    void                    getLayoutInfo(LayoutInfo&) const;
+    const uint8_t*          getLinkEditContent(const LayoutInfo& info, uint32_t fileOffset) const;
+
+#if !DYLD_IN_PROCESS
+    struct ArchInfo
+    {
+        const char* name;
+        uint32_t    cputype;
+        uint32_t    cpusubtype;
+    };
+    static const ArchInfo   _s_archInfos[];
+
+    const uint8_t*          getContentForVMAddr(const LayoutInfo& info, uint64_t vmAddr) const;
+    bool                    doLocalReloc(Diagnostics& diag, uint32_t r_address, bool& stop, void (^callback)(uint32_t dataSegIndex, uint64_t dataSegOffset, uint8_t type, bool& stop)) const;
+    uint8_t                 relocPointerType() const;
+    int                     libOrdinalFromDesc(uint16_t n_desc) const;
+    bool                    doExternalReloc(Diagnostics& diag, uint32_t r_address, uint32_t r_symbolnum, LinkEditInfo& leInfo, bool& stop,
+                                            void (^callback)(uint32_t dataSegIndex, uint64_t dataSegOffset, uint8_t type, int libOrdinal,
+                                                             uint64_t addend, const char* symbolName, bool weakImport, bool lazy, bool& stop)) const;
+    bool                    validLoadCommands(Diagnostics& diag, size_t fileLen);
+    bool                    validEmbeddedPaths(Diagnostics& diag);
+    bool                    validSegments(Diagnostics& diag, size_t fileLen);
+    bool                    validLinkeditLayout(Diagnostics& diag);
+    bool                    invalidBindState(Diagnostics& diag, const char* opcodeName, const MachOParser::LinkEditInfo& leInfo, bool segIndexSet, bool libraryOrdinalSet,
+                                             uint32_t dylibCount, int libOrdinal, uint32_t pointerSize, uint8_t segmentIndex, uint64_t segmentOffset, uint8_t type, const char* symbolName) const;
+    bool                    invalidRebaseState(Diagnostics& diag, const char* opcodeName, const MachOParser::LinkEditInfo& leInfo, bool segIndexSet,
+                                              uint32_t pointerSize, uint8_t segmentIndex, uint64_t segmentOffset, uint8_t type) const;
+#endif
+    static const void*      findCodeDirectoryBlob(const void* codeSigStart, size_t codeSignLen);
+    void                    forEachSection(void (^callback)(const char* segName, uint32_t segIndex, uint64_t segVMAddr, const char* sectionName, uint32_t sectFlags,
+                                                            uint64_t sectAddr, uint64_t size, uint32_t alignP2, uint32_t reserved1, uint32_t reserved2, bool illegalSectionSize, bool& stop)) const;
+
+    void                    forEachLoadCommand(Diagnostics& diag, void (^callback)(const load_command* cmd, bool& stop)) const;
+    bool                    isRaw() const;
+    bool                    inRawCache() const;
+
+    long                _data; // if low bit true, then this is raw file (not loaded image)
+};
+
+
+
+class VIS_HIDDEN FatUtil
+{
+public:
+    static bool         isFatFile(const void* fileStart);
+    static void         forEachSlice(Diagnostics& diag, const void* fileContent, size_t fileLen, void (^callback)(uint32_t sliceCpuType, uint32_t sliceCpuSubType, const void* sliceStart, size_t sliceSize, bool& stop));
+#if !DYLD_IN_PROCESS
+    static bool         isFatFileWithSlice(Diagnostics& diag, const void* fileContent, size_t fileLen, const std::string& archName, size_t& sliceOffset, size_t& sliceLen, bool& missingSlice);
+#endif
+};
+
+
+} // namespace dyld3
+
+namespace std {
+template <>
+struct hash<dyld3::UUID> {
+    size_t operator()(const dyld3::UUID& x) const
+    {
+        return x.hash();
+    }
+};
+}
+
+#endif // MachOParser_h
diff --git a/dyld3/PathOverrides.cpp b/dyld3/PathOverrides.cpp
new file mode 100644 (file)
index 0000000..2516175
--- /dev/null
@@ -0,0 +1,436 @@
+/*
+ * Copyright (c) 2017 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 <stdint.h>
+#include <string.h>
+#include <assert.h>
+#include <uuid/uuid.h>
+#include <mach/mach.h>
+#include <sys/stat.h> 
+#include <fcntl.h>
+#include <sys/errno.h>
+#include <unistd.h>
+
+#include "PathOverrides.h"
+
+
+
+namespace dyld3 {
+
+#if BUILDING_LIBDYLD
+PathOverrides   gPathOverrides;
+#endif
+
+
+// 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;
+}
+
+
+#if DYLD_IN_PROCESS
+void PathOverrides::setEnvVars(const char* envp[])
+{
+    for (const char** p = envp; *p != NULL; p++) {
+        addEnvVar(*p);
+    }
+}
+
+#else
+PathOverrides::PathOverrides(const std::vector<std::string>& env)
+{
+    for (const std::string& envVar : env) {
+        addEnvVar(envVar.c_str());
+    }
+}
+#endif
+
+#if !BUILDING_LIBDYLD
+// libdyld is never unloaded
+PathOverrides::~PathOverrides()
+{
+    freeArray(_dylibPathOverrides);
+    freeArray(_frameworkPathOverrides);
+    freeArray(_frameworkPathFallbacks);
+    freeArray(_dylibPathFallbacks);
+}
+#endif
+
+
+void PathOverrides::handleEnvVar(const char* key, const char* value, void (^handler)(const char* envVar)) const
+{
+    if ( value == nullptr )
+        return;
+    size_t allocSize = strlen(key) + strlen(value) + 2;
+    char buffer[allocSize];
+    strlcpy(buffer, key, allocSize);
+    strlcat(buffer, "=", allocSize);
+    strlcat(buffer, value, allocSize);
+    handler(buffer);
+}
+
+void PathOverrides::handleListEnvVar(const char* key, const char** list, void (^handler)(const char* envVar)) const
+{
+    if ( list == nullptr )
+        return;
+    size_t allocSize = strlen(key) + 2;
+    for (const char** lp=list; *lp != nullptr; ++lp)
+        allocSize += strlen(*lp)+1;
+    char buffer[allocSize];
+    strlcpy(buffer, key, allocSize);
+    strlcat(buffer, "=", allocSize);
+    bool needColon = false;
+    for (const char** lp=list; *lp != nullptr; ++lp) {
+        if ( needColon )
+            strlcat(buffer, ":", allocSize);
+        strlcat(buffer, *lp, allocSize);
+        needColon = true;
+    }
+    handler(buffer);
+}
+
+void PathOverrides::forEachEnvVar(void (^handler)(const char* envVar)) const
+{
+    handleListEnvVar("DYLD_LIBRARY_PATH",            _dylibPathOverrides,      handler);
+    handleListEnvVar("DYLD_FRAMEWORK_PATH",          _frameworkPathOverrides,  handler);
+    handleListEnvVar("DYLD_FALLBACK_FRAMEWORK_PATH", _frameworkPathFallbacks,  handler);
+    handleListEnvVar("DYLD_FALLBACK_LIBRARY_PATH",   _dylibPathFallbacks,      handler);
+    handleListEnvVar("DYLD_INSERT_LIBRARIES",        _insertedDylibs,          handler);
+    handleEnvVar(    "DYLD_IMAGE_SUFFIX",            _imageSuffix,             handler);
+    handleEnvVar(    "DYLD_ROOT_PATH",               _rootPath,                handler);
+}
+
+uint32_t PathOverrides::envVarCount() const
+{
+    uint32_t count = 0;
+    if ( _dylibPathOverrides != nullptr )
+        ++count;
+    if ( _frameworkPathOverrides != nullptr )
+        ++count;
+    if ( _frameworkPathFallbacks != nullptr )
+        ++count;
+    if ( _dylibPathFallbacks != nullptr )
+        ++count;
+    if ( _insertedDylibs != nullptr )
+        ++count;
+    if ( _imageSuffix != nullptr )
+        ++count;
+    if ( _rootPath != nullptr )
+        ++count;
+    return count;
+}
+
+void PathOverrides::forEachInsertedDylib(void (^handler)(const char* dylibPath)) const
+{
+    if ( _insertedDylibs == nullptr )
+        return;
+    for (const char** lp=_insertedDylibs; *lp != nullptr; ++lp)
+        handler(*lp);
+}
+
+void PathOverrides::addEnvVar(const char* keyEqualsValue)
+{
+    const char* equals = strchr(keyEqualsValue, '=');
+    if ( equals != NULL ) {
+        const char* value = &equals[1];
+        const size_t keyLen = equals-keyEqualsValue;
+        char key[keyLen+1];
+        strncpy(key, keyEqualsValue, keyLen);
+        key[keyLen] = '\0';
+        if ( strcmp(key, "DYLD_LIBRARY_PATH") == 0 ) {
+            _dylibPathOverrides = parseColonListIntoArray(value);
+        }
+        else if ( strcmp(key, "DYLD_FRAMEWORK_PATH") == 0 ) {
+            _frameworkPathOverrides = parseColonListIntoArray(value);
+        }
+        else if ( strcmp(key, "DYLD_FALLBACK_FRAMEWORK_PATH") == 0 ) {
+            _frameworkPathFallbacks = parseColonListIntoArray(value);
+        }
+        else if ( strcmp(key, "DYLD_FALLBACK_LIBRARY_PATH") == 0 ) {
+            _dylibPathFallbacks = parseColonListIntoArray(value);
+        }
+        else if ( strcmp(key, "DYLD_INSERT_LIBRARIES") == 0 ) {
+            _insertedDylibs = parseColonListIntoArray(value);
+        }
+        else if ( strcmp(key, "DYLD_IMAGE_SUFFIX") == 0 ) {
+            _imageSuffix = value;
+        }
+        else if ( strcmp(key, "DYLD_ROOT_PATH") == 0 ) {
+            _rootPath = value;
+        }
+    }
+}
+
+void PathOverrides::forEachInColonList(const char* list, void (^handler)(const char* path))
+{
+    char buffer[strlen(list)+1];
+    const char* t = list;
+    for (const char* s=list; *s != '\0'; ++s) {
+        if (*s != ':')
+            continue;
+        size_t len = s - t;
+        memcpy(buffer, t, len);
+        buffer[len] = '\0';
+        handler(buffer);
+        t = s+1;
+    }
+    handler(t);
+}
+
+const char** PathOverrides::parseColonListIntoArray(const char* list)
+{
+    __block int count = 1;
+    forEachInColonList(list, ^(const char* path) {
+        ++count;
+    });
+    const char** array = (const char**)malloc(count*sizeof(char*));
+    __block const char** p = array;
+    forEachInColonList(list, ^(const char* path) {
+        *p++ = strdup(path);
+    });
+    *p = nullptr;
+    return array;
+}
+
+void PathOverrides::freeArray(const char** array)
+{
+    if ( array == nullptr )
+        return;
+
+    for (const char** p=array; *p != nullptr; ++p) {
+        free((void*)*p);
+    }
+    free(array);
+}
+
+void PathOverrides::forEachDylibFallback(Platform platform, void (^handler)(const char* fallbackDir, bool& stop)) const
+{
+    bool stop = false;
+    if ( _dylibPathFallbacks != nullptr ) {
+        for (const char** fp=_dylibPathFallbacks; *fp != nullptr; ++fp) {
+            handler(*fp, stop);
+            if ( stop )
+                return;
+        }
+    }
+    else {
+        switch ( platform ) {
+            case Platform::macOS:
+                // "$HOME/lib"
+                handler("/usr/local/lib", stop);  // FIXME: not for restricted processes
+                if ( !stop )
+                    handler("/usr/lib", stop);
+                break;
+            case Platform::iOS:
+            case Platform::watchOS:
+            case Platform::tvOS:
+            case Platform::bridgeOS:
+            case Platform::unknown:
+                handler("/usr/local/lib", stop);
+                if ( !stop )
+                    handler("/usr/lib", stop);
+                break;
+        }
+    }
+}
+
+void PathOverrides::forEachFrameworkFallback(Platform platform, void (^handler)(const char* fallbackDir, bool& stop)) const
+{
+    bool stop = false;
+    if ( _frameworkPathFallbacks != nullptr ) {
+        for (const char** fp=_frameworkPathFallbacks; *fp != nullptr; ++fp) {
+            handler(*fp, stop);
+            if ( stop )
+                return;
+        }
+    }
+    else {
+        switch ( platform ) {
+            case Platform::macOS:
+                // "$HOME/Library/Frameworks"
+                handler("/Library/Frameworks", stop);   // FIXME: not for restricted processes
+                // "/Network/Library/Frameworks"
+                if ( !stop )
+                    handler("/System/Library/Frameworks", stop);
+                break;
+            case Platform::iOS:
+            case Platform::watchOS:
+            case Platform::tvOS:
+            case Platform::bridgeOS:
+            case Platform::unknown:
+                handler("/System/Library/Frameworks", stop);
+                break;
+        }
+    }
+}
+
+void PathOverrides::forEachPathVariant(const char* initialPath,
+#if !DYLD_IN_PROCESS
+                                       Platform platform,
+#endif
+                                       void (^handler)(const char* possiblePath, bool& stop)) const
+{
+#if DYLD_IN_PROCESS
+    Platform platform = MachOParser::currentPlatform();
+#endif
+    __block bool stop = false;
+
+    // check for overrides
+    const char* frameworkPartialPath = getFrameworkPartialPath(initialPath);
+    if ( frameworkPartialPath != nullptr ) {
+        const size_t frameworkPartialPathLen = strlen(frameworkPartialPath);
+        // look at each DYLD_FRAMEWORK_PATH directory
+        if ( _frameworkPathOverrides != nullptr ) {
+            for (const char** fp=_frameworkPathOverrides; *fp != nullptr; ++fp) {
+                char npath[strlen(*fp)+frameworkPartialPathLen+8];
+                strcpy(npath, *fp);
+                strcat(npath, "/");
+                strcat(npath, frameworkPartialPath);
+                handler(npath, stop);
+                if ( stop )
+                    return;
+            }
+        }
+    }
+    else {
+        const char* libraryLeafName = getLibraryLeafName(initialPath);
+        const size_t libraryLeafNameLen = strlen(libraryLeafName);
+        // look at each DYLD_LIBRARY_PATH directory
+        if ( _dylibPathOverrides != nullptr ) {
+            for (const char** lp=_dylibPathOverrides; *lp != nullptr; ++lp) {
+                char libpath[strlen(*lp)+libraryLeafNameLen+8];
+                strcpy(libpath, *lp);
+                strcat(libpath, "/");
+                strcat(libpath, libraryLeafName);
+                handler(libpath, stop);
+                if ( stop )
+                    return;
+            }
+        }
+    }
+
+    // try original path
+    handler(initialPath, stop);
+    if ( stop )
+        return;
+
+    // check fallback paths
+    if ( frameworkPartialPath != nullptr ) {
+        const size_t frameworkPartialPathLen = strlen(frameworkPartialPath);
+        // look at each DYLD_FALLBACK_FRAMEWORK_PATH directory
+        forEachFrameworkFallback(platform, ^(const char* dir, bool& innerStop) {
+            char npath[strlen(dir)+frameworkPartialPathLen+8];
+            strcpy(npath, dir);
+            strcat(npath, "/");
+            strcat(npath, frameworkPartialPath);
+            handler(npath, innerStop);
+            if ( innerStop )
+                stop = innerStop;
+        });
+
+    }
+   else {
+        const char* libraryLeafName = getLibraryLeafName(initialPath);
+        const size_t libraryLeafNameLen = strlen(libraryLeafName);
+        // look at each DYLD_FALLBACK_LIBRARY_PATH directory
+        forEachDylibFallback(platform, ^(const char* dir, bool& innerStop) {
+            char libpath[strlen(dir)+libraryLeafNameLen+8];
+            strcpy(libpath, dir);
+            strcat(libpath, "/");
+            strcat(libpath, libraryLeafName);
+            handler(libpath, innerStop);
+            if ( innerStop )
+                stop = innerStop;
+        });
+    }
+}
+
+
+//
+// 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 nullptr if not a framework path
+//
+const char* PathOverrides::getFrameworkPartialPath(const char* path) const
+{
+    const char* dirDot = strrstr(path, ".framework/");
+    if ( dirDot != nullptr ) {
+        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 != nullptr ) {
+                    if ( strcmp(framework, &leaf[1]) == 0 ) {
+                        return frameworkStart;
+                    }
+                    if (  _imageSuffix != nullptr ) {
+                        // some debug frameworks have install names that end in _debug
+                        if ( strncmp(framework, &leaf[1], len) == 0 ) {
+                            if ( strcmp( _imageSuffix, &leaf[len+1]) == 0 )
+                                return frameworkStart;
+                        }
+                    }
+                }
+            }
+        }
+    }
+    return nullptr;
+}
+
+
+const char* PathOverrides::getLibraryLeafName(const char* path)
+{
+    const char* start = strrchr(path, '/');
+    if ( start != nullptr )
+        return &start[1];
+    else
+        return path;
+}
+
+} // namespace dyld3
+
+
+
+
+
diff --git a/dyld3/PathOverrides.h b/dyld3/PathOverrides.h
new file mode 100644 (file)
index 0000000..4ae7407
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2017 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_PATH_OVERRIDES_H__
+#define __DYLD_PATH_OVERRIDES_H__
+
+#include <stdint.h>
+
+#if !DYLD_IN_PROCESS
+#include <vector>
+#include <string>
+#endif
+
+#include "Logging.h"
+#include "MachOParser.h"
+
+
+namespace dyld3 {
+
+class VIS_HIDDEN PathOverrides
+{
+public:
+#if !BUILDING_LIBDYLD
+    // libdyld is never unloaded
+                                    ~PathOverrides();
+#endif
+
+#if DYLD_IN_PROCESS
+    void                            setEnvVars(const char* envp[]);
+    void                            forEachPathVariant(const char* initialPath, void (^handler)(const char* possiblePath, bool& stop)) const;
+#else
+                                    PathOverrides(const std::vector<std::string>& env);
+    void                            forEachPathVariant(const char* initialPath, Platform platform, void (^handler)(const char* possiblePath, bool& stop)) const;
+#endif
+
+    uint32_t                        envVarCount() const;
+    void                            forEachEnvVar(void (^handler)(const char* envVar)) const;
+    void                            forEachInsertedDylib(void (^handler)(const char* dylibPath)) const;
+
+private:
+    void                            forEachInColonList(const char* list, void (^callback)(const char* path));
+    const char**                    parseColonListIntoArray(const char* list);
+    void                            freeArray(const char** array);
+    void                            addEnvVar(const char* keyEqualsValue);
+    const char*                     getFrameworkPartialPath(const char* path) const;
+    static const char*              getLibraryLeafName(const char* path);
+    void                            handleListEnvVar(const char* key, const char** list, void (^handler)(const char* envVar)) const;
+    void                            handleEnvVar(const char* key, const char* value, void (^handler)(const char* envVar)) const;
+    void                            forEachDylibFallback(Platform platform, void (^handler)(const char* fallbackDir, bool& stop)) const;
+    void                            forEachFrameworkFallback(Platform platform, void (^handler)(const char* fallbackDir, bool& stop)) const;
+
+    const char**                    _dylibPathOverrides         = nullptr;
+    const char**                    _frameworkPathOverrides     = nullptr;
+    const char**                    _dylibPathFallbacks         = nullptr;
+    const char**                    _frameworkPathFallbacks     = nullptr;
+    const char**                    _insertedDylibs             = nullptr;
+    const char*                     _imageSuffix                = nullptr;
+    const char*                     _rootPath                   = nullptr;  // simulator only
+};
+
+#if BUILDING_LIBDYLD
+extern PathOverrides   gPathOverrides;
+#endif
+
+
+} // namespace dyld3
+
+#endif // __DYLD_PATH_OVERRIDES_H__
+
+
diff --git a/dyld3/SharedCacheRuntime.cpp b/dyld3/SharedCacheRuntime.cpp
new file mode 100644 (file)
index 0000000..d1c821b
--- /dev/null
@@ -0,0 +1,645 @@
+/*
+ * Copyright (c) 2017 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 <stdint.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/syscall.h>
+#include <sys/syslog.h>
+#include <sys/sysctl.h>
+#include <sys/mman.h>
+#include <mach/mach.h>
+#include <mach-o/fat.h>
+#include <mach-o/loader.h>
+#include <mach-o/ldsyms.h>
+#include <mach/shared_region.h>
+#include <mach/mach.h>
+#include <Availability.h>
+#include <TargetConditionals.h>
+
+#include "dyld_cache_format.h"
+#include "SharedCacheRuntime.h"
+#include "LaunchCache.h"
+#include "LaunchCacheFormat.h"
+#include "Loading.h"
+
+#define ENABLE_DYLIBS_TO_OVERRIDE_CACHE_SIZE 1024
+
+// should be in mach/shared_region.h
+extern "C" int __shared_region_check_np(uint64_t* startaddress);
+extern "C" int __shared_region_map_and_slide_np(int fd, uint32_t count, const shared_file_mapping_np mappings[], long slide, const dyld_cache_slide_info2* slideInfo, size_t slideInfoSize);
+
+
+namespace dyld {
+    extern int  my_stat(const char* path, struct stat* buf);
+    extern int  my_open(const char* path, int flag, int other);
+    extern void log(const char*, ...);
+}
+
+
+namespace dyld3 {
+
+
+struct CacheInfo
+{
+    int                     fd;
+    shared_file_mapping_np  mappings[3];
+    uint64_t                slideInfoAddressUnslid;
+    size_t                  slideInfoSize;
+    uint64_t                cachedDylibsGroupUnslid;
+    uint64_t                sharedRegionStart;
+    uint64_t                sharedRegionSize;
+    uint64_t                maxSlide;
+};
+
+
+
+
+#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_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 __arm64e__
+    #define ARCH_NAME            "arm64e"
+    #define ARCH_CACHE_MAGIC     "dyld_v1  arm64e"
+#elif __arm64__
+    #define ARCH_NAME            "arm64"
+    #define ARCH_CACHE_MAGIC     "dyld_v1   arm64"
+#endif
+
+
+
+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 getCachePath(const SharedCacheOptions& options, size_t pathBufferSize, char pathBuffer[])
+{
+    // set cache dir
+    if ( options.cacheDirOverride != nullptr ) {
+        strlcpy(pathBuffer, options.cacheDirOverride, pathBufferSize);
+    }
+    else {
+#if __IPHONE_OS_VERSION_MIN_REQUIRED
+        strlcpy(pathBuffer, IPHONE_DYLD_SHARED_CACHE_DIR, sizeof(IPHONE_DYLD_SHARED_CACHE_DIR));
+#else
+        strlcpy(pathBuffer, MACOSX_DYLD_SHARED_CACHE_DIR, sizeof(MACOSX_DYLD_SHARED_CACHE_DIR));
+#endif
+    }
+
+    // append file component of cache file
+    if ( pathBuffer[strlen(pathBuffer)-1] != '/' )
+        strlcat(pathBuffer, "/", pathBufferSize);
+#if __x86_64__ && !__IPHONE_OS_VERSION_MIN_REQUIRED
+    if ( options.useHaswell ) {
+        size_t len = strlen(pathBuffer);
+        struct stat haswellStatBuf;
+        strlcat(pathBuffer, DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME_H, pathBufferSize);
+        if ( dyld::my_stat(pathBuffer, &haswellStatBuf) == 0 )
+            return;
+        // no haswell cache file, use regular x86_64 cache
+        pathBuffer[len] = '\0';
+    }
+#endif
+    strlcat(pathBuffer, DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME, pathBufferSize);
+
+#if __IPHONE_OS_VERSION_MIN_REQUIRED && !TARGET_IPHONE_SIMULATOR
+    // use .development cache if it exists
+    struct stat enableStatBuf;
+    struct stat devCacheStatBuf;
+    struct stat optCacheStatBuf;
+    bool enableFileExists = (dyld::my_stat(IPHONE_DYLD_SHARED_CACHE_DIR "enable-dylibs-to-override-cache", &enableStatBuf) == 0);
+    bool devCacheExists = (dyld::my_stat(IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME DYLD_SHARED_CACHE_DEVELOPMENT_EXT, &devCacheStatBuf) == 0);
+    bool optCacheExists = (dyld::my_stat(IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME, &optCacheStatBuf) == 0);
+    if ( (enableFileExists && (enableStatBuf.st_size < ENABLE_DYLIBS_TO_OVERRIDE_CACHE_SIZE) && devCacheExists) || !optCacheExists )
+        strlcat(pathBuffer, DYLD_SHARED_CACHE_DEVELOPMENT_EXT, pathBufferSize);
+#endif
+
+}
+
+
+int openSharedCacheFile(const SharedCacheOptions& options, SharedCacheLoadInfo* results)
+{
+    getCachePath(options, sizeof(results->path), results->path);
+    return dyld::my_open(results->path, O_RDONLY, 0);
+}
+
+static bool validMagic(const SharedCacheOptions& options, const DyldSharedCache* cache)
+{
+    if ( strcmp(cache->header.magic, ARCH_CACHE_MAGIC) == 0 )
+        return true;
+
+#if __x86_64__
+    if ( options.useHaswell ) {
+        if ( strcmp(cache->header.magic, ARCH_CACHE_MAGIC_H) == 0 )
+            return true;
+    }
+#endif
+    return false;
+}
+
+
+static bool validPlatform(const SharedCacheOptions& options, const DyldSharedCache* cache)
+{
+    // grandfather in old cache that does not have platform in header
+    if ( cache->header.mappingOffset < 0xE0 )
+        return true;
+
+    if ( cache->header.platform != (uint32_t)MachOParser::currentPlatform() )
+        return false;
+
+#if TARGET_IPHONE_SIMULATOR
+    if ( cache->header.simulator == 0 )
+        return false;
+#else
+    if ( cache->header.simulator != 0 )
+        return false;
+#endif
+
+    return true;
+}
+
+
+static void verboseSharedCacheMappings(const shared_file_mapping_np mappings[3])
+{
+    for (int i=0; i < 3; ++i) {
+        dyld::log("        0x%08llX->0x%08llX init=%x, max=%x %s%s%s\n",
+            mappings[i].sfm_address, mappings[i].sfm_address+mappings[i].sfm_size-1,
+            mappings[i].sfm_init_prot, mappings[i].sfm_init_prot,
+            ((mappings[i].sfm_init_prot & VM_PROT_READ) ? "read " : ""),
+            ((mappings[i].sfm_init_prot & VM_PROT_WRITE) ? "write " : ""),
+            ((mappings[i].sfm_init_prot & VM_PROT_EXECUTE) ? "execute " : ""));
+    }
+}
+
+static bool preflightCacheFile(const SharedCacheOptions& options, SharedCacheLoadInfo* results, CacheInfo* info)
+{
+    // find and open shared cache file
+    int fd = openSharedCacheFile(options, results);
+    if ( fd == -1 ) {
+        results->errorMessage = "shared cache file cannot be opened";
+        return false;
+    }
+    struct stat cacheStatBuf;
+    if ( dyld::my_stat(results->path, &cacheStatBuf) != 0 ) {
+        results->errorMessage = "shared cache file cannot be stat()ed";
+        ::close(fd);
+        return false;
+    }
+    size_t cacheFileLength = (size_t)(cacheStatBuf.st_size);
+
+    // sanity check header and mappings
+    uint8_t firstPage[0x4000];
+    if ( ::pread(fd, firstPage, sizeof(firstPage), 0) != sizeof(firstPage) ) {
+        results->errorMessage = "shared cache header could not be read";
+        ::close(fd);
+        return false;
+    }
+    const DyldSharedCache* cache = (DyldSharedCache*)firstPage;
+    if ( !validMagic(options, cache) ) {
+        results->errorMessage = "shared cache file has wrong magic";
+        ::close(fd);
+        return false;
+    }
+    if ( !validPlatform(options, cache) ) {
+        results->errorMessage = "shared cache file is for a different platform";
+        ::close(fd);
+        return false;
+    }
+    const dyld_cache_mapping_info* const fileMappings = (dyld_cache_mapping_info*)&firstPage[cache->header.mappingOffset];
+    if ( (cache->header.mappingCount != 3)
+      || (cache->header.mappingOffset > 0x120)
+      || (fileMappings[0].fileOffset != 0)
+      || ((fileMappings[0].address + fileMappings[0].size) > fileMappings[1].address)
+      || ((fileMappings[1].address + fileMappings[1].size) > fileMappings[2].address)
+      || ((fileMappings[0].fileOffset + fileMappings[0].size) != fileMappings[1].fileOffset)
+      || ((fileMappings[1].fileOffset + fileMappings[1].size) != fileMappings[2].fileOffset)
+      || ((cache->header.codeSignatureOffset + cache->header.codeSignatureSize) != cacheFileLength)
+      || (fileMappings[0].maxProt != (VM_PROT_READ|VM_PROT_EXECUTE))
+      || (fileMappings[1].maxProt != (VM_PROT_READ|VM_PROT_WRITE))
+      || (fileMappings[2].maxProt != VM_PROT_READ) ) {
+        results->errorMessage = "shared cache file mappings are invalid";
+        ::close(fd);
+        return false;
+    }
+
+    if ( cache->header.mappingOffset >= 0xF8 ) {
+        if ( (fileMappings[0].address != cache->header.sharedRegionStart) || ((fileMappings[2].address + fileMappings[2].size) > (cache->header.sharedRegionStart+cache->header.sharedRegionSize)) ) {
+            results->errorMessage = "shared cache file mapping addressses invalid";
+            ::close(fd);
+            return false;
+        }
+    }
+    else {
+        if ( (fileMappings[0].address != SHARED_REGION_BASE) || ((fileMappings[2].address + fileMappings[2].size) > (SHARED_REGION_BASE+SHARED_REGION_SIZE)) ) {
+            results->errorMessage = "shared cache file mapping addressses invalid";
+            ::close(fd);
+            return false;
+        }
+    }
+
+    // register code signature of cache file
+    fsignatures_t siginfo;
+    siginfo.fs_file_start = 0;  // cache always starts at beginning of file
+    siginfo.fs_blob_start = (void*)cache->header.codeSignatureOffset;
+    siginfo.fs_blob_size  = (size_t)(cache->header.codeSignatureSize);
+    int result = fcntl(fd, F_ADDFILESIGS_RETURN, &siginfo);
+    if ( result == -1 ) {
+        results->errorMessage = "code signature registration for shared cache failed";
+        ::close(fd);
+        return false;
+    }
+
+    // <rdar://problem/23188073> validate code signature covers entire shared cache
+    uint64_t codeSignedLength = siginfo.fs_file_start;
+    if ( codeSignedLength < cache->header.codeSignatureOffset ) {
+        results->errorMessage = "code signature does not cover entire shared cache file";
+        ::close(fd);
+        return false;
+    }
+    void* mappedData = ::mmap(NULL, sizeof(firstPage), PROT_READ|PROT_EXEC, MAP_PRIVATE, fd, 0);
+    if ( mappedData == MAP_FAILED ) {
+        results->errorMessage = "first page of shared cache not mmap()able";
+        ::close(fd);
+        return false;
+    }
+    if ( memcmp(mappedData, firstPage, sizeof(firstPage)) != 0 ) {
+        results->errorMessage = "first page of shared cache not mmap()able";
+        ::close(fd);
+        return false;
+    }
+    ::munmap(mappedData, sizeof(firstPage));
+
+    // fill out results
+    info->fd = fd;
+    for (int i=0; i < 3; ++i) {
+        info->mappings[i].sfm_address       = fileMappings[i].address;
+        info->mappings[i].sfm_size          = fileMappings[i].size;
+        info->mappings[i].sfm_file_offset   = fileMappings[i].fileOffset;
+        info->mappings[i].sfm_max_prot      = fileMappings[i].maxProt;
+        info->mappings[i].sfm_init_prot     = fileMappings[i].initProt;
+    }
+    info->mappings[1].sfm_max_prot  |= VM_PROT_SLIDE;
+    info->mappings[1].sfm_init_prot |= VM_PROT_SLIDE;
+    info->slideInfoAddressUnslid  = fileMappings[2].address + cache->header.slideInfoOffset - fileMappings[2].fileOffset;
+    info->slideInfoSize           = (long)cache->header.slideInfoSize;
+    if ( cache->header.mappingOffset > 0xD0 )
+        info->cachedDylibsGroupUnslid = cache->header.dylibsImageGroupAddr;
+    else
+        info->cachedDylibsGroupUnslid = 0;
+    if ( cache->header.mappingOffset >= 0xf8 ) {
+        info->sharedRegionStart = cache->header.sharedRegionStart;
+        info->sharedRegionSize  = cache->header.sharedRegionSize;
+        info->maxSlide          = cache->header.maxSlide;
+    }
+    else {
+        info->sharedRegionStart = SHARED_REGION_BASE;
+        info->sharedRegionSize  = SHARED_REGION_SIZE;
+        info->maxSlide          = SHARED_REGION_SIZE - (fileMappings[2].address + fileMappings[2].size - fileMappings[0].address);
+    }
+    return true;
+}
+
+#if !TARGET_IPHONE_SIMULATOR
+static bool reuseExistingCache(const SharedCacheOptions& options, SharedCacheLoadInfo* results)
+{
+    uint64_t cacheBaseAddress;
+#if __i386__
+    if ( syscall(294, &cacheBaseAddress) == 0 ) {
+#else
+    if ( __shared_region_check_np(&cacheBaseAddress) == 0 ) {
+#endif
+        const DyldSharedCache* existingCache = (DyldSharedCache*)cacheBaseAddress;
+        if ( validMagic(options, existingCache) ) {
+            const dyld_cache_mapping_info* const fileMappings = (dyld_cache_mapping_info*)(cacheBaseAddress + existingCache->header.mappingOffset);
+            results->loadAddress = existingCache;
+            results->slide = (long)(cacheBaseAddress - fileMappings[0].address);
+            if ( (existingCache->header.mappingOffset > 0xD0) && (existingCache->header.dylibsImageGroupAddr != 0) )
+                results->cachedDylibsGroup = (const launch_cache::binary_format::ImageGroup*)(existingCache->header.dylibsImageGroupAddr + results->slide);
+            else
+                results->cachedDylibsGroup = nullptr;
+            // we don't know the path this cache was previously loaded from, assume default
+            getCachePath(options, sizeof(results->path), results->path);
+            if ( options.verbose ) {
+                const shared_file_mapping_np* const mappings = (shared_file_mapping_np*)(cacheBaseAddress + existingCache->header.mappingOffset);
+                dyld::log("re-using existing shared cache (%s):\n", results->path);
+                shared_file_mapping_np slidMappings[3];
+                for (int i=0; i < 3; ++i) {
+                    slidMappings[i] = mappings[i];
+                    slidMappings[i].sfm_address += results->slide;
+                }
+                verboseSharedCacheMappings(slidMappings);
+            }
+        }
+        else {
+            results->errorMessage = "existing shared cache in memory is not compatible";
+        }
+        return true;
+    }
+    return false;
+}
+
+static long pickCacheASLR(CacheInfo& info)
+{
+    // choose new random slide
+#if __IPHONE_OS_VERSION_MIN_REQUIRED
+    // <rdar://problem/20848977> change shared cache slide for 32-bit arm to always be 16k aligned
+    long slide = ((arc4random() % info.maxSlide) & (-16384));
+#else
+    long slide = ((arc4random() % info.maxSlide) & (-4096));
+#endif
+
+    // <rdar://problem/32031197> respect -disable_aslr boot-arg
+    if ( dyld3::loader::bootArgsContains("-disable_aslr") )
+        slide = 0;
+
+    // update mappings
+    for (uint32_t i=0; i < 3; ++i) {
+        info.mappings[i].sfm_address += slide;
+    }
+    
+    return slide;
+}
+
+static bool mapCacheSystemWide(const SharedCacheOptions& options, SharedCacheLoadInfo* results)
+{
+    CacheInfo info;
+    if ( !preflightCacheFile(options, results, &info) )
+        return false;
+
+    const dyld_cache_slide_info2* slideInfo = nullptr;
+    if ( info.slideInfoSize != 0 ) {
+        results->slide = pickCacheASLR(info);
+        slideInfo = (dyld_cache_slide_info2*)(info.slideInfoAddressUnslid + results->slide);
+    }
+    if ( info.cachedDylibsGroupUnslid != 0 )
+        results->cachedDylibsGroup = (const launch_cache::binary_format::ImageGroup*)(info.cachedDylibsGroupUnslid + results->slide);
+    else
+        results->cachedDylibsGroup = nullptr;
+
+    int result = __shared_region_map_and_slide_np(info.fd, 3, info.mappings, results->slide, slideInfo, info.slideInfoSize);
+    ::close(info.fd);
+    if ( result == 0 ) {
+        results->loadAddress = (const DyldSharedCache*)(info.mappings[0].sfm_address);
+    }
+    else {
+        // could be another process beat us to it
+        if ( reuseExistingCache(options, results) )
+            return true;
+        // if cache does not exist, then really is an error
+        results->errorMessage = "syscall to map cache into shared region failed";
+        return false;
+    }
+
+    if ( options.verbose ) {
+        dyld::log("mapped dyld cache file system wide: %s\n", results->path);
+        verboseSharedCacheMappings(info.mappings);
+    }
+    return true;
+}
+#endif
+
+static bool mapCachePrivate(const SharedCacheOptions& options, SharedCacheLoadInfo* results)
+{
+    // open and validate cache file
+    CacheInfo info;
+    if ( !preflightCacheFile(options, results, &info) )
+        return false;
+
+    // compute ALSR slide
+    results->slide = 0;
+    const dyld_cache_slide_info2* slideInfo = nullptr;
+#if !TARGET_IPHONE_SIMULATOR // simulator caches do not support sliding
+    if ( info.slideInfoSize != 0 ) {
+        results->slide = pickCacheASLR(info);
+        slideInfo = (dyld_cache_slide_info2*)(info.slideInfoAddressUnslid + results->slide);
+    }
+#endif
+    results->loadAddress = (const DyldSharedCache*)(info.mappings[0].sfm_address);
+     if ( info.cachedDylibsGroupUnslid != 0 )
+        results->cachedDylibsGroup = (const launch_cache::binary_format::ImageGroup*)(info.cachedDylibsGroupUnslid + results->slide);
+    else
+        results->cachedDylibsGroup = nullptr;
+
+    // remove the shared region sub-map
+    vm_deallocate(mach_task_self(), (vm_address_t)info.sharedRegionStart, (vm_size_t)info.sharedRegionSize);
+    
+    // map cache just for this process with mmap()
+    for (int i=0; i < 3; ++i) {
+        void* mmapAddress = (void*)(uintptr_t)(info.mappings[i].sfm_address);
+        size_t size = (size_t)(info.mappings[i].sfm_size);
+        //dyld::log("dyld: mapping address %p with size 0x%08lX\n", mmapAddress, size);
+        int protection = 0;
+        if ( info.mappings[i].sfm_init_prot & VM_PROT_EXECUTE )
+            protection   |= PROT_EXEC;
+        if ( info.mappings[i].sfm_init_prot & VM_PROT_READ )
+            protection   |= PROT_READ;
+        if ( info.mappings[i].sfm_init_prot & VM_PROT_WRITE )
+            protection   |= PROT_WRITE;
+        off_t offset = info.mappings[i].sfm_file_offset;
+        if ( ::mmap(mmapAddress, size, protection, MAP_FIXED | MAP_PRIVATE, info.fd, offset) != mmapAddress ) {
+            // failed to map some chunk of this shared cache file
+            // clear shared region
+            vm_deallocate(mach_task_self(), (vm_address_t)info.sharedRegionStart, (vm_size_t)info.sharedRegionSize);
+            // return failure
+            results->loadAddress        = nullptr;
+            results->cachedDylibsGroup  = nullptr;
+            results->errorMessage       = "could not mmap() part of dyld cache";
+            return false;
+        }
+    }
+
+    // update all __DATA pages with slide info
+    const dyld_cache_slide_info* slideInfoHeader = (dyld_cache_slide_info*)slideInfo;
+    if ( slideInfoHeader != nullptr ) {
+        if ( slideInfoHeader->version != 2 ) {
+            results->errorMessage = "invalide slide info in cache file";
+            return false;
+        }
+        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 = (uintptr_t)info.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 pInfo = page_extras[chainIndex];
+                    uint16_t pageStartOffset = (pInfo & 0x3FFF)*4;
+                    //dyld::log("     chain[%d] pageOffset=0x%03X\n", chainIndex, pageStartOffset);
+                    rebaseChain(page, pageStartOffset, results->slide, slideInfo);
+                    done = (pInfo & DYLD_CACHE_SLIDE_PAGE_ATTR_END);
+                    ++chainIndex;
+                }
+            }
+            else {
+                uint32_t pageOffset = pageEntry * 4;
+                //dyld::log("     start pageOffset=0x%03X\n", pageOffset);
+                rebaseChain(page, pageOffset, results->slide, slideInfo);
+            }
+        }
+    }
+
+    if ( options.verbose ) {
+        dyld::log("mapped dyld cache file private to process (%s):\n", results->path);
+        verboseSharedCacheMappings(info.mappings);
+    }
+    return true;
+}
+
+
+
+bool loadDyldCache(const SharedCacheOptions& options, SharedCacheLoadInfo* results)
+{
+    results->loadAddress        = 0;
+    results->slide              = 0;
+    results->cachedDylibsGroup  = nullptr;
+    results->errorMessage       = nullptr;
+
+#if TARGET_IPHONE_SIMULATOR
+    // simulator only supports mmap()ing cache privately into process
+    return mapCachePrivate(options, results);
+#else
+    if ( options.forcePrivate ) {
+        // mmap cache into this process only
+        return mapCachePrivate(options, results);
+    }
+    else {
+        // fast path: when cache is already mapped into shared region
+        if ( reuseExistingCache(options, results) )
+            return (results->errorMessage != nullptr);
+
+        // slow path: this is first process to load cache
+        return mapCacheSystemWide(options, results);
+    }
+#endif
+}
+
+
+bool findInSharedCacheImage(const SharedCacheLoadInfo& loadInfo, const char* dylibPathToFind, SharedCacheFindDylibResults* results)
+{
+    if ( loadInfo.loadAddress == nullptr )
+        return false;
+
+    // HACK: temp support for old caches
+    if ( (loadInfo.cachedDylibsGroup == nullptr) || (loadInfo.loadAddress->header.formatVersion != launch_cache::binary_format::kFormatVersion) ) {
+        const dyld_cache_image_info* const start = (dyld_cache_image_info*)((uint8_t*)loadInfo.loadAddress + loadInfo.loadAddress->header.imagesOffset);
+        const dyld_cache_image_info* const end = &start[loadInfo.loadAddress->header.imagesCount];
+        for (const dyld_cache_image_info* p = start; p != end; ++p) {
+            const char* aPath = (char*)loadInfo.loadAddress + p->pathFileOffset;
+            if ( strcmp(aPath, dylibPathToFind) == 0 ) {
+                results->mhInCache    = (const mach_header*)(p->address+loadInfo.slide);
+                results->pathInCache  = aPath;
+                results->slideInCache = loadInfo.slide;
+                results->imageData    = nullptr;
+                return true;
+            }
+        }
+        return false;
+    }
+    // HACK: end
+
+    launch_cache::ImageGroup dylibsGroup(loadInfo.cachedDylibsGroup);
+    uint32_t foundIndex;
+    const launch_cache::binary_format::Image* imageData = dylibsGroup.findImageByPath(dylibPathToFind, foundIndex);
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
+    // <rdar://problem/32740215> handle symlink to cached dylib
+    if ( imageData == nullptr ) {
+        char resolvedPath[PATH_MAX];
+        if ( realpath(dylibPathToFind, resolvedPath) != nullptr )
+            imageData = dylibsGroup.findImageByPath(resolvedPath, foundIndex);
+    }
+#endif
+    if ( imageData == nullptr )
+        return false;
+
+    launch_cache::Image image(imageData);
+    results->mhInCache    = (const mach_header*)((uintptr_t)loadInfo.loadAddress + image.cacheOffset());
+    results->pathInCache  = image.path();
+    results->slideInCache = loadInfo.slide;
+    results->imageData    = imageData;
+    return true;
+}
+
+
+bool pathIsInSharedCacheImage(const SharedCacheLoadInfo& loadInfo, const char* dylibPathToFind)
+{
+    if ( (loadInfo.loadAddress == nullptr) || (loadInfo.cachedDylibsGroup == nullptr) || (loadInfo.loadAddress->header.formatVersion != launch_cache::binary_format::kFormatVersion) )
+        return false;
+
+    launch_cache::ImageGroup dylibsGroup(loadInfo.cachedDylibsGroup);
+    uint32_t foundIndex;
+    const launch_cache::binary_format::Image* imageData = dylibsGroup.findImageByPath(dylibPathToFind, foundIndex);
+    return (imageData != nullptr);
+}
+
+
+} // namespace dyld3
+
diff --git a/dyld3/SharedCacheRuntime.h b/dyld3/SharedCacheRuntime.h
new file mode 100644 (file)
index 0000000..dbd4595
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2017 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_RUNTIME_H__
+#define __DYLD_SHARED_CACHE_RUNTIME_H__
+
+#include <string.h>
+#include <stdint.h>
+
+#include "DyldSharedCache.h"
+
+namespace dyld3 {
+
+struct SharedCacheOptions {
+    const char*     cacheDirOverride;
+    bool            forcePrivate;
+    bool            useHaswell;
+    bool            verbose;
+};
+
+struct SharedCacheLoadInfo {
+    const DyldSharedCache*                          loadAddress;
+    long                                            slide;
+    const launch_cache::binary_format::ImageGroup*  cachedDylibsGroup;
+    const char*                                     errorMessage;
+    char                                            path[256];
+};
+
+bool loadDyldCache(const SharedCacheOptions& options, SharedCacheLoadInfo* results);
+
+struct SharedCacheFindDylibResults {
+    const mach_header*                          mhInCache;
+    const char*                                 pathInCache;
+    long                                        slideInCache;
+    const launch_cache::binary_format::Image*   imageData;
+};
+
+
+bool findInSharedCacheImage(const SharedCacheLoadInfo& loadInfo, const char* dylibPathToFind, SharedCacheFindDylibResults* results);
+
+bool pathIsInSharedCacheImage(const SharedCacheLoadInfo& loadInfo, const char* dylibPathToFind);
+
+
+} // namespace dyld3
+
+#endif // __DYLD_SHARED_CACHE_RUNTIME_H__
+
+
diff --git a/dyld3/Tracing.cpp b/dyld3/Tracing.cpp
new file mode 100644 (file)
index 0000000..ff15311
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2017 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 <atomic>
+
+#include <assert.h>
+#include <mach/mach.h>
+
+#include "Tracing.h"
+
+namespace {
+VIS_HIDDEN
+static uint64_t elapsed(const time_value_t start, const time_value_t end) {
+    uint64_t duration;
+    duration = 1000000*(end.seconds - start.seconds);
+    duration += (end.microseconds - start.microseconds);
+    return duration;
+}
+}
+
+namespace dyld3 {
+
+VIS_HIDDEN
+void kdebug_trace_dyld_image(const uint32_t code,
+                       const uuid_t* uuid_bytes,
+                       const fsobj_id_t fsobjid,
+                       const fsid_t fsid,
+                       const mach_header* load_addr)
+{
+#if __LP64__
+    uint64_t *uuid = (uint64_t *)uuid_bytes[0];
+    kdebug_trace(KDBG_CODE(DBG_DYLD, DBG_DYLD_UUID, code), uuid[0],
+                 uuid[1], (uint64_t)load_addr,
+                 (uint64_t)fsid.val[0] | ((uint64_t)fsid.val[1] << 32));
+    kdebug_trace(KDBG_CODE(DBG_DYLD, DBG_DYLD_UUID, code + 1),
+                 (uint64_t)fsobjid.fid_objno |
+                 ((uint64_t)fsobjid.fid_generation << 32),
+                 0, 0, 0);
+#else /* __LP64__ */
+    uint32_t *uuid = (uint32_t *)uuid_bytes[0];
+    kdebug_trace(KDBG_CODE(DBG_DYLD, DBG_DYLD_UUID, code + 2), uuid[0],
+                 uuid[1], uuid[2], uuid[3]);
+    kdebug_trace(KDBG_CODE(DBG_DYLD, DBG_DYLD_UUID, code + 3),
+                 (uint32_t)load_addr, fsid.val[0], fsid.val[1],
+                 fsobjid.fid_objno);
+    kdebug_trace(KDBG_CODE(DBG_DYLD, DBG_DYLD_UUID, code + 4),
+                 fsobjid.fid_generation, 0, 0, 0);
+#endif /* __LP64__ */
+}
+
+VIS_HIDDEN
+void kdebug_trace_dyld_signpost(const uint32_t code, uint64_t data1, uint64_t data2) {
+    if (kdebug_is_enabled(KDBG_CODE(DBG_DYLD, DBG_DYLD_SIGNPOST, code))) {
+        task_thread_times_info info;
+        mach_msg_type_number_t infoSize = sizeof(task_thread_times_info);
+        (void)task_info(mach_task_self(), TASK_THREAD_TIMES_INFO, (task_info_t)&info, &infoSize);
+        uint64_t user_duration = elapsed({0,0}, info.user_time);
+        uint64_t system_duration = elapsed({0,0}, info.system_time);
+        kdebug_trace(KDBG_CODE(DBG_DYLD, DBG_DYLD_SIGNPOST, code), user_duration, system_duration, data1, data2);
+    }
+}
+
+static std::atomic<uint64_t> trace_pair_id(0);
+
+VIS_HIDDEN
+void kdebug_trace_dyld_duration(const uint32_t code, uint64_t data1, uint64_t data2, void (^block)()) {
+    //FIXME: We should assert here, but it is verified on our current platforms
+    //Re-enabled when we move to C++17 and can use constexpr is_lock_always_free()
+    //assert(std::atomic<uint64_t>{}.is_lock_free());
+    if (kdebug_is_enabled(KDBG_CODE(DBG_DYLD, DBG_DYLD_TIMING, code))) {
+        uint64_t current_trace_id = trace_pair_id++;
+        kdebug_trace(KDBG_CODE(DBG_DYLD, DBG_DYLD_TIMING, code) | DBG_FUNC_START, current_trace_id, 0, data1, data2);
+        block();
+        kdebug_trace(KDBG_CODE(DBG_DYLD, DBG_DYLD_TIMING, code) | DBG_FUNC_END, current_trace_id, 0, data1, data2);
+    } else {
+        block();
+    }
+}
+
+void kdebug_trace_print(const uint32_t code, const char *string) {
+    if (kdebug_is_enabled(KDBG_CODE(DBG_DYLD, DBG_DYLD_PRINT, code))) {
+        kdebug_trace_string(KDBG_CODE(DBG_DYLD, DBG_DYLD_PRINT, code), 0, string);
+    }
+}
+};
diff --git a/dyld3/Tracing.h b/dyld3/Tracing.h
new file mode 100644 (file)
index 0000000..b127aa9
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2017 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 Tracing_h
+#define Tracing_h
+
+#include <stdio.h>
+#include <stdint.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <uuid/uuid.h>
+#include <mach-o/loader.h>
+#include <System/sys/kdebug.h>
+
+#ifndef DBG_DYLD_SIGNPOST
+    #define DBG_DYLD_SIGNPOST (6)
+#endif
+
+#ifndef DBG_DYLD_TIMING
+    #define DBG_DYLD_TIMING (7)
+#endif
+
+#ifndef DBG_DYLD_PRINT
+    #define DBG_DYLD_PRINT (8)
+#endif
+
+#ifndef DBG_DYLD_SIGNPOST_START_DYLD
+    #define DBG_DYLD_SIGNPOST_START_DYLD (0)
+#endif
+
+#ifndef DBG_DYLD_SIGNPOST_START_MAIN
+    #define DBG_DYLD_SIGNPOST_START_MAIN (1)
+#endif
+
+#ifndef DBG_DYLD_SIGNPOST_START_MAIN_DYLD2
+    #define DBG_DYLD_SIGNPOST_START_MAIN_DYLD2 (2)
+#endif
+
+#ifndef DBG_DYLD_TIMING_STATIC_INITIALIZER
+    #define DBG_DYLD_TIMING_STATIC_INITIALIZER (0)
+#endif
+
+#ifndef DBG_DYLD_PRINT_GENERIC
+    #define DBG_DYLD_PRINT_GENERIC (0)
+#endif
+
+
+#define VIS_HIDDEN __attribute__((visibility("hidden")))
+
+namespace dyld3 {
+
+VIS_HIDDEN
+void kdebug_trace_dyld_image(const uint32_t code,
+                       const uuid_t* uuid_bytes,
+                       const fsobj_id_t fsobjid,
+                       const fsid_t fsid,
+                       const mach_header* load_addr);
+
+VIS_HIDDEN
+void kdebug_trace_dyld_signpost(const uint32_t code, uint64_t data1, uint64_t data2);
+
+VIS_HIDDEN
+void kdebug_trace_dyld_duration(const uint32_t code, uint64_t data1, uint64_t data2, void (^block)());
+
+VIS_HIDDEN
+void kdebug_trace_print(const uint32_t code, const char *string);
+}
+
+#endif /* Tracing_h */
diff --git a/dyld3/closured/closured.cpp b/dyld3/closured/closured.cpp
new file mode 100644 (file)
index 0000000..1e239e8
--- /dev/null
@@ -0,0 +1,357 @@
+/*
+ * Copyright (c) 2017 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 <stdio.h>
+#include <string.h>
+#include <sandbox/private.h>
+#include <bootstrap.h>
+#include <mach/mach.h>
+#include <os/log.h>
+#include <sys/mman.h>
+#include <sys/errno.h>
+#include <dispatch/dispatch.h>
+#include <dispatch/private.h>
+#include <bootstrap_priv.h>      // for bootstrap_check_in()
+#include <CrashReporterClient.h>
+#include <libproc.h>
+#include <uuid/uuid.h>
+
+#include <string>
+#include <vector>
+#include <unordered_map>
+
+#include "dyld_priv.h"
+#include "ImageProxy.h"
+#include "DyldSharedCache.h"
+#include "FileUtils.h"
+
+extern "C" {
+    #include "closuredProtocolServer.h"
+}
+
+
+static os_log_t sLog = os_log_create("com.apple.dyld.closured", "closured");
+
+static char sCrashMessageBuffer[1024];
+
+
+kern_return_t
+do_CreateClosure(
+    mach_port_t             port,
+    task_t                  requestor,
+    vm_address_t            buffer,
+    mach_msg_type_number_t  bufferCnt,
+    vm_address_t*           returnData,
+    mach_msg_type_number_t* returnDataCnt)
+{
+    dyld3::ClosureBuffer clsBuff((void*)buffer, bufferCnt);
+    const char* imagePath = clsBuff.targetPath();
+    os_log(sLog, "request to build closure for %s\n", imagePath);
+
+    // set crash log message in case there is an assert during processing
+    strlcpy(sCrashMessageBuffer, "building closure for: ", sizeof(sCrashMessageBuffer));
+    strlcat(sCrashMessageBuffer, imagePath, sizeof(sCrashMessageBuffer));
+    CRSetCrashLogMessage(sCrashMessageBuffer);
+
+    Diagnostics diag;
+    const dyld3::launch_cache::binary_format::Closure* cls = dyld3::ImageProxyGroup::makeClosure(diag, clsBuff, requestor);
+
+    os_log_info(sLog, "finished closure build, closure=%p\n", cls);
+    for (const std::string& message: diag.warnings())
+        os_log(sLog, "Image generated warning: %s\n", message.c_str());
+
+    if ( diag.noError() ) {
+        // on success return the closure binary in the "returnData" buffer
+        dyld3::ClosureBuffer result(cls);
+        *returnData    = result.vmBuffer();
+        *returnDataCnt = result.vmBufferSize();
+    }
+    else {
+        // on failure return the error message in the "returnData" buffer
+        os_log_error(sLog, "failed to create ImageGroup: %s\n", diag.errorMessage().c_str());
+        dyld3::ClosureBuffer err(diag.errorMessage().c_str());
+        *returnData    = err.vmBuffer();
+        *returnDataCnt = err.vmBufferSize();
+    }
+
+    CRSetCrashLogMessage(nullptr);
+    return KERN_SUCCESS;
+}
+
+kern_return_t
+do_CreateImageGroup(
+    mach_port_t             port,
+    task_t                  requestor,
+    vm_address_t            buffer,
+    mach_msg_type_number_t  bufferCnt,
+    vm_address_t*           returnData,
+    mach_msg_type_number_t* returnDataCnt)
+{
+    dyld3::ClosureBuffer clsBuff((void*)buffer, bufferCnt);
+    const char* imagePath = clsBuff.targetPath();
+    int requestorPid;
+    char requestorName[MAXPATHLEN];
+    if ( pid_for_task(requestor, &requestorPid) == 0 ) {
+        int nameLen = proc_name(requestorPid, requestorName, sizeof(requestorName));
+        if ( nameLen <= 0 )
+            strcpy(requestorName, "???");
+        os_log(sLog, "request from %d (%s) to build dlopen ImageGroup for %s\n", requestorPid, requestorName, imagePath);
+    }
+
+    // set crash log message in case there is an assert during processing
+    strlcpy(sCrashMessageBuffer, "building ImageGroup for dlopen(", sizeof(sCrashMessageBuffer));
+    strlcat(sCrashMessageBuffer, imagePath, sizeof(sCrashMessageBuffer));
+    strlcat(sCrashMessageBuffer, ") requested by ", sizeof(sCrashMessageBuffer));
+    strlcat(sCrashMessageBuffer, requestorName, sizeof(sCrashMessageBuffer));
+    CRSetCrashLogMessage(sCrashMessageBuffer);
+
+    uuid_string_t  uuidStr;
+    dyld3::ClosureBuffer::CacheIdent cacheIdent = clsBuff.cacheIndent();
+    uuid_unparse(cacheIdent.cacheUUID, uuidStr);
+    os_log_info(sLog, "findDyldCache(): cache addr=0x%llX, size=0x%0llX, uuid = %s\n", cacheIdent.cacheAddress, cacheIdent.cacheMappedSize, uuidStr);
+
+    Diagnostics diag;
+    const dyld3::launch_cache::binary_format::ImageGroup* imageGroup = dyld3::ImageProxyGroup::makeDlopenGroup(diag, clsBuff, requestor, {""});
+
+    os_log(sLog, "finished ImageGroup build, imageGroup=%p\n", imageGroup);
+    for (const std::string& message: diag.warnings()) {
+        os_log(sLog, "Image generated warning: %s\n", message.c_str());
+    }
+
+    // delete incoming out-of-line data 
+    vm_deallocate(mach_task_self(), buffer, bufferCnt);
+
+    if ( diag.noError() ) {
+        // on success return the ImageGroup binary in the "returnData" buffer
+        dyld3::ClosureBuffer result(imageGroup);
+        os_log_info(sLog, "returning closure buffer: 0x%lX, size=0x%X\n", (long)result.vmBuffer(), result.vmBufferSize());
+        *returnData    = result.vmBuffer();
+        *returnDataCnt = result.vmBufferSize();
+        free((void*)imageGroup);
+    }
+    else {
+        // on failure return the error message in the "returnData" buffer
+        os_log_error(sLog, "failed to create ImageGroup: %s\n", diag.errorMessage().c_str());
+        dyld3::ClosureBuffer err(diag.errorMessage().c_str());
+        *returnData    = err.vmBuffer();
+        *returnDataCnt = err.vmBufferSize();
+    }
+
+    CRSetCrashLogMessage(nullptr);
+    return KERN_SUCCESS;
+}
+
+
+
+
+// /usr/libexec/closured -create_closure /Applications/TextEdit.app -pipefd 4  -env DYLD_FOO=1  -cache_uuid C153F90A-69F2-323E-AC9F-2BBAE48ABAF7
+int runAsTool(int argc, const char* argv[])
+{
+    const char*               progPath  = nullptr;
+    int                       pipeNum   = -1;
+    bool                      verbose   = false;
+    std::vector<std::string>  dyldEnvVars;
+    
+    dyld3::ClosureBuffer::CacheIdent cacheIdent;
+    bzero(&cacheIdent, sizeof(cacheIdent));
+
+    // set crash log message in case there is an assert during processing
+    sCrashMessageBuffer[0] = '\0';
+    for (int i=0; i < argc; ++i) {
+        strlcat(sCrashMessageBuffer, argv[i], sizeof(sCrashMessageBuffer));
+        strlcat(sCrashMessageBuffer, " ", sizeof(sCrashMessageBuffer));
+    }
+    CRSetCrashLogMessage(sCrashMessageBuffer);
+
+    for (int i=1; i < argc; ++i) {
+        const char* arg = argv[i];
+        if ( strcmp(arg, "-create_closure") == 0 ) {
+            progPath = argv[++i];
+            if ( progPath == nullptr ) {
+                fprintf(stderr, "-create_closure option requires a path to follow\n");
+                return 1;
+            }
+        }
+        else if ( strcmp(arg, "-cache_uuid") == 0 ) {
+            const char* uuidStr = argv[++i];
+            if ( uuidStr == nullptr ) {
+                fprintf(stderr, "-cache_uuid option requires a path to follow\n");
+                return 1;
+            }
+            uuid_parse(uuidStr, cacheIdent.cacheUUID);
+        }
+        else if ( strcmp(arg, "-cache_address") == 0 ) {
+            const char* cacheAddr = argv[++i];
+            if ( cacheAddr == nullptr ) {
+                fprintf(stderr, "-cache_address option requires a path to follow\n");
+                return 1;
+            }
+            char *end;
+            cacheIdent.cacheAddress = strtol(cacheAddr, &end, 0);
+        }
+        else if ( strcmp(arg, "-cache_size") == 0 ) {
+            const char* cacheSize = argv[++i];
+            if ( cacheSize == nullptr ) {
+                fprintf(stderr, "-cache_size option requires a path to follow\n");
+                return 1;
+            }
+            char *end;
+            cacheIdent.cacheMappedSize = strtol(cacheSize, &end, 0);
+        }
+        else if ( strcmp(arg, "-pipefd") == 0 ) {
+            const char* numStr = argv[++i];
+            if ( numStr == nullptr ) {
+                fprintf(stderr, "-pipefd option requires an file descriptor number to follow\n");
+                return 1;
+            }
+            char *end;
+            pipeNum = (int)strtol(numStr, &end, 0);
+            if ( (pipeNum == 0) && (errno != 0) ) {
+                fprintf(stderr, "bad value (%s) for -pipefd option %d\n", numStr, pipeNum);
+                return 1;
+            }
+        }
+        else if ( strcmp(arg, "-env") == 0 ) {
+            const char* var = argv[++i];
+            if ( var == nullptr ) {
+                fprintf(stderr, "-env option requires a following VAR=XXX\n");
+                return 1;
+            }
+            dyldEnvVars.push_back(var);
+        }
+        else {
+            fprintf(stderr, "unknown option: %s\n", arg);
+            return 1;
+        }
+    }
+    if ( progPath == nullptr ) {
+        fprintf(stderr, "missing required -create_closure option\n");
+        return 1;
+    }
+    if ( pipeNum == -1 ) {
+        fprintf(stderr, "missing required -pipefd option\n");
+        return 1;
+    }
+
+    if ( verbose ) {
+        fprintf(stderr, "closured: runAsTool()\n");
+        for (int i=1; i < argc; ++i)
+            fprintf(stderr, "   argv[%d] = %s\n", i, argv[i]);
+    }
+
+    os_log(sLog, "fork/exec request to build closure for %s\n", progPath);
+    SocketBasedClousureHeader header;
+
+    // find dyld cache for requested arch
+    size_t currentCacheSize;
+    const DyldSharedCache* currentCache = (const DyldSharedCache*)_dyld_get_shared_cache_range(&currentCacheSize);
+    if ( currentCache == nullptr ) {
+        os_log_error(sLog, "closured is running without a dyld cache\n");
+        return 1;
+    }
+    uuid_t currentCacheUUID;
+    currentCache->getUUID(currentCacheUUID);
+    if ( memcmp(currentCacheUUID, cacheIdent.cacheUUID, 16) != 0 ) {
+        const char* errorString = "closured is running with a different dyld cache than client";
+        os_log_error(sLog, "%s\n", errorString);
+        header.success = 0;
+        header.length  = (int)strlen(errorString) + 1;
+        write(pipeNum, &header, sizeof(SocketBasedClousureHeader));
+        write(pipeNum, errorString, header.length);
+        close(pipeNum);
+        return 0;
+    }
+    dyld3::DyldCacheParser cacheParser(currentCache, false);
+
+    Diagnostics diag;
+    os_log_info(sLog, "starting closure build\n");
+    const dyld3::launch_cache::BinaryClosureData* cls = dyld3::ImageProxyGroup::makeClosure(diag, cacheParser, progPath, false, {""}, dyldEnvVars);
+    os_log_info(sLog, "finished closure build, cls=%p\n", cls);
+    if ( diag.noError() ) {
+        // on success write the closure binary after the header to the socket
+        dyld3::launch_cache::Closure closure(cls);
+        os_log(sLog, "returning closure, size=%lu\n", closure.size());
+        header.success = 1;
+        header.length  = (uint32_t)closure.size();
+        write(pipeNum, &header, sizeof(SocketBasedClousureHeader));
+        write(pipeNum, cls, closure.size());
+    }
+    else {
+        // on failure write the error message after the header to the socket
+        const std::string& message = diag.errorMessage();
+        os_log_error(sLog, "closure could not be created: %s\n", message.c_str());
+        header.success = 0;
+        header.length  = (uint32_t)message.size() + 1;
+        write(pipeNum, &header, sizeof(SocketBasedClousureHeader));
+        write(pipeNum, message.c_str(), header.length);
+    }
+
+    close(pipeNum);
+
+    return 0;
+}
+
+
+union MaxMsgSize {
+    union __RequestUnion__do_closured_subsystem req;
+    union __ReplyUnion__do_closured_subsystem   rep;
+};
+
+int main(int argc, const char* argv[])
+{
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
+    // establish sandbox around process
+    char* errMsg = nullptr;
+    if ( sandbox_init_with_parameters("com.apple.dyld.closured", SANDBOX_NAMED, nullptr, &errMsg) != 0 ) {
+        os_log_error(sLog, "Failed to enter sandbox: %{public}s", errMsg);
+        exit(EXIT_FAILURE);
+    }
+#endif
+
+    if ( argc != 1 )
+        return runAsTool(argc, argv);\
+
+    mach_port_t serverPort = MACH_PORT_NULL;
+    kern_return_t kr = bootstrap_check_in(bootstrap_port, CLOSURED_SERVICE_NAME, &serverPort);
+    if (kr != KERN_SUCCESS)
+        exit(-1);
+    
+    dispatch_source_t machSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV, serverPort, 0, dispatch_get_main_queue());
+    if (machSource == nullptr)
+        exit(-1);
+    
+    dispatch_source_set_event_handler(machSource, ^{
+        dispatch_mig_server(machSource, sizeof(union MaxMsgSize), closured_server);
+    });
+    dispatch_source_set_cancel_handler(machSource, ^{
+        mach_port_t port = (mach_port_t)dispatch_source_get_handle(machSource);
+        kern_return_t kr = mach_port_mod_refs(mach_task_self(), port, MACH_PORT_RIGHT_RECEIVE, -1);
+        if (kr != KERN_SUCCESS)
+            exit(-1);
+    });
+    dispatch_resume(machSource);
+    dispatch_main();
+
+    return 0;
+}
+
diff --git a/dyld3/closured/closuredProtocol.defs b/dyld3/closured/closuredProtocol.defs
new file mode 100644 (file)
index 0000000..565c8bc
--- /dev/null
@@ -0,0 +1,29 @@
+
+
+#include <mach/mach_types.defs>
+#include <mach/std_types.defs>
+
+import "closuredtypes.h";
+
+subsystem closured 6000;
+
+userprefix      closured_;      // Routine prefixes for user access
+serverprefix    do_;            // Routine prefixes for internal server access
+
+type OutOfLineBuffer_t = ^array[] of MACH_MSG_TYPE_BYTE ctype: vm_address_t;
+
+// used at launch
+routine CreateClosure (
+                port        : mach_port_t;
+    in          requestor   : task_t;
+    in          buffer      : OutOfLineBuffer_t;
+    out         returnData  : OutOfLineBuffer_t, dealloc
+);
+
+// used in dlopen()cl
+routine CreateImageGroup (
+                port        : mach_port_t;
+    in          requestor   : task_t;
+    in          buffer      : OutOfLineBuffer_t;
+    out         returnData  : OutOfLineBuffer_t, dealloc
+);
diff --git a/dyld3/closured/closured_entitlements.plist b/dyld3/closured/closured_entitlements.plist
new file mode 100644 (file)
index 0000000..5a352d6
--- /dev/null
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+    <dict>
+       <key>seatbelt-profiles</key>
+       <array>
+               <string>closured</string>
+       </array>
+       <key>platform-application</key>
+       <true/>
+    </dict>
+</plist>
diff --git a/dyld3/closured/closuredtypes.h b/dyld3/closured/closuredtypes.h
new file mode 100644 (file)
index 0000000..6acc727
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2017 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 <stdbool.h>
+
+#undef __MigTypeCheck
+#undef USING_VOUCHERS
+
+
+typedef void* ClosureBufferPtr;
+typedef void* ClosureBufferConstPtr;
+
+struct SocketBasedClousureHeader
+{
+    uint32_t    success;    // 1 => rest of buffer is closure, 0 => rest of buffer is error string
+    uint32_t    length;
+};
+
+#define CLOSURED_SERVICE_NAME "com.apple.dyld.closured"
+
+#define mig_external __private_extern__
+
diff --git a/dyld3/closured/com.apple.dyld.closured.plist b/dyld3/closured/com.apple.dyld.closured.plist
new file mode 100644 (file)
index 0000000..e19d134
--- /dev/null
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+       <key>ProcessType</key>
+       <string>Adaptive</string>
+       <key>EnableTransactions</key>
+       <true/>
+       <key>EnablePressuredExit</key>
+       <true/>
+       <key>Label</key>
+       <string>com.apple.dyld.closured</string>
+       <key>MachServices</key>
+       <dict>
+               <key>com.apple.dyld.closured</key>
+               <true/>
+       </dict>
+       <key>TimeOut</key>
+       <integer>60</integer>
+       <key>ProgramArguments</key>
+       <array>
+               <string>/usr/libexec/closured</string>
+       </array>
+</dict>
+</plist>
diff --git a/dyld3/closured/com.apple.dyld.closured.sb b/dyld3/closured/com.apple.dyld.closured.sb
new file mode 100644 (file)
index 0000000..bd89f9c
--- /dev/null
@@ -0,0 +1,22 @@
+;;; Copyright (c) 2017 Apple Inc.  All Rights reserved.
+;;;
+;;; WARNING: The sandbox rules in this file currently constitute
+;;; Apple System Private Interface and are subject to change at any time and
+;;; without notice.
+;;;
+(version 1)
+
+(deny default)
+(deny file-map-executable iokit-get-properties process-info* nvram*)
+(deny dynamic-code-generation)
+
+(import "system.sb")
+
+;; For reading dylibs
+(allow file-read*)
+
+;; For resolving symlinks, realpath(3), and equivalents.
+(allow file-read-metadata)
+
+;; for logging name of client
+(allow process-info-pidinfo)
diff --git a/dyld3/dyld-potential-framework-overrides b/dyld3/dyld-potential-framework-overrides
new file mode 100644 (file)
index 0000000..998a298
--- /dev/null
@@ -0,0 +1,7 @@
+/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation
+/System/Library/Frameworks/Foundation.framework/Versions/C/Foundation
+/System/Library/Frameworks/MediaToolbox.framework/Versions/A/MediaToolbox
+/System/Library/PrivateFrameworks/MetalTools.framework/Versions/A/MetalTools
+/System/Library/Frameworks/WebKit.framework/Versions/A/WebKit
+/System/Library/Frameworks/JavaScriptCore.framework/Versions/A/JavaScriptCore
+
diff --git a/dyld3/libclosured-stub.cpp b/dyld3/libclosured-stub.cpp
new file mode 100644 (file)
index 0000000..90820e4
--- /dev/null
@@ -0,0 +1,12 @@
+
+namespace dyld3 {
+
+struct ClosureBuffer { int x; };
+
+ClosureBuffer closured_CreateImageGroup(const ClosureBuffer& input)
+{
+    return ClosureBuffer();
+}
+
+
+} // namespace dyld3
diff --git a/dyld3/libdyldEntryVector.cpp b/dyld3/libdyldEntryVector.cpp
new file mode 100644 (file)
index 0000000..9735df2
--- /dev/null
@@ -0,0 +1,146 @@
+/*
+ * Copyright (c) 2017 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 <stdarg.h>
+
+#include "dyld_priv.h"
+#include "libdyldEntryVector.h"
+#include "AllImages.h"
+#include "Logging.h"
+#include "PathOverrides.h"
+#include "LaunchCacheFormat.h"
+
+extern "C" void start();
+
+
+VIS_HIDDEN const char** appleParams;
+
+extern bool gUseDyld3;
+
+namespace dyld3 {
+
+
+AllImages::ProgramVars sVars;
+static void (*sChildForkFunction)();
+
+static const char* leafName(const char* argv0)
+{
+    if ( argv0 == nullptr )
+       return "";
+
+    if ( const char* lastSlash = strrchr(argv0, '/') )
+        return lastSlash+1;
+    else
+        return argv0;
+}
+
+static void entry_setVars(const mach_header* mainMH, int argc, const char* argv[], const char* envp[], const char* apple[])
+{
+    NXArgc       = argc;
+    NXArgv       = argv;
+    environ      = (char**)envp;
+    appleParams  = apple;
+    __progname   = leafName(argv[0]);
+
+    sVars.mh            = mainMH;
+    sVars.NXArgcPtr     = &NXArgc;
+    sVars.NXArgvPtr     = &NXArgv;
+    sVars.environPtr    = (const char***)&environ;
+    sVars.__prognamePtr = &__progname;
+    gAllImages.setProgramVars(&sVars);
+
+    gUseDyld3 = true;
+
+    setLoggingFromEnvs(envp);
+}
+
+static void entry_setHaltFunction(void (*func)(const char* message) __attribute__((noreturn)) )
+{
+    setHaltFunction(func);
+}
+
+static void entry_setLogFunction(void (*logFunction)(const char* format, va_list list))
+{
+    setLoggingFunction(logFunction);
+}
+
+static void entry_setOldAllImageInfo(dyld_all_image_infos* old)
+{
+    gAllImages.setOldAllImageInfo(old);
+}
+
+static void entry_setInitialImageList(const launch_cache::binary_format::Closure* closure,
+                                const void* dyldCacheLoadAddress, const char* dyldCachePath,
+                                const dyld3::launch_cache::DynArray<loader::ImageInfo>& initialImages,
+                                const mach_header* libSystemMH, const launch_cache::binary_format::Image* libSystemImage)
+{
+    gAllImages.init(closure, dyldCacheLoadAddress, dyldCachePath, initialImages);
+    gAllImages.applyInterposingToDyldCache(closure, initialImages);
+
+    const char* mainPath = _simple_getenv(appleParams, "executable_path");
+    if ( (mainPath != nullptr) && (mainPath[0] == '/') )
+        gAllImages.setMainPath(mainPath);
+
+    // ok to call before malloc is ready because 4 slots are reserved.
+    gAllImages.setInitialGroups();
+
+    // run initializer for libSytem.B.dylib
+    // this calls back into _dyld_initializer which calls gAllIimages.addImages()
+    gAllImages.runLibSystemInitializer(libSystemMH, libSystemImage);
+
+    // now that malloc is available, parse DYLD_ env vars
+    gPathOverrides.setEnvVars((const char**)environ);
+}
+
+static void entry_runInitialzersBottomUp(const mach_header* mainExecutableImageLoadAddress)
+{
+    gAllImages.runInitialzersBottomUp(mainExecutableImageLoadAddress);
+    gAllImages.notifyMonitorMain();
+}
+
+static void entry_setChildForkFunction(void (*func)() )
+{
+    sChildForkFunction = func;
+}
+
+const LibDyldEntryVector entryVectorForDyld = {
+    LibDyldEntryVector::kCurrentVectorVersion,
+    launch_cache::binary_format::kFormatVersion,
+    &entry_setVars,
+    &entry_setHaltFunction,
+    &entry_setOldAllImageInfo,
+    &entry_setInitialImageList,
+    &entry_runInitialzersBottomUp,
+    &start,
+    &entry_setChildForkFunction,
+    &entry_setLogFunction,
+};
+
+VIS_HIDDEN void _dyld_fork_child()
+{
+    (*sChildForkFunction)();
+}
+
+
+} // namespace dyld3
+
diff --git a/dyld3/libdyldEntryVector.h b/dyld3/libdyldEntryVector.h
new file mode 100644 (file)
index 0000000..3c61df2
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2017 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_ENTRY_VECTOR_H__
+#define __DYLD_ENTRY_VECTOR_H__
+
+#include <mach-o/loader.h>
+
+#include "LaunchCache.h"
+#include "Loading.h"
+
+struct dyld_all_image_infos;
+
+namespace dyld3 {
+
+struct LibDyldEntryVector
+{
+    enum { kCurrentVectorVersion = 4 };
+
+    uint32_t    vectorVersion;              // should be kCurrentVectorVersion
+    uint32_t    binaryFormatVersion;        // should be launch_cache::binary_format::kFormatVersion
+    void        (*setVars)(const mach_header* mainMH, int argc, const char* argv[], const char* envp[], const char* apple[]);
+    void        (*setHaltFunction)(void (*func)(const char* message) __attribute__((noreturn)) );
+    void        (*setOldAllImageInfo)(dyld_all_image_infos*);
+    void        (*setInitialImageList)(const launch_cache::BinaryClosureData* closure,
+                                        const void* dyldCacheLoadAddress, const char* dyldCachePath,
+                                        const dyld3::launch_cache::DynArray<loader::ImageInfo>& initialImages,
+                                        const mach_header* libSystemMH, const launch_cache::BinaryImageData* libSystemImage);
+    void        (*runInitialzersBottomUp)(const mach_header* topImageLoadAddress);
+    void        (*startFunc)();
+    // added in version 3
+    void        (*setChildForkFunction)(void (*func)());
+    // added in version 4
+    void        (*setLogFunction)(void (*logFunction)(const char* format, va_list list));
+};
+
+extern const LibDyldEntryVector entryVectorForDyld;
+
+} // namespace dyld3
+
+
+#endif // __DYLD_ENTRY_VECTOR_H__
+
+
+
+
diff --git a/dyld3/shared-cache/AdjustDylibSegments.cpp b/dyld3/shared-cache/AdjustDylibSegments.cpp
new file mode 100644 (file)
index 0000000..9ab3bb8
--- /dev/null
@@ -0,0 +1,1084 @@
+/* -*- 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 <dirent.h>
+#include <sys/errno.h>
+#include <sys/fcntl.h>
+#include <mach-o/loader.h>
+#include <mach-o/fat.h>
+#include <assert.h>
+
+#include <fstream>
+#include <string>
+#include <algorithm>
+#include <unordered_map>
+#include <unordered_set>
+
+#include "CacheBuilder.h"
+#include "Diagnostics.h"
+#include "DyldSharedCache.h"
+#include "Trie.hpp"
+#include "MachOFileAbstraction.hpp"
+
+
+#ifndef EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE
+    #define EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE 0x02
+#endif
+
+namespace {
+
+template <typename P>
+class Adjustor {
+public:
+                    Adjustor(DyldSharedCache* cacheBuffer, macho_header<P>* mh, const std::vector<CacheBuilder::SegmentMappingInfo>& mappingInfo, Diagnostics& diag);
+    void            adjustImageForNewSegmentLocations(std::vector<void*>& pointersForASLR);
+
+private:
+    void            adjustReferencesUsingInfoV2(std::vector<void*>& 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<void*>& pointersForASLR, uint32_t*& lastMappedAddr32,
+                                    uint32_t& lastKind, uint64_t& lastToNewAddress);
+    void            adjustDataPointers(std::vector<void*>& pointersForASLR);
+    void            slidePointer(int segIndex, uint64_t segOffset, uint8_t type, std::vector<void*>& pointersForASLR);
+    void            adjustSymbolTable();
+    void            adjustExportsTrie(std::vector<uint8_t>& 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;
+
+    DyldSharedCache*                                        _cacheBuffer;
+    macho_header<P>*                                        _mh;
+    Diagnostics&                                            _diagnostics;
+    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<P>*                                _symTabCmd          = nullptr;
+    macho_dysymtab_command<P>*                              _dynSymTabCmd       = nullptr;
+    macho_dyld_info_command<P>*                             _dyldInfo           = nullptr;
+    macho_linkedit_data_command<P>*                         _splitSegInfoCmd    = nullptr;
+    macho_linkedit_data_command<P>*                         _functionStartsCmd  = nullptr;
+    macho_linkedit_data_command<P>*                         _dataInCodeCmd      = nullptr;
+    std::vector<uint64_t>                                   _segOrigStartAddresses;
+    std::vector<uint64_t>                                   _segSlides;
+    std::vector<macho_segment_command<P>*>                  _segCmds;
+    const std::vector<CacheBuilder::SegmentMappingInfo>&    _mappingInfo;
+};
+
+template <typename P>
+Adjustor<P>::Adjustor(DyldSharedCache* cacheBuffer, macho_header<P>* mh, const std::vector<CacheBuilder::SegmentMappingInfo>& mappingInfo, Diagnostics& diag)
+    : _cacheBuffer(cacheBuffer), _mh(mh), _diagnostics(diag), _mappingInfo(mappingInfo)
+{
+    assert((mh->magic() == MH_MAGIC) || (mh->magic() == MH_MAGIC_64));
+    macho_segment_command<P>* segCmd;
+    const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)mh + sizeof(macho_header<P>));
+    const uint32_t cmd_count = mh->ncmds();
+    const macho_load_command<P>* 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<P>*)cmd)->name();
+                break;
+            case LC_SYMTAB:
+                _symTabCmd = (macho_symtab_command<P>*)cmd;
+                break;
+            case LC_DYSYMTAB:
+                _dynSymTabCmd = (macho_dysymtab_command<P>*)cmd;
+                break;
+            case LC_DYLD_INFO:
+            case LC_DYLD_INFO_ONLY:
+                _dyldInfo = (macho_dyld_info_command<P>*)cmd;
+                break;
+            case LC_SEGMENT_SPLIT_INFO:
+                _splitSegInfoCmd = (macho_linkedit_data_command<P>*)cmd;
+                break;
+            case LC_FUNCTION_STARTS:
+                _functionStartsCmd = (macho_linkedit_data_command<P>*)cmd;
+                break;
+            case LC_DATA_IN_CODE:
+                _dataInCodeCmd = (macho_linkedit_data_command<P>*)cmd;
+                break;
+            case macho_segment_command<P>::CMD:
+                segCmd = (macho_segment_command<P>*)cmd;
+                _segCmds.push_back(segCmd);
+                _segOrigStartAddresses.push_back(segCmd->vmaddr());
+                _segSlides.push_back(_mappingInfo[segIndex].dstCacheAddress - segCmd->vmaddr());
+                if ( strcmp(segCmd->segname(), "__LINKEDIT") == 0 ) {
+                    _linkeditAdjust = _mappingInfo[segIndex].dstCacheOffset - segCmd->fileoff();
+                    _linkeditBias = (uint8_t*)cacheBuffer + _linkeditAdjust;
+                    _linkeditSegIndex = segIndex;
+                }
+                ++segIndex;
+                break;
+        }
+        cmd = (const macho_load_command<P>*)(((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);
+    }
+    else {
+        _diagnostics.error("missing LC_SEGMENT_SPLIT_INFO in %s", _installName);
+    }
+}
+
+template <typename P>
+void Adjustor<P>::adjustImageForNewSegmentLocations(std::vector<void*>& pointersForASLR)
+{
+    if ( _diagnostics.hasError() )
+        return;
+    if ( _splitSegInfoV2 ) {
+        adjustReferencesUsingInfoV2(pointersForASLR);
+    }
+    else {
+        adjustDataPointers(pointersForASLR);
+        adjustCode();
+    }
+    if ( _diagnostics.hasError() )
+        return;
+    adjustSymbolTable();
+    if ( _diagnostics.hasError() )
+        return;
+    rebuildLinkEditAndLoadCommands();
+}
+
+template <typename P>
+uint64_t Adjustor<P>::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);
+    }
+    _diagnostics.error("slide not known for dylib address 0x%llX in %s", addr, _installName);
+    return 0;
+}
+
+template <typename P>
+void Adjustor<P>::rebuildLinkEditAndLoadCommands()
+{
+    // Exports trie is only data structure in LINKEDIT that might grow
+    std::vector<uint8_t> 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<P>);
+    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 ) {
+        _diagnostics.error("LINKEDIT overflow in %s", _installName);
+        return;
+    }
+
+    uint32_t linkeditStartOffset = (uint32_t)_mappingInfo[_linkeditSegIndex].dstCacheOffset;
+    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<P>* const cmds = (macho_load_command<P>*)((uint8_t*)_mh + sizeof(macho_header<P>));
+    uint32_t cmd_count = _mh->ncmds();
+    const macho_load_command<P>* 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<P>*           symTabCmd;
+        macho_dysymtab_command<P>*         dynSymTabCmd;
+        macho_dyld_info_command<P>*        dyldInfo;
+        macho_linkedit_data_command<P>*    functionStartsCmd;
+        macho_linkedit_data_command<P>*    dataInCodeCmd;
+        macho_linkedit_data_command<P>*    splitSegInfoCmd;
+        macho_segment_command<P>*          segCmd;
+        macho_routines_command<P>*         routinesCmd;
+        macho_dylib_command<P>*            dylibIDCmd;
+        uint32_t cmdSize = cmd->cmdsize();
+        int32_t segFileOffsetDelta;
+        bool remove = false;
+        switch ( cmd->cmd() ) {
+            case LC_ID_DYLIB:
+                dylibIDCmd = (macho_dylib_command<P>*)cmd;
+                dylibIDCmd->set_timestamp(2); // match what static linker sets in LC_LOAD_DYLIB
+                break;
+            case LC_SYMTAB:
+                symTabCmd = (macho_symtab_command<P>*)cmd;
+                symTabCmd->set_symoff(linkeditStartOffset+symbolTableOffset);
+                symTabCmd->set_stroff(linkeditStartOffset+symbolStringsOffset);
+              break;
+            case LC_DYSYMTAB:
+                dynSymTabCmd = (macho_dysymtab_command<P>*)cmd;
+                dynSymTabCmd->set_indirectsymoff(linkeditStartOffset+indirectTableOffset);
+                break;
+            case LC_DYLD_INFO:
+            case LC_DYLD_INFO_ONLY:
+                dyldInfo = (macho_dyld_info_command<P>*)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<P>*)cmd;
+                functionStartsCmd->set_dataoff(linkeditStartOffset+funcStartsOffset);
+               break;
+            case LC_DATA_IN_CODE:
+                dataInCodeCmd = (macho_linkedit_data_command<P>*)cmd;
+                dataInCodeCmd->set_dataoff(linkeditStartOffset+dataInCodeOffset);
+                break;
+            case macho_routines_command<P>::CMD:
+                routinesCmd = (macho_routines_command<P>*)cmd;
+                routinesCmd->set_init_address(routinesCmd->init_address()+slideForOrigAddress(routinesCmd->init_address()));
+                break;
+            case macho_segment_command<P>::CMD:
+                segCmd = (macho_segment_command<P>*)cmd;
+                segFileOffsetDelta = (int32_t)(_mappingInfo[segIndex].dstCacheOffset - segCmd->fileoff());
+                segCmd->set_vmaddr(_mappingInfo[segIndex].dstCacheAddress);
+                segCmd->set_vmsize(_mappingInfo[segIndex].dstCacheSegmentSize);
+                segCmd->set_fileoff(_mappingInfo[segIndex].dstCacheOffset);
+                segCmd->set_filesize(_mappingInfo[segIndex].copySegmentSize);
+                if ( strcmp(segCmd->segname(), "__LINKEDIT") == 0 )
+                    segCmd->set_vmsize(linkeditBufferSize);
+                if ( segCmd->nsects() > 0 ) {
+                    macho_section<P>* const sectionsStart = (macho_section<P>*)((uint8_t*)segCmd + sizeof(macho_segment_command<P>));
+                    macho_section<P>* const sectionsEnd = &sectionsStart[segCmd->nsects()];
+                    for (macho_section<P>*  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:
+                _diagnostics.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<P>*)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<P>* nextCmd = (macho_load_command<P>*)(((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 <typename P>
+void Adjustor<P>::adjustSymbolTable()
+{
+    macho_nlist<P>*  symbolTable = (macho_nlist<P>*)&_linkeditBias[_symTabCmd->symoff()];
+
+    // adjust global symbol table entries
+    macho_nlist<P>* lastExport = &symbolTable[_dynSymTabCmd->iextdefsym()+_dynSymTabCmd->nextdefsym()];
+    for (macho_nlist<P>* 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<P>*  lastLocal = &symbolTable[_dynSymTabCmd->ilocalsym()+_dynSymTabCmd->nlocalsym()];
+    for (macho_nlist<P>* 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 <typename P>
+void Adjustor<P>::slidePointer(int segIndex, uint64_t segOffset, uint8_t type, std::vector<void*>& pointersForASLR)
+{
+    pint_t*   mappedAddrP = (pint_t*)((uint8_t*)_cacheBuffer + _mappingInfo[segIndex].dstCacheOffset + 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:
+            _diagnostics.error("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 <typename P>
+void Adjustor<P>::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<void*>& pointersForASLR, uint32_t*& lastMappedAddr32, uint32_t& lastKind, uint64_t& lastToNewAddress)
+{
+    uint64_t value64;
+    uint64_t* mappedAddr64 = 0;
+    uint32_t value32;
+    uint32_t* mappedAddr32 = 0;
+    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) ) {
+                _diagnostics.error("DYLD_CACHE_ADJ_V2_DELTA_32 can't be adjust by 0x%016llX in %s", adjust, _installName);
+                return;
+            }
+            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) ) {
+                _diagnostics.error("bad DYLD_CACHE_ADJ_V2_POINTER_32 value not as expected at address 0x%llX in %s", fromNewAddress, _installName);
+                return;
+            }
+            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) ) {
+                _diagnostics.error("bad DYLD_CACHE_ADJ_V2_POINTER_64 value not as expected at address 0x%llX in %s", fromNewAddress, _installName);
+                return;
+            }
+            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 ) {
+                _diagnostics.error("DYLD_CACHE_ADJ_V2_IMAGE_OFF_32 can't be adjust to 0x%016llX in %s", toNewAddress, _installName);
+                return;
+            }
+            P::E::set32(*mappedAddr32, (uint32_t)value64);
+            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) ) {
+                    _diagnostics.error("DYLD_CACHE_ADJ_V2_ARM64_ADRP can't be adjusted that far in %s", _installName);
+                    return;
+                }
+                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 ) {
+                                    _diagnostics.error("can't adjust off12 scale=16 instruction by %lld bytes at mapped address=%p in %s", offsetAdjust, mappedAddr, _installName);
+                                    return;
+                                }
+                                if ( encodedAddend*16 >= 4096 ) {
+                                    _diagnostics.error("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 ) {
+                                _diagnostics.error("can't adjust off12 scale=2 instruction by %lld bytes at mapped address=%p in %s", offsetAdjust, mappedAddr, _installName);
+                                return;
+                            }
+                            if ( encodedAddend*2 >= 4096 ) {
+                                _diagnostics.error("off12 scale=2 instruction points outside its page at mapped address=%p in %s", mappedAddr, _installName);
+                                return;
+                            }
+                            newAddend = (encodedAddend + offsetAdjust/2) % 2048;
+                            break;
+                        case 0x80000000:
+                            if ( offsetAdjust & 3 ) {
+                                _diagnostics.error("can't adjust off12 scale=4 instruction by %lld bytes at mapped address=%p in %s", offsetAdjust, mappedAddr, _installName);
+                                return;
+                            }
+                            if ( encodedAddend*4 >= 4096 ) {
+                                _diagnostics.error("off12 scale=4 instruction points outside its page at mapped address=%p in %s", mappedAddr, _installName);
+                                return;
+                            }
+                            newAddend = (encodedAddend + offsetAdjust/4) % 1024;
+                            break;
+                        case 0xC0000000:
+                            if ( offsetAdjust & 7 ) {
+                                _diagnostics.error("can't adjust off12 scale=8 instruction by %lld bytes at mapped address=%p in %s", offsetAdjust, mappedAddr, _installName);
+                                return;
+                            }
+                            if ( encodedAddend*8 >= 4096 ) {
+                                _diagnostics.error("off12 scale=8 instruction points outside its page at mapped address=%p in %s", mappedAddr, _installName);
+                                return;
+                            }
+                            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 ) {
+                    _diagnostics.error("ADD off12 uses shift at mapped address=%p in %s", mappedAddr, _installName);
+                    return;
+                }
+                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
+                _diagnostics.error("unknown off12 instruction 0x%08X at 0x%0llX in %s", instruction, fromNewAddress, _installName);
+                return;
+            }
+            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 {
+                        _diagnostics.error("two DYLD_CACHE_ADJ_V2_THUMB_MOVW_MOVT in a row but not paried in %s", _installName);
+                        return;
+                    }
+                    P::E::set32(*lastMappedAddr32, instruction1);
+                    P::E::set32(*mappedAddr32, instruction2);
+                    kind = 0;
+                }
+                else {
+                    _diagnostics.error("two DYLD_CACHE_ADJ_V2_THUMB_MOVW_MOVT in a row but target different addresses in %s", _installName);
+                    return;
+                }
+            }
+            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 {
+                        _diagnostics.error("two DYLD_CACHE_ADJ_V2_ARM_MOVW_MOVT in a row but not paired in %s", _installName);
+                        return;
+                    }
+                    P::E::set32(*lastMappedAddr32, instruction1);
+                    P::E::set32(*mappedAddr32, instruction2);
+                    kind = 0;
+                }
+                else {
+                    _diagnostics.error("two DYLD_CACHE_ADJ_V2_ARM_MOVW_MOVT in a row but target different addresses in %s", _installName);
+                    return;
+                }
+            }
+            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:
+            _diagnostics.error("unknown split seg kind=%d in %s", kind, _installName);
+            return;
+    }
+    lastKind = kind;
+    lastToNewAddress = toNewAddress;
+    lastMappedAddr32 = mappedAddr32;
+}
+
+template <typename P>
+void Adjustor<P>::adjustReferencesUsingInfoV2(std::vector<void*>& 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 ) {
+        _diagnostics.error("malformed split seg info in %s", _installName);
+        return;
+    }
+    // build section arrays of slide and mapped address for each section
+    std::vector<uint64_t> sectionSlides;
+    std::vector<uint64_t> sectionNewAddress;
+    std::vector<uint8_t*> sectionMappedAddress;
+    sectionSlides.reserve(16);
+    sectionNewAddress.reserve(16);
+    sectionMappedAddress.reserve(16);
+    // section index 0 refers to mach_header
+    sectionMappedAddress.push_back((uint8_t*)_cacheBuffer + _mappingInfo[0].dstCacheOffset);
+    sectionSlides.push_back(_segSlides[0]);
+    sectionNewAddress.push_back(_mappingInfo[0].dstCacheAddress);
+    // section 1 and later refer to real sections
+    unsigned sectionIndex = 0;
+    for (unsigned segmentIndex=0; segmentIndex < _segCmds.size(); ++segmentIndex) {
+        macho_segment_command<P>* segCmd = _segCmds[segmentIndex];
+        macho_section<P>* const sectionsStart = (macho_section<P>*)((char*)segCmd + sizeof(macho_segment_command<P>));
+        macho_section<P>* const sectionsEnd = &sectionsStart[segCmd->nsects()];
+        for(macho_section<P>* sect = sectionsStart; sect < sectionsEnd; ++sect) {
+            sectionMappedAddress.push_back((uint8_t*)_cacheBuffer + _mappingInfo[segmentIndex].dstCacheOffset + sect->addr() - segCmd->vmaddr());
+            sectionSlides.push_back(_segSlides[segmentIndex]);
+            sectionNewAddress.push_back(_mappingInfo[segmentIndex].dstCacheAddress + 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         :== <count> FromToSection+
+    // FromToSection :== <from-sect-index> <to-sect-index> <count> ToOffset+
+    // ToOffset         :== <to-sect-offset-delta> <count> FromOffset+
+    // FromOffset     :== <kind> <count> <from-sect-offset-delta>
+    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 ) {
+                    _diagnostics.error("bad kind value (%llu) in %s", kind, _installName);
+                    return;
+                }
+                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);
+                    if ( _diagnostics.hasError() )
+                        return;
+                }
+            }
+        }
+    }
+
+}
+
+template <typename P>
+void Adjustor<P>::adjustDataPointers(std::vector<void*>& 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:
+                _diagnostics.error("unknown rebase opcode 0x%02X in %s", opcode, _installName);
+                done = true;
+                break;
+        }
+    }
+}
+
+
+template <typename P>
+void Adjustor<P>::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 <typename P>
+void Adjustor<P>::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:  [ <kind> [uleb128-delta]+ <0> ] + <0>
+    for (const uint8_t* p = infoStart; (*p != 0) && (p < infoEnd);) {
+        uint8_t kind = *p++;
+        uint64_t cacheOffset = _mappingInfo[0].dstCacheOffset;
+        while (uint64_t delta = read_uleb128(p, infoEnd)) {
+            cacheOffset += delta;
+            adjustInstruction(kind, cacheOffset, codeToDataDelta);
+        }
+    }
+}
+
+
+template <typename P>
+void Adjustor<P>::adjustExportsTrie(std::vector<uint8_t>& 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<ExportInfoTrie::Entry> originalExports;
+    if ( !ExportInfoTrie::parseTrie(start, end, originalExports) ) {
+        _diagnostics.error("malformed exports trie in %s", _installName);
+        return;
+    }
+
+    std::vector<ExportInfoTrie::Entry> 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 adjustDylibSegments(DyldSharedCache* cache, bool is64, mach_header* mhInCache, const std::vector<CacheBuilder::SegmentMappingInfo>& mappingInfo, std::vector<void*>& pointersForASLR, Diagnostics& diag)
+{
+    if ( is64 ) {
+        Adjustor<Pointer64<LittleEndian>> adjustor64(cache, (macho_header<Pointer64<LittleEndian>>*)mhInCache, mappingInfo, diag);
+        adjustor64.adjustImageForNewSegmentLocations(pointersForASLR);
+    }
+    else {
+        Adjustor<Pointer32<LittleEndian>> adjustor32(cache, (macho_header<Pointer32<LittleEndian>>*)mhInCache, mappingInfo, diag);
+        adjustor32.adjustImageForNewSegmentLocations(pointersForASLR);
+    }
+}
+
+
+
+
+
diff --git a/dyld3/shared-cache/BuilderUtils.h b/dyld3/shared-cache/BuilderUtils.h
new file mode 100644 (file)
index 0000000..713d0bd
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2017 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 BuilderUtils_h
+#define BuilderUtils_h
+
+dispatch_group_t buildGroup();
+void makeBoms(dyld3::Manifest& manifest, const std::string& masterDstRoot);
+bool build(Diagnostics& diags, dyld3::Manifest& manifest, const std::string& masterDstRoot, bool dedupe, bool verbose, bool skipWrites, bool agileChooseSHA256CdHash);
+
+#endif /* BuilderUtils_h */
diff --git a/dyld3/shared-cache/BuilderUtils.mm b/dyld3/shared-cache/BuilderUtils.mm
new file mode 100644 (file)
index 0000000..ef2a675
--- /dev/null
@@ -0,0 +1,311 @@
+/*
+ * Copyright (c) 2017 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 <set>
+#include <string>
+#include <sstream>
+#include <iomanip> // std::setfill, std::setw
+#include <pthread.h>
+#include <mach/mach.h>
+#include <dispatch/dispatch.h>
+
+#include <Bom/Bom.h>
+#include <Security/Security.h>
+#include <Security/SecCodeSigner.h>
+#include <CommonCrypto/CommonCrypto.h>
+
+#include "Manifest.h"
+#include "Diagnostics.h"
+#include "FileUtils.h"
+
+#include "BuilderUtils.h"
+
+static dispatch_queue_t write_queue = dispatch_queue_create("com.apple.dyld.cache-builder.write", DISPATCH_QUEUE_CONCURRENT);
+static dispatch_group_t build_group = dispatch_group_create();
+
+dispatch_group_t buildGroup() {
+    return build_group;
+}
+
+void insertFileInBom(const std::string& path, BOMBom bom)
+{
+    std::vector<std::string> components;
+    std::vector<std::string> processed_components;
+    std::stringstream ss(path);
+    std::string item;
+
+    while (std::getline(ss, item, '/')) {
+        if (!item.empty()) {
+            components.push_back(item);
+        }
+    }
+
+    std::string partialPath = ".";
+    std::string lastComponent = components.back();
+    components.pop_back();
+    BOMFSObject fso = BOMFSObjectNew(BOMDirectoryType);
+    BOMFSObjectSetFlags(fso, B_PATHONLY);
+    BOMFSObjectSetPathName(fso, ".", true);
+    BOMFSObjectSetShortName(fso, ".", true);
+    (void)BOMBomInsertFSObject(bom, fso, false);
+    BOMFSObjectFree(fso);
+
+    for (const auto& component : components) {
+        partialPath = partialPath + "/" + component;
+        fso = BOMFSObjectNew(BOMDirectoryType);
+        BOMFSObjectSetFlags(fso, B_PATHONLY);
+        BOMFSObjectSetPathName(fso, partialPath.c_str(), true);
+        BOMFSObjectSetShortName(fso, component.c_str(), true);
+        (void)BOMBomInsertFSObject(bom, fso, false);
+        BOMFSObjectFree(fso);
+    }
+
+    partialPath = partialPath + "/" + lastComponent;
+    fso = BOMFSObjectNew(BOMFileType);
+    BOMFSObjectSetFlags(fso, B_PATHONLY);
+    BOMFSObjectSetPathName(fso, partialPath.c_str(), true);
+    BOMFSObjectSetShortName(fso, lastComponent.c_str(), true);
+    (void)BOMBomInsertFSObject(bom, fso, false);
+    BOMFSObjectFree(fso);
+}
+
+void makeBoms(dyld3::Manifest& manifest, const std::string& masterDstRoot)
+{
+    mkpath_np((masterDstRoot + "/Boms/").c_str(), 0755);
+
+    manifest.forEachConfiguration([&manifest, &masterDstRoot](const std::string& configName) {
+        auto                     config = manifest.configuration(configName);
+        std::vector<std::string> prodBomPaths;
+        std::vector<std::string> devBomPaths;
+
+        std::string runtimePath =  "/System/Library/Caches/com.apple.dyld/";
+        if (manifest.platform() == dyld3::Platform::macOS) {
+            runtimePath =  "/private/var/db/dyld/";
+        }
+
+        for (auto& arch : config.architectures) {
+            std::string cachePath = "dyld_shared_cache_" + arch.first;
+            prodBomPaths.push_back(cachePath);
+            if (manifest.platform() != dyld3::Platform::macOS) {
+                cachePath += ".development";
+            }
+            devBomPaths.push_back(cachePath);
+            char buffer[MAXPATHLEN];
+            sprintf(buffer, "%s/Boms/%s.prod.bom", masterDstRoot.c_str(), configName.c_str());
+            BOMBom bom = BOMBomNew(buffer);
+            for (auto& path : prodBomPaths) {
+                insertFileInBom(runtimePath + path, bom);
+            }
+            BOMBomFree(bom);
+
+            sprintf(buffer, "%s/Boms/%s.dev.bom", masterDstRoot.c_str(), configName.c_str());
+            bom = BOMBomNew(buffer);
+            for (auto& path : devBomPaths) {
+                insertFileInBom(runtimePath + path, bom);
+            }
+            BOMBomFree(bom);
+            
+            sprintf(buffer, "%s/Boms/%s.full.bom", masterDstRoot.c_str(), configName.c_str());
+            bom = BOMBomNew(buffer);
+            for (auto& path : prodBomPaths) {
+                insertFileInBom(runtimePath + path, bom);
+            }
+            for (auto& path : devBomPaths) {
+                insertFileInBom(runtimePath + path, bom);
+            }
+            BOMBomFree(bom);
+        }
+    });
+}
+
+bool build(Diagnostics& diags, dyld3::Manifest& manifest, const std::string& masterDstRoot, bool dedupe, bool verbose,
+           bool skipWrites, bool agileChooseSHA256CdHash)
+{
+    dispatch_queue_t                   queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
+    dispatch_queue_t                   warningQueue = dispatch_queue_create("com.apple.dyld.cache-builder.warnings", DISPATCH_QUEUE_SERIAL);
+    std::vector<std::set<std::string>> dedupedCacheSets;
+    if (dedupe) {
+        manifest.forEachConfiguration([&manifest, &dedupedCacheSets](const std::string& configName) {
+            auto config = manifest.configuration(configName);
+            bool dupeFound = false;
+            
+            for (auto& cacheSet : dedupedCacheSets) {
+                if (config == manifest.configuration(*cacheSet.begin())) {
+                    cacheSet.insert(configName);
+                    dupeFound = true;
+                    break;
+                }
+            }
+            
+            if (!dupeFound) {
+                std::set<std::string> temp;
+                temp.insert(configName);
+                dedupedCacheSets.push_back(temp);
+            }
+        });
+    } else {
+        manifest.forEachConfiguration([&manifest, &dedupedCacheSets](const std::string& configName) {
+            std::set<std::string> temp;
+            temp.insert(configName);
+            dedupedCacheSets.push_back(temp);
+        });
+    }
+    
+    std::vector<dyld3::BuildQueueEntry> buildQueue;
+    
+    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<uint8_t, CC_SHA1_DIGEST_LENGTH> 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());
+        
+        if (dedupe) {
+            for (auto& config : cacheSet) {
+                if (!skipWrites) {
+                    int err = symlink(("DedupedConfigs/" + fileName).c_str(), (masterDstRoot + "/" + config).c_str());
+                    if (err) {
+                        diags.warning("Could not create symlink '%s' -> 'DedupedConfigs/%s' (%d)", config.c_str(), fileName.c_str(), err);
+                    }
+                }
+            }
+        }
+        
+        manifest.configuration(*cacheSet.begin()).forEachArchitecture([&masterDstRoot, &dedupe, &fileName, &setName, &manifest, &buildQueue, &cacheSet, verbose](const std::string& arch) {
+            std::string configPath;
+            std::string runtimePath =  "/System/Library/Caches/com.apple.dyld/";
+            if (manifest.platform() == dyld3::Platform::macOS) {
+                runtimePath =  "/private/var/db/dyld/";
+            }
+            if (dedupe) {
+                configPath = masterDstRoot + "/DedupedConfigs/" + fileName + runtimePath;
+            } else {
+                configPath = masterDstRoot + runtimePath;
+            }
+
+            if (manifest.platform() == dyld3::Platform::macOS) {
+                buildQueue.push_back(manifest.makeQueueEntry(configPath + "dyld_shared_cache_" + arch, cacheSet, arch, false, setName + "/" + arch, verbose));
+            } else {
+                buildQueue.push_back(manifest.makeQueueEntry(configPath + "dyld_shared_cache_" + arch + ".development", cacheSet, arch, false, setName + "/" + arch, verbose));
+                buildQueue.push_back(manifest.makeQueueEntry(configPath + "dyld_shared_cache_" + arch, cacheSet, arch, true, setName + "/" + arch, verbose));
+            }
+        });
+    }
+
+    __block bool cacheBuildFailure = false;
+    __block std::set<std::string> warnings;
+    __block std::set<std::string> errors;
+
+    dispatch_sync(warningQueue, ^{
+        auto manifestWarnings = diags.warnings();
+        warnings.insert(manifestWarnings.begin(), manifestWarnings.end());
+    });
+
+    dispatch_apply(buildQueue.size(), queue, ^(size_t index) {
+        auto queueEntry = buildQueue[index];
+        pthread_setname_np(queueEntry.options.loggingPrefix.substr(0, MAXTHREADNAMESIZE - 1).c_str());
+        
+        DyldSharedCache::CreateResults results;
+        while (1) {
+            results = DyldSharedCache::create(queueEntry.options, queueEntry.dylibsForCache, queueEntry.otherDylibsAndBundles, queueEntry.mainExecutables);
+            if (!results.overflowed)
+                break;
+            auto evicted = manifest.removeLargestLeafDylib(queueEntry.configNames, queueEntry.options.archName);
+            if (evicted.empty())
+                break;
+            queueEntry = manifest.makeQueueEntry(queueEntry.outputPath, queueEntry.configNames, queueEntry.options.archName, queueEntry.options.optimizeStubs,  queueEntry.options.loggingPrefix, queueEntry.options.verbose);
+            dispatch_sync(warningQueue, ^{
+                warnings.insert("[WARNING] CACHE OVERFLOW: " + queueEntry.options.loggingPrefix + " evicted dylib: " + evicted);
+            });
+        }
+        dispatch_sync(warningQueue, ^{
+            warnings.insert(results.warnings.begin(), results.warnings.end());
+            bool chooseSecondCdHash = agileChooseSHA256CdHash;
+            if (agileChooseSHA256CdHash && !results.agileSignature) {
+                // Ignore this option for caches that are not signed agile (which is the majority).
+                chooseSecondCdHash = false;
+            }
+            for (const auto& configName : queueEntry.configNames) {
+                manifest.configuration(configName).architecture(queueEntry.options.archName).results.warnings = results.warnings;
+                if (queueEntry.options.optimizeStubs) {
+                    manifest.configuration(configName).architecture(queueEntry.options.archName)
+                    .results.developmentCache.cdHash = chooseSecondCdHash ? results.cdHashSecond : results.cdHashFirst;
+                } else {
+                    manifest.configuration(configName).architecture(queueEntry.options.archName)
+                    .results.productionCache.cdHash =  chooseSecondCdHash ? results.cdHashSecond : results.cdHashFirst;
+                }
+            }
+        });
+        if (!results.errorMessage.empty()) {
+            fprintf(stderr, "[%s] ERROR: %s\n", queueEntry.options.loggingPrefix.c_str(), results.errorMessage.c_str());
+        } else if (!skipWrites) {
+            dispatch_sync(write_queue, ^{
+                // save new cache file to disk and write new .map file
+                assert(results.cacheContent != nullptr);
+                mkpath_np(dirPath(queueEntry.outputPath).c_str(), 0755);
+                if (!safeSave(results.cacheContent, results.cacheLength, queueEntry.outputPath)) {
+                    cacheBuildFailure = true;
+                    fprintf(stderr, "[%s] ERROR: Could not write cache to: %s\n", queueEntry.options.loggingPrefix.c_str(), queueEntry.outputPath.c_str());
+                } else {
+                    fprintf(stderr, "[%s] Wrote cache to: %s\n", queueEntry.options.loggingPrefix.c_str(), queueEntry.outputPath.c_str());
+                    std::string mapStr = results.cacheContent->mapFile();
+                    std::string outFileMap = queueEntry.outputPath + ".map";
+                    safeSave(mapStr.c_str(), mapStr.size(), outFileMap);
+                }
+                // free created cache buffer
+                vm_deallocate(mach_task_self(), (vm_address_t)results.cacheContent, results.cacheLength);
+            });
+        } else {
+            fprintf(stderr, "[%s] Skipped writing cache to: %s\n", queueEntry.options.loggingPrefix.c_str(), queueEntry.outputPath.c_str());
+            vm_deallocate(mach_task_self(), (vm_address_t)results.cacheContent, results.cacheLength);
+        }
+    });
+    
+    // print any warnings
+    for (const std::string& warn : warnings) {
+        fprintf(stderr, "[WARNING] %s\n", warn.c_str());
+    }
+    
+    int err = sync_volume_np(masterDstRoot.c_str(), SYNC_VOLUME_FULLSYNC | SYNC_VOLUME_WAIT);
+    if (err) {
+        fprintf(stderr, "Volume sync failed errnor=%d (%s)\n", err, strerror(err));
+    }
+    
+    return !cacheBuildFailure;
+}
diff --git a/dyld3/shared-cache/CacheBuilder.cpp b/dyld3/shared-cache/CacheBuilder.cpp
new file mode 100644 (file)
index 0000000..b4fe35a
--- /dev/null
@@ -0,0 +1,1784 @@
+/* -*- 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 <unistd.h>
+#include <dirent.h>
+#include <sys/errno.h>
+#include <sys/fcntl.h>
+#include <sys/param.h>
+#include <mach/mach.h>
+#include <mach/mach_time.h>
+#include <mach-o/loader.h>
+#include <mach-o/fat.h>
+#include <mach/shared_region.h>
+#include <assert.h>
+#include <CommonCrypto/CommonHMAC.h>
+#include <CommonCrypto/CommonDigest.h>
+#include <CommonCrypto/CommonDigestSPI.h>
+#include <pthread/pthread.h>
+
+#include <string>
+#include <vector>
+#include <unordered_map>
+#include <unordered_set>
+
+#include "MachOParser.h"
+#include "CodeSigningTypes.h"
+#include "DyldSharedCache.h"
+#include "CacheBuilder.h"
+#include "FileAbstraction.hpp"
+#include "LaunchCacheWriter.h"
+#include "Trie.hpp"
+#include "Diagnostics.h"
+#include "ImageProxy.h"
+
+#if __has_include("dyld_cache_config.h")
+    #include "dyld_cache_config.h"
+#else
+    #define ARM_SHARED_REGION_START    0x1A000000ULL
+    #define ARM_SHARED_REGION_SIZE     0x26000000ULL
+    #define ARM64_SHARED_REGION_START 0x180000000ULL
+    #define ARM64_SHARED_REGION_SIZE   0x40000000ULL
+#endif
+
+const CacheBuilder::ArchLayout CacheBuilder::_s_archLayout[] = {
+    { 0x7FFF20000000ULL,         0xEFE00000ULL,             0x40000000, 0xFFFF000000000000, "x86_64",  0,          0,          0,          12, true,  true  },
+    { 0x7FFF20000000ULL,         0xEFE00000ULL,             0x40000000, 0xFFFF000000000000, "x86_64h", 0,          0,          0,          12, true,  true  },
+    { SHARED_REGION_BASE_I386,   SHARED_REGION_SIZE_I386,   0x00200000,                0x0, "i386",    0,          0,          0,          12, false, false },
+    { ARM64_SHARED_REGION_START, ARM64_SHARED_REGION_SIZE,  0x02000000, 0x00FFFF0000000000, "arm64",   0x0000C000, 0x00100000, 0x07F00000, 14, false, true  },
+    { ARM64_SHARED_REGION_START, ARM64_SHARED_REGION_SIZE,  0x02000000, 0x00FFFF0000000000, "arm64e",  0x0000C000, 0x00100000, 0x07F00000, 14, false, true  },
+    { ARM_SHARED_REGION_START,   ARM_SHARED_REGION_SIZE,    0x02000000,         0xE0000000, "armv7s",  0,          0,          0,          14, false, false },
+    { ARM_SHARED_REGION_START,   ARM_SHARED_REGION_SIZE,    0x00400000,         0xE0000000, "armv7k",  0,          0,          0,          14, false, false },
+    { 0x40000000,                0x40000000,                0x02000000,                0x0, "sim-x86", 0,          0,          0,          14, false, false }
+};
+
+
+// These are dylibs that may be interposed, so stubs calling into them should never be bypassed
+const char* const CacheBuilder::_s_neverStubEliminate[] = {
+    "/usr/lib/system/libdispatch.dylib",
+    nullptr
+};
+
+
+CacheBuilder::CacheBuilder(const DyldSharedCache::CreateOptions& options)
+    : _options(options)
+    , _buffer(nullptr)
+    , _diagnostics(options.loggingPrefix, options.verbose)
+    , _archLayout(nullptr)
+    , _aliasCount(0)
+    , _slideInfoFileOffset(0)
+    , _slideInfoBufferSizeAllocated(0)
+    , _allocatedBufferSize(0)
+    , _currentFileSize(0)
+    , _vmSize(0)
+    , _branchPoolsLinkEditStartAddr(0)
+{
+
+    std::string targetArch = options.archName;
+    if ( options.forSimulator && (options.archName == "i386") )
+        targetArch = "sim-x86";
+
+    for (const ArchLayout& layout : _s_archLayout) {
+        if ( layout.archName == targetArch ) {
+            _archLayout = &layout;
+            break;
+        }
+    }
+}
+
+
+std::string CacheBuilder::errorMessage()
+{
+    return _diagnostics.errorMessage();
+}
+
+const std::set<std::string> CacheBuilder::warnings()
+{
+    return _diagnostics.warnings();
+}
+
+void CacheBuilder::deleteBuffer()
+{
+    vm_deallocate(mach_task_self(), (vm_address_t)_buffer, _allocatedBufferSize);
+    _buffer = nullptr;
+    _allocatedBufferSize = 0;
+}
+
+std::vector<DyldSharedCache::MappedMachO>
+CacheBuilder::makeSortedDylibs(const std::vector<DyldSharedCache::MappedMachO>& dylibs, const std::unordered_map<std::string, unsigned> sortOrder)
+{
+    std::vector<DyldSharedCache::MappedMachO> sortedDylibs = dylibs;
+
+    std::sort(sortedDylibs.begin(), sortedDylibs.end(), [&](const DyldSharedCache::MappedMachO& a, const DyldSharedCache::MappedMachO& b) {
+        const auto& orderA = sortOrder.find(a.runtimePath);
+        const auto& orderB = sortOrder.find(b.runtimePath);
+        bool foundA = (orderA != sortOrder.end());
+        bool foundB = (orderB != sortOrder.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 a.runtimePath < b.runtimePath;
+    });
+
+    return sortedDylibs;
+}
+
+
+inline uint32_t absolutetime_to_milliseconds(uint64_t abstime)
+{
+    return (uint32_t)(abstime/1000/1000);
+}
+
+struct DylibAndSize
+{
+    const char*     installName;
+    uint64_t        size;
+};
+
+bool CacheBuilder::cacheOverflow(const dyld_cache_mapping_info regions[3])
+{
+    if ( _archLayout->sharedRegionsAreDiscontiguous ) {
+        // for macOS x86_64 cache, need to check each region for overflow
+        return ( (regions[0].size > 0x60000000) || (regions[1].size > 0x40000000) || (regions[2].size > 0x3FE00000) );
+    }
+    else {
+        return (_vmSize > _archLayout->sharedMemorySize);
+    }
+}
+
+bool CacheBuilder::build(const std::vector<DyldSharedCache::MappedMachO>& dylibs,
+                         const std::vector<DyldSharedCache::MappedMachO>& otherOsDylibsInput,
+                         const std::vector<DyldSharedCache::MappedMachO>& osExecutables)
+{
+    // <rdar://problem/21317611> error out instead of crash if cache has no dylibs
+    // FIXME: plist should specify required vs optional dylibs
+    if ( dylibs.size() < 30 ) {
+        _diagnostics.error("missing required minimum set of dylibs");
+        return false;
+    }
+    uint64_t t1 = mach_absolute_time();
+
+
+    // make copy of dylib list and sort
+    std::vector<DyldSharedCache::MappedMachO> sortedDylibs = makeSortedDylibs(dylibs, _options.dylibOrdering);
+    std::vector<DyldSharedCache::MappedMachO> otherOsDylibs = otherOsDylibsInput;
+
+    // assign addresses for each segment of each dylib in new cache
+    dyld_cache_mapping_info regions[3];
+    SegmentMapping segmentMapping = assignSegmentAddresses(sortedDylibs, regions);
+    if ( cacheOverflow(regions) ) {
+        if ( !_options.evictLeafDylibsOnOverflow ) {
+            _diagnostics.error("cache overflow: %lluMB (max %lluMB)", _vmSize / 1024 / 1024, (_archLayout->sharedMemorySize) / 1024 / 1024);
+            return false;
+        }
+        // find all leaf (not referenced by anything else in cache) dylibs
+
+        // build count of how many references there are to each dylib
+        __block std::map<std::string, unsigned int> referenceCount;
+        for (const DyldSharedCache::MappedMachO& dylib : sortedDylibs) {
+            dyld3::MachOParser parser(dylib.mh);
+            parser.forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool &stop) {
+                referenceCount[loadPath] += 1;
+            });
+        }
+
+        // find all dylibs not referenced
+        std::vector<DylibAndSize> unreferencedDylibs;
+        for (const DyldSharedCache::MappedMachO& dylib : sortedDylibs) {
+            dyld3::MachOParser parser(dylib.mh);
+            const char* installName = parser.installName();
+            if ( referenceCount.count(installName) == 0 ) {
+                // conservative: sum up all segments except LINKEDIT
+                __block uint64_t segsSize = 0;
+                parser.forEachSegment(^(const char* segName, uint32_t fileOffset, uint32_t fileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, bool &stop) {
+                    if ( strcmp(segName, "__LINKEDIT") != 0 )
+                        segsSize += vmSize;
+                });
+                unreferencedDylibs.push_back({installName, segsSize});
+            }
+        }
+        // sort leaf dylibs by size
+        std::sort(unreferencedDylibs.begin(), unreferencedDylibs.end(), [&](const DylibAndSize& a, const DylibAndSize& b) {
+            return ( a.size > b.size );
+        });
+
+        // build set of dylibs that if removed will allow cache to build
+        uint64_t reductionTarget = _vmSize - _archLayout->sharedMemorySize;
+        std::set<std::string> toRemove;
+        for (DylibAndSize& dylib : unreferencedDylibs) {
+            if ( _options.verbose )
+                _diagnostics.warning("to prevent cache overflow, not caching %s", dylib.installName);
+            toRemove.insert(dylib.installName);
+            if ( dylib.size > reductionTarget )
+                break;
+            reductionTarget -= dylib.size;
+        }
+        // transfer overflow dylibs from cached vector to other vector
+        for (const std::string& installName : toRemove) {
+            for (std::vector<DyldSharedCache::MappedMachO>::iterator it=sortedDylibs.begin(); it != sortedDylibs.end(); ++it) {
+                dyld3::MachOParser parser(it->mh);
+                if ( installName == parser.installName() ) {
+                    otherOsDylibs.push_back(*it);
+                    sortedDylibs.erase(it);
+                    break;
+                }
+            }
+        }
+        // re-layout cache
+        segmentMapping = assignSegmentAddresses(sortedDylibs, regions);
+        if ( cacheOverflow(regions) ) {
+            _diagnostics.error("cache overflow, tried evicting %ld leaf daylibs, but still too big: %lluMB (max %lluMB)",
+                               toRemove.size(), _vmSize / 1024 / 1024, (_archLayout->sharedMemorySize) / 1024 / 1024);
+            return false;
+        }
+    }
+
+    // allocate buffer for new cache
+    _allocatedBufferSize = std::max(_currentFileSize, (uint64_t)0x100000)*1.1; // add 10% to allocation to support large closures
+    if ( vm_allocate(mach_task_self(), (vm_address_t*)&_buffer, _allocatedBufferSize, VM_FLAGS_ANYWHERE) != 0 ) {
+        _diagnostics.error("could not allocate buffer");
+        return false;
+    }
+    _currentFileSize = _allocatedBufferSize;
+
+    // write unoptimized cache
+    writeCacheHeader(regions, sortedDylibs, segmentMapping);
+    copyRawSegments(sortedDylibs, segmentMapping);
+    adjustAllImagesForNewSegmentLocations(sortedDylibs, segmentMapping);
+    if ( _diagnostics.hasError() )
+        return false;
+
+    bindAllImagesInCacheFile(regions);
+    if ( _diagnostics.hasError() )
+        return false;
+
+    // optimize ObjC
+    if ( _options.optimizeObjC )
+        optimizeObjC(_buffer, _archLayout->is64, _options.optimizeStubs, _pointersForASLR, _diagnostics);
+    if ( _diagnostics.hasError() )
+        return false;
+
+    // optimize away stubs
+    std::vector<uint64_t> branchPoolOffsets;
+    uint64_t cacheStartAddress = _archLayout->sharedMemoryStart;
+    if ( _options.optimizeStubs ) {
+        std::vector<uint64_t> branchPoolStartAddrs;
+        const uint64_t* p = (uint64_t*)((uint8_t*)_buffer + _buffer->header.branchPoolsOffset);
+        for (int i=0; i < _buffer->header.branchPoolsCount; ++i) {
+            uint64_t poolAddr = p[i];
+            branchPoolStartAddrs.push_back(poolAddr);
+            branchPoolOffsets.push_back(poolAddr - cacheStartAddress);
+        }
+        bypassStubs(_buffer, branchPoolStartAddrs, _s_neverStubEliminate, _diagnostics);
+    }
+    uint64_t t2 = mach_absolute_time();
+
+    // FIPS seal corecrypto, This must be done after stub elimination (so that
+    // __TEXT,__text is not changed after sealing), but before LINKEDIT
+    // optimization  (so that we still have access to local symbols)
+    fipsSign();
+
+    // merge and compact LINKEDIT segments
+    dyld_cache_local_symbols_info* localsInfo = nullptr;
+    if ( dylibs.size() == 0 )
+        _currentFileSize = 0x1000;
+    else
+        _currentFileSize = optimizeLinkedit(_buffer, _archLayout->is64, _options.excludeLocalSymbols, _options.optimizeStubs, branchPoolOffsets, _diagnostics, &localsInfo);
+
+    uint64_t t3 = mach_absolute_time();
+
+    // add ImageGroup for all dylibs in cache
+    __block std::vector<DyldSharedCache::MappedMachO> cachedDylibs;
+    std::unordered_map<std::string, const DyldSharedCache::MappedMachO*> mapIntoSortedDylibs;
+    for (const DyldSharedCache::MappedMachO& entry : sortedDylibs) {
+        mapIntoSortedDylibs[entry.runtimePath] = &entry;
+    }
+    _buffer->forEachImage(^(const mach_header* mh, const char* installName) {
+        auto pos = mapIntoSortedDylibs.find(installName);
+        if ( pos != mapIntoSortedDylibs.end() ) {
+            DyldSharedCache::MappedMachO newEntry = *(pos->second);
+            newEntry.mh = mh;
+            cachedDylibs.push_back(newEntry);
+        }
+        else {
+            bool found = false;
+            for (const std::string& prefix :  _options.pathPrefixes) {
+                std::string fullPath = prefix + installName;
+                char resolvedPath[PATH_MAX];
+                if ( realpath(fullPath.c_str(), resolvedPath) != nullptr ) {
+                    std::string resolvedUnPrefixed = &resolvedPath[prefix.size()];
+                    pos = mapIntoSortedDylibs.find(resolvedUnPrefixed);
+                    if ( pos != mapIntoSortedDylibs.end() ) {
+                        DyldSharedCache::MappedMachO newEntry = *(pos->second);
+                        newEntry.mh = mh;
+                        cachedDylibs.push_back(newEntry);
+                        found = true;
+                   }
+                }
+            }
+            if ( !found )
+                fprintf(stderr, "missing mapping for %s\n", installName);
+        }
+    });
+    dyld3::DyldCacheParser dyldCacheParser(_buffer, true);
+    dyld3::ImageProxyGroup* dylibGroup = dyld3::ImageProxyGroup::makeDyldCacheDylibsGroup(_diagnostics, dyldCacheParser, cachedDylibs,
+                                                                                          _options.pathPrefixes, _patchTable,
+                                                                                          _options.optimizeStubs, !_options.dylibsRemovedDuringMastering);
+    if ( _diagnostics.hasError() )
+        return false;
+    addCachedDylibsImageGroup(dylibGroup);
+    if ( _diagnostics.hasError() )
+        return false;
+
+    uint64_t t4 = mach_absolute_time();
+
+    // add ImageGroup for other OS dylibs and bundles
+    dyld3::ImageProxyGroup* otherGroup = dyld3::ImageProxyGroup::makeOtherOsGroup(_diagnostics, dyldCacheParser, dylibGroup, otherOsDylibs,
+                                                                                  _options.inodesAreSameAsRuntime, _options.pathPrefixes);
+    if ( _diagnostics.hasError() )
+        return false;
+    addCachedOtherDylibsImageGroup(otherGroup);
+    if ( _diagnostics.hasError() )
+        return false;
+
+    uint64_t t5 = mach_absolute_time();
+
+    // compute and add launch closures
+    std::map<std::string, const dyld3::launch_cache::binary_format::Closure*> closures;
+    for (const DyldSharedCache::MappedMachO& mainProg : osExecutables) {
+        Diagnostics clsDiag;
+        const dyld3::launch_cache::binary_format::Closure* cls = dyld3::ImageProxyGroup::makeClosure(clsDiag, dyldCacheParser, dylibGroup, otherGroup, mainProg,
+                                                                                                     _options.inodesAreSameAsRuntime, _options.pathPrefixes);
+        if ( clsDiag.hasError() ) {
+            // if closure cannot be built, silently skip it, unless in verbose mode
+            if ( _options.verbose ) {
+                _diagnostics.warning("building closure for '%s': %s", mainProg.runtimePath.c_str(), clsDiag.errorMessage().c_str());
+                for (const std::string& warn : clsDiag.warnings() )
+                    _diagnostics.warning("%s", warn.c_str());
+            }
+        }
+        else {
+            closures[mainProg.runtimePath] = cls;
+       }
+    }
+    addClosures(closures);
+    if ( _diagnostics.hasError() )
+        return false;
+
+    uint64_t t6 = mach_absolute_time();
+
+    // fill in slide info at start of region[2]
+    // do this last because it modifies pointers in DATA segments
+    if ( _options.cacheSupportsASLR ) {
+        if ( _archLayout->is64 )
+            writeSlideInfoV2<Pointer64<LittleEndian>>();
+        else
+            writeSlideInfoV2<Pointer32<LittleEndian>>();
+    }
+
+    uint64_t t7 = mach_absolute_time();
+
+    // update last region size
+    dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((char*)_buffer + _buffer->header.mappingOffset);
+    _currentFileSize = align(_currentFileSize, _archLayout->sharedRegionAlignP2);
+    mappings[2].size = _currentFileSize - mappings[2].fileOffset;
+
+    // record cache bounds
+    _buffer->header.sharedRegionStart = _archLayout->sharedMemoryStart;
+    _buffer->header.sharedRegionSize  = _archLayout->sharedMemorySize;
+    if ( _archLayout->sharedRegionsAreDiscontiguous ) {
+        // special case x86_64 which has three non-contiguous chunks each in their own 1GB regions
+        uint64_t maxSlide0 = 0x60000000 - mappings[0].size; // TEXT region has 1.5GB region
+        uint64_t maxSlide1 = 0x40000000 - mappings[1].size;
+        uint64_t maxSlide2 = 0x3FE00000 - mappings[2].size;
+        _buffer->header.maxSlide = std::min(std::min(maxSlide0, maxSlide1), maxSlide2);
+    }
+    else {
+        _buffer->header.maxSlide = (_archLayout->sharedMemoryStart + _archLayout->sharedMemorySize) - (mappings[2].address + mappings[2].size);
+    }
+
+    // append "unmapped" local symbols region
+    if ( _options.excludeLocalSymbols ) {
+        size_t localsInfoSize = align(localsInfo->stringsOffset + localsInfo->stringsSize, _archLayout->sharedRegionAlignP2);
+        if ( _currentFileSize + localsInfoSize > _allocatedBufferSize ) {
+            _diagnostics.warning("local symbols omitted because cache buffer overflow");
+        }
+        else {
+            memcpy((char*)_buffer+_currentFileSize, localsInfo, localsInfoSize);
+            _buffer->header.localSymbolsOffset = _currentFileSize;
+            _buffer->header.localSymbolsSize   = localsInfoSize;
+            _currentFileSize += localsInfoSize;
+        }
+        free((void*)localsInfo);
+    }
+
+    recomputeCacheUUID();
+
+    // Calculate the VMSize of the resulting cache
+    __block uint64_t endAddr = 0;
+    _buffer->forEachRegion(^(const void* content, uint64_t vmAddr, uint64_t size, uint32_t permissions) {
+        if (vmAddr+size > endAddr)
+            endAddr = vmAddr+size;
+    });
+    _vmSize = endAddr - cacheStartAddress;
+
+    // last sanity check on size
+    if ( _vmSize > _archLayout->sharedMemorySize ) {
+        _diagnostics.error("cache overflow after optimizations.  %lluMB (max %lluMB)", _vmSize / 1024 / 1024, (_archLayout->sharedMemorySize) / 1024 / 1024);
+        return true;
+    }
+
+    // codesignature is part of file, but is not mapped
+    codeSign();
+    if ( _diagnostics.hasError() )
+        return false;
+
+    uint64_t t8 = mach_absolute_time();
+
+    if ( _options.verbose ) {
+        fprintf(stderr, "time to copy and bind cached dylibs: %ums\n", absolutetime_to_milliseconds(t2-t1));
+        fprintf(stderr, "time to optimize LINKEDITs: %ums\n", absolutetime_to_milliseconds(t3-t2));
+        fprintf(stderr, "time to build ImageGroup of %lu cached dylibs: %ums\n", sortedDylibs.size(), absolutetime_to_milliseconds(t4-t3));
+        fprintf(stderr, "time to build ImageGroup of %lu other dylibs: %ums\n", otherOsDylibs.size(), absolutetime_to_milliseconds(t5-t4));
+        fprintf(stderr, "time to build %lu closures: %ums\n", osExecutables.size(), absolutetime_to_milliseconds(t6-t5));
+        fprintf(stderr, "time to compute slide info: %ums\n", absolutetime_to_milliseconds(t7-t6));
+        fprintf(stderr, "time to compute UUID and codesign cache file: %ums\n", absolutetime_to_milliseconds(t8-t7));
+    }
+
+    // trim over allocated buffer
+    if ( _allocatedBufferSize > _currentFileSize ) {
+        uint8_t* startOfUnused  = (uint8_t*)_buffer+_currentFileSize;
+        size_t unusedLen = _allocatedBufferSize-_currentFileSize;
+        vm_deallocate(mach_task_self(), (vm_address_t)startOfUnused, unusedLen);
+        _allocatedBufferSize = _currentFileSize;
+    }
+
+    return false;
+}
+
+
+void CacheBuilder::writeCacheHeader(const dyld_cache_mapping_info regions[3], const std::vector<DyldSharedCache::MappedMachO>& dylibs, const SegmentMapping& segmentMappings)
+{
+    // "dyld_v1" + spaces + archName(), with enough spaces to pad to 15 bytes
+    std::string magic = "dyld_v1";
+    magic.append(15 - magic.length() - _options.archName.length(), ' ');
+    magic.append(_options.archName);
+    assert(magic.length() == 15);
+
+    // fill in header
+    memcpy(_buffer->header.magic, magic.c_str(), 16);
+    _buffer->header.mappingOffset      = sizeof(dyld_cache_header);
+    _buffer->header.mappingCount       = 3;
+    _buffer->header.imagesOffset       = (uint32_t)(_buffer->header.mappingOffset + 3*sizeof(dyld_cache_mapping_info) + sizeof(uint64_t)*_branchPoolStarts.size());
+    _buffer->header.imagesCount        = (uint32_t)dylibs.size() + _aliasCount;
+    _buffer->header.dyldBaseAddress    = 0;
+    _buffer->header.codeSignatureOffset= 0;
+    _buffer->header.codeSignatureSize  = 0;
+    _buffer->header.slideInfoOffset    = _slideInfoFileOffset;
+    _buffer->header.slideInfoSize      = _slideInfoBufferSizeAllocated;
+    _buffer->header.localSymbolsOffset = 0;
+    _buffer->header.localSymbolsSize   = 0;
+    _buffer->header.cacheType          = _options.optimizeStubs ? kDyldSharedCacheTypeProduction : kDyldSharedCacheTypeDevelopment;
+    _buffer->header.accelerateInfoAddr = 0;
+    _buffer->header.accelerateInfoSize = 0;
+    bzero(_buffer->header.uuid, 16);    // overwritten later by recomputeCacheUUID()
+    _buffer->header.branchPoolsOffset    = _buffer->header.mappingOffset + 3*sizeof(dyld_cache_mapping_info);
+    _buffer->header.branchPoolsCount     = (uint32_t)_branchPoolStarts.size();
+    _buffer->header.imagesTextOffset     = _buffer->header.imagesOffset + sizeof(dyld_cache_image_info)*_buffer->header.imagesCount;
+    _buffer->header.imagesTextCount      = dylibs.size();
+    _buffer->header.platform             = (uint8_t)_options.platform;
+    _buffer->header.formatVersion        = dyld3::launch_cache::binary_format::kFormatVersion;
+    _buffer->header.dylibsExpectedOnDisk = !_options.dylibsRemovedDuringMastering;
+    _buffer->header.simulator            = _options.forSimulator;
+
+    // fill in mappings
+    dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((char*)_buffer + _buffer->header.mappingOffset);
+    mappings[0] = regions[0];
+    mappings[1] = regions[1];
+    mappings[2] = regions[2];
+
+    // fill in branch pool addresses
+    uint64_t* p = (uint64_t*)((char*)_buffer + _buffer->header.branchPoolsOffset);
+    for (uint64_t pool : _branchPoolStarts) {
+        *p++ = pool;
+    }
+
+    // fill in image table
+    dyld_cache_image_info* images = (dyld_cache_image_info*)((char*)_buffer + _buffer->header.imagesOffset);
+    for (const DyldSharedCache::MappedMachO& dylib : dylibs) {
+        const std::vector<SegmentMappingInfo>& segs = segmentMappings.at(dylib.mh);
+        dyld3::MachOParser parser(dylib.mh);
+        const char* installName = parser.installName();
+        images->address = segs[0].dstCacheAddress;
+        if ( _options.dylibsRemovedDuringMastering ) {
+            images->modTime = 0;
+            images->inode   = pathHash(installName);
+        }
+        else {
+            images->modTime = dylib.modTime;
+            images->inode   = dylib.inode;
+        }
+        uint32_t installNameOffsetInTEXT =  (uint32_t)(installName - (char*)dylib.mh);
+        images->pathFileOffset = (uint32_t)segs[0].dstCacheOffset + installNameOffsetInTEXT;
+        ++images;
+    }
+    // append aliases image records and strings
+/*
+    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->modTime = dylib->lastModTime;
+                    images->inode = dylib->inode;
+                }
+                else {
+                    images->modTime = 0;
+                    images->inode = pathHash(alias.c_str());
+                }
+                images->pathFileOffset = offset;
+                //fprintf(stderr, "adding alias %s for %s\n", alias.c_str(), dylib->installName.c_str());
+                ::strcpy((char*)&_buffer[offset], alias.c_str());
+                offset += alias.size() + 1;
+                ++images;
+            }
+        }
+    }
+*/
+    // calculate start of text image array and trailing string pool
+    dyld_cache_image_text_info* textImages = (dyld_cache_image_text_info*)((char*)_buffer + _buffer->header.imagesTextOffset);
+    uint32_t stringOffset = (uint32_t)(_buffer->header.imagesTextOffset + sizeof(dyld_cache_image_text_info) * dylibs.size());
+
+    // write text image array and image names pool at same time
+    for (const DyldSharedCache::MappedMachO& dylib : dylibs) {
+        const std::vector<SegmentMappingInfo>& segs = segmentMappings.at(dylib.mh);
+        dyld3::MachOParser parser(dylib.mh);
+        parser.getUuid(textImages->uuid);
+        textImages->loadAddress     = segs[0].dstCacheAddress;
+        textImages->textSegmentSize = (uint32_t)segs[0].dstCacheSegmentSize;
+        textImages->pathOffset      = stringOffset;
+        const char* installName = parser.installName();
+        ::strcpy((char*)_buffer + stringOffset, installName);
+        stringOffset += (uint32_t)strlen(installName)+1;
+        ++textImages;
+    }
+
+    // make sure header did not overflow into first mapped image
+    const dyld_cache_image_info* firstImage = (dyld_cache_image_info*)((char*)_buffer + _buffer->header.imagesOffset);
+    assert(stringOffset <= (firstImage->address - mappings[0].address));
+}
+
+
+void CacheBuilder::copyRawSegments(const std::vector<DyldSharedCache::MappedMachO>& dylibs, const SegmentMapping& mapping)
+{
+    uint8_t* cacheBytes = (uint8_t*)_buffer;
+    for (const DyldSharedCache::MappedMachO& dylib : dylibs) {
+        auto pos = mapping.find(dylib.mh);
+        assert(pos != mapping.end());
+        for (const SegmentMappingInfo& info : pos->second) {
+            //fprintf(stderr, "copy %s segment %s (0x%08X bytes) from %p to %p (logical addr 0x%llX) for %s\n", _options.archName.c_str(), info.segName, info.copySegmentSize, info.srcSegment, &cacheBytes[info.dstCacheOffset], info.dstCacheAddress, dylib.runtimePath.c_str());
+            ::memcpy(&cacheBytes[info.dstCacheOffset], info.srcSegment, info.copySegmentSize);
+        }
+    }
+}
+
+void CacheBuilder::adjustAllImagesForNewSegmentLocations(const std::vector<DyldSharedCache::MappedMachO>& dylibs, const SegmentMapping& mapping)
+{
+    uint8_t* cacheBytes = (uint8_t*)_buffer;
+    for (const DyldSharedCache::MappedMachO& dylib : dylibs) {
+        auto pos = mapping.find(dylib.mh);
+        assert(pos != mapping.end());
+        mach_header* mhInCache = (mach_header*)&cacheBytes[pos->second[0].dstCacheOffset];
+        adjustDylibSegments(_buffer, _archLayout->is64, mhInCache, pos->second, _pointersForASLR, _diagnostics);
+        if ( _diagnostics.hasError() )
+            break;
+    }
+}
+
+struct Counts {
+    unsigned long lazyCount    = 0;
+    unsigned long nonLazyCount = 0;
+};
+
+void CacheBuilder::bindAllImagesInCacheFile(const dyld_cache_mapping_info regions[3])
+{
+    const bool log = false;
+    __block std::unordered_map<std::string, Counts> useCounts;
+
+    // build map of install names to mach_headers
+    __block std::unordered_map<std::string, const mach_header*> installNameToMH;
+    __block std::vector<const mach_header*> dylibMHs;
+    _buffer->forEachImage(^(const mach_header* mh, const char* installName) {
+        installNameToMH[installName] = mh;
+        dylibMHs.push_back(mh);
+    });
+
+    __block Diagnostics parsingDiag;
+    bool (^dylibFinder)(uint32_t, const char*, void* , const mach_header**, void**) = ^(uint32_t depIndex, const char* depLoadPath, void* extra, const mach_header** foundMH, void** foundExtra) {
+        auto pos = installNameToMH.find(depLoadPath);
+        if ( pos != installNameToMH.end() ) {
+            *foundMH = pos->second;
+            *foundExtra = nullptr;
+            return true;
+        }
+        parsingDiag.error("dependent dylib %s not found", depLoadPath);
+        return false;
+    };
+    if ( parsingDiag.hasError() ) {
+        _diagnostics.error("%s", parsingDiag.errorMessage().c_str());
+        return;
+    }
+
+    // bind every dylib in cache
+    for (const mach_header* mh : dylibMHs) {
+        dyld3::MachOParser parser(mh, true);
+        bool is64 = parser.is64();
+        const char* depPaths[256];
+        const char** depPathsArray = depPaths;
+        __block int depIndex = 1;
+        parser.forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool& stop) {
+            depPathsArray[depIndex++] = loadPath;
+        });
+        uint8_t*  segCacheStarts[10];
+        uint64_t  segCacheAddrs[10];
+        uint8_t** segCacheStartsArray = segCacheStarts;
+        uint64_t* segCacheAddrsArray  = segCacheAddrs;
+        __block int segIndex = 0;
+        parser.forEachSegment(^(const char* segName, uint32_t fileOffset, uint32_t fileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, bool& stop) {
+             segCacheStartsArray[segIndex] = (segIndex == 0) ? (uint8_t*)mh : (uint8_t*)_buffer + fileOffset;
+             segCacheAddrsArray[segIndex] = vmAddr;
+             ++segIndex;
+        });
+        __block Diagnostics bindingDiag;
+        parser.forEachBind(bindingDiag, ^(uint32_t dataSegIndex, uint64_t dataSegOffset, uint8_t type, int libOrdinal, uint64_t addend, const char* symbolName, bool weakImport, bool lazy, bool& stop) {
+            if ( log ) {
+                if ( lazy )
+                    useCounts[symbolName].lazyCount += 1;
+                else
+                    useCounts[symbolName].nonLazyCount += 1;
+            }
+            const mach_header* targetMH = nullptr;
+            if ( libOrdinal == BIND_SPECIAL_DYLIB_SELF ) {
+                targetMH = mh;
+            }
+            else if ( libOrdinal == BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE ) {
+                parsingDiag.error("bind ordinal BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE not supported in dylibs in dyld shared cache (found in %s)", parser.installName());
+                stop = true;
+                return;
+            }
+            else if ( libOrdinal == BIND_SPECIAL_DYLIB_FLAT_LOOKUP ) {
+                parsingDiag.error("bind ordinal BIND_SPECIAL_DYLIB_FLAT_LOOKUP not supported in dylibs in dyld shared cache (found in %s)", parser.installName());
+                stop = true;
+                return;
+            }
+            else {
+                const char* fromPath = depPathsArray[libOrdinal];
+                auto pos = installNameToMH.find(fromPath);
+                if (pos == installNameToMH.end()) {
+                    if (!weakImport) {
+                        _diagnostics.error("dependent dylib %s not found", fromPath);
+                    }
+                    return;
+                }
+                targetMH = pos->second;
+            }
+            dyld3::MachOParser targetParser(targetMH, true);
+            dyld3::MachOParser::FoundSymbol foundInfo;
+            uint64_t targetValue = 0;
+            uint8_t* fixupLoc = segCacheStartsArray[dataSegIndex] + dataSegOffset;
+            if ( targetParser.findExportedSymbol(parsingDiag, symbolName, nullptr, foundInfo, dylibFinder) ) {
+                const mach_header* foundInMH = foundInfo.foundInDylib;
+                dyld3::MachOParser foundInParser(foundInMH, true);
+                uint64_t foundInBaseAddress = foundInParser.preferredLoadAddress();
+                switch ( foundInfo.kind ) {
+                    case dyld3::MachOParser::FoundSymbol::Kind::resolverOffset:
+                        // Bind to the target stub for resolver based functions.
+                        // There may be a later optimization to alter the client
+                        // stubs to directly to the target stub's lazy pointer.
+                    case dyld3::MachOParser::FoundSymbol::Kind::headerOffset:
+                        targetValue = foundInBaseAddress + foundInfo.value + addend;
+                        _pointersForASLR.push_back((void*)fixupLoc);
+                        if ( foundInMH != mh ) {
+                            uint32_t mhVmOffset                 = (uint32_t)((uint8_t*)foundInMH - (uint8_t*)_buffer);
+                            uint32_t definitionCacheVmOffset    = (uint32_t)(mhVmOffset + foundInfo.value);
+                            uint32_t referenceCacheDataVmOffset = (uint32_t)(segCacheAddrsArray[dataSegIndex] + dataSegOffset - regions[1].address);
+                            assert(referenceCacheDataVmOffset < (1<<30));
+                            dyld3::launch_cache::binary_format::PatchOffset entry;
+                            entry.last              = false;
+                            entry.hasAddend         = (addend != 0);
+                            entry.dataRegionOffset  = referenceCacheDataVmOffset;
+                            _patchTable[foundInMH][definitionCacheVmOffset].insert(*((uint32_t*)&entry));
+                        }
+                       break;
+                    case dyld3::MachOParser::FoundSymbol::Kind::absolute:
+                        // pointers set to absolute values are not slid
+                        targetValue = foundInfo.value + addend;
+                        break;
+                }
+            }
+            else if ( weakImport ) {
+                // weak pointers set to zero are not slid
+                targetValue = 0;
+            }
+            else {
+                parsingDiag.error("cannot find symbol %s, needed in dylib %s", symbolName, parser.installName());
+                stop = true;
+            }
+            switch ( type ) {
+                case BIND_TYPE_POINTER:
+                    if ( is64 )
+                        *((uint64_t*)fixupLoc) = targetValue;
+                    else
+                        *((uint32_t*)fixupLoc) = (uint32_t)targetValue;
+                    break;
+                case BIND_TYPE_TEXT_ABSOLUTE32:
+                case BIND_TYPE_TEXT_PCREL32:
+                    parsingDiag.error("text relocs not supported for shared cache binding in %s", parser.installName());
+                    stop = true;
+                    break;
+                default:
+                    parsingDiag.error("bad bind type (%d) in %s", type, parser.installName());
+                    stop = true;
+                    break;
+
+            }
+        });
+        if ( bindingDiag.hasError() ) {
+            parsingDiag.error("%s in dylib %s", bindingDiag.errorMessage().c_str(), parser.installName());
+        }
+        if ( parsingDiag.hasError() )
+            break;
+        // also need to add patch locations for weak-binds that point within same image, since they are not captured by binds above
+        parser.forEachWeakDef(bindingDiag, ^(bool strongDef, uint32_t dataSegIndex, uint64_t dataSegOffset, uint64_t addend, const char* symbolName, bool &stop) {
+            if ( strongDef )
+                return;
+            uint8_t* fixupLoc = segCacheStartsArray[dataSegIndex] + dataSegOffset;
+            dyld3::MachOParser::FoundSymbol weakFoundInfo;
+            Diagnostics weakLookupDiag;
+            if ( parser.findExportedSymbol(weakLookupDiag, symbolName, nullptr, weakFoundInfo, nullptr) ) {
+                // this is an interior pointing (rebased) pointer
+                uint64_t targetValue;
+                if ( is64 )
+                    targetValue = *((uint64_t*)fixupLoc);
+                else
+                    targetValue = *((uint32_t*)fixupLoc);
+                uint32_t definitionCacheVmOffset    = (uint32_t)(targetValue - regions[0].address);
+                uint32_t referenceCacheDataVmOffset = (uint32_t)(segCacheAddrsArray[dataSegIndex] + dataSegOffset - regions[1].address);
+                assert(referenceCacheDataVmOffset < (1<<30));
+                dyld3::launch_cache::binary_format::PatchOffset entry;
+                entry.last              = false;
+                entry.hasAddend         = (addend != 0);
+                entry.dataRegionOffset  = referenceCacheDataVmOffset;
+                _patchTable[mh][definitionCacheVmOffset].insert(*((uint32_t*)&entry));
+            }
+        });
+        if ( bindingDiag.hasError() ) {
+            parsingDiag.error("%s in dylib %s", bindingDiag.errorMessage().c_str(), parser.installName());
+        }
+        if ( parsingDiag.hasError() )
+            break;
+    }
+
+    if ( log ) {
+        unsigned lazyCount = 0;
+        unsigned nonLazyCount = 0;
+        std::unordered_set<std::string> lazyTargets;
+        for (auto entry : useCounts) {
+            fprintf(stderr, "% 3ld      % 3ld     %s\n", entry.second.lazyCount, entry.second.nonLazyCount, entry.first.c_str());
+            lazyCount += entry.second.lazyCount;
+            nonLazyCount += entry.second.nonLazyCount;
+            if ( entry.second.lazyCount != 0 )
+                lazyTargets.insert(entry.first);
+        }
+        fprintf(stderr, "lazyCount = %d\n", lazyCount);
+        fprintf(stderr, "nonLazyCount = %d\n", nonLazyCount);
+        fprintf(stderr, "unique lazys = %ld\n", lazyTargets.size());
+    }
+    
+    if ( parsingDiag.hasError() )
+        _diagnostics.error("%s", parsingDiag.errorMessage().c_str());
+}
+
+
+void CacheBuilder::recomputeCacheUUID(void)
+{
+    // Clear existing UUID, then MD5 whole cache buffer.
+    uint8_t* uuidLoc = _buffer->header.uuid;
+    bzero(uuidLoc, 16);
+    CC_MD5(_buffer, (unsigned)_currentFileSize, uuidLoc);
+    // <rdar://problem/6723729> 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;
+}
+
+
+CacheBuilder::SegmentMapping CacheBuilder::assignSegmentAddresses(const std::vector<DyldSharedCache::MappedMachO>& dylibs, dyld_cache_mapping_info regions[3])
+{
+    // calculate size of header info and where first dylib's mach_header should start
+    size_t startOffset = sizeof(dyld_cache_header) + 3*sizeof(dyld_cache_mapping_info);
+    size_t maxPoolCount = 0;
+    if (  _archLayout->branchReach != 0 )
+        maxPoolCount = (_archLayout->sharedMemorySize / _archLayout->branchReach);
+    startOffset += maxPoolCount * sizeof(uint64_t);
+    startOffset += sizeof(dyld_cache_image_info) * dylibs.size();
+    startOffset += sizeof(dyld_cache_image_text_info) * dylibs.size();
+    for (const DyldSharedCache::MappedMachO& dylib : dylibs) {
+        dyld3::MachOParser parser(dylib.mh);
+        startOffset += (strlen(parser.installName()) + 1);
+    }
+    //fprintf(stderr, "%s total header size = 0x%08lX\n", _options.archName.c_str(), startOffset);
+    startOffset = align(startOffset, 12);
+
+    _branchPoolStarts.clear();
+    __block uint64_t addr = _archLayout->sharedMemoryStart;
+    __block SegmentMapping result;
+
+    // assign TEXT segment addresses
+    regions[0].address      = addr;
+    regions[0].fileOffset   = 0;
+    regions[0].initProt     = VM_PROT_READ | VM_PROT_EXECUTE;
+    regions[0].maxProt      = VM_PROT_READ | VM_PROT_EXECUTE;
+    addr += startOffset; // header
+
+    __block uint64_t lastPoolAddress = addr;
+    for (const DyldSharedCache::MappedMachO& dylib : dylibs) {
+        dyld3::MachOParser parser(dylib.mh, true);
+        parser.forEachSegment(^(const char* segName, uint32_t fileOffset, uint32_t fileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, uint32_t segIndex, uint64_t sizeOfSections, uint8_t p2align, bool& stop) {
+             if ( protections != (VM_PROT_READ | VM_PROT_EXECUTE) )
+                return;
+            // Insert branch island pools every 128MB for arm64
+            if ( (_archLayout->branchPoolTextSize != 0) && ((addr + vmSize - lastPoolAddress) > _archLayout->branchReach) ) {
+                _branchPoolStarts.push_back(addr);
+                _diagnostics.verbose("adding branch pool at 0x%llX\n", addr);
+                lastPoolAddress = addr;
+                addr += _archLayout->branchPoolTextSize;
+            }
+            // Keep __TEXT segments 4K or more aligned
+            addr = align(addr, std::max(p2align, (uint8_t)12));
+            SegmentMappingInfo info;
+            info.srcSegment          = (uint8_t*)dylib.mh + fileOffset;
+            info.segName             = segName;
+            info.dstCacheAddress     = addr;
+            info.dstCacheOffset      = (uint32_t)(addr - regions[0].address + regions[0].fileOffset);
+            info.dstCacheSegmentSize = (uint32_t)align(sizeOfSections, 12);
+            info.copySegmentSize     = (uint32_t)align(sizeOfSections, 12);
+            info.srcSegmentIndex     = segIndex;
+            result[dylib.mh].push_back(info);
+            addr += info.dstCacheSegmentSize;
+        });
+    }
+    // align TEXT region end
+    uint64_t endTextAddress = align(addr, _archLayout->sharedRegionAlignP2);
+    regions[0].size         = endTextAddress - regions[0].address;
+
+    // assign __DATA* addresses
+    if ( _archLayout->sharedRegionsAreDiscontiguous )
+        addr = _archLayout->sharedMemoryStart + 0x60000000;
+    else
+        addr = align((addr + _archLayout->sharedRegionPadding), _archLayout->sharedRegionAlignP2);
+    regions[1].address      = addr;
+    regions[1].fileOffset   = regions[0].fileOffset + regions[0].size;
+    regions[1].initProt     = VM_PROT_READ | VM_PROT_WRITE;
+    regions[1].maxProt      = VM_PROT_READ | VM_PROT_WRITE;
+
+    // layout all __DATA_CONST segments
+    __block int dataConstSegmentCount = 0;
+    for (const DyldSharedCache::MappedMachO& dylib : dylibs) {
+        dyld3::MachOParser parser(dylib.mh, true);
+        parser.forEachSegment(^(const char* segName, uint32_t fileOffset, uint32_t fileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, uint32_t segIndex, uint64_t sizeOfSections, uint8_t p2align, bool& stop) {
+            if ( protections != (VM_PROT_READ | VM_PROT_WRITE) )
+                return;
+            if ( strcmp(segName, "__DATA_CONST") != 0 )
+                return;
+            ++dataConstSegmentCount;
+            // Pack __DATA_CONST segments
+            addr = align(addr, p2align);
+            size_t copySize = std::min((size_t)fileSize, (size_t)sizeOfSections);
+            SegmentMappingInfo info;
+            info.srcSegment          = (uint8_t*)dylib.mh + fileOffset;
+            info.segName             = segName;
+            info.dstCacheAddress     = addr;
+            info.dstCacheOffset      = (uint32_t)(addr - regions[1].address + regions[1].fileOffset);
+            info.dstCacheSegmentSize = (uint32_t)sizeOfSections;
+            info.copySegmentSize     = (uint32_t)copySize;
+            info.srcSegmentIndex     = segIndex;
+            result[dylib.mh].push_back(info);
+            addr += info.dstCacheSegmentSize;
+        });
+    }
+
+    // layout all __DATA segments (and other r/w non-dirty, non-const) segments
+    for (const DyldSharedCache::MappedMachO& dylib : dylibs) {
+        dyld3::MachOParser parser(dylib.mh, true);
+        parser.forEachSegment(^(const char* segName, uint32_t fileOffset, uint32_t fileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, uint32_t segIndex, uint64_t sizeOfSections, uint8_t p2align, bool& stop) {
+            if ( protections != (VM_PROT_READ | VM_PROT_WRITE) )
+                return;
+            if ( strcmp(segName, "__DATA_CONST") == 0 )
+                return;
+            if ( strcmp(segName, "__DATA_DIRTY") == 0 )
+                return;
+            if ( dataConstSegmentCount > 10 ) {
+                // Pack __DATA segments only if we also have __DATA_CONST segments
+                addr = align(addr, p2align);
+            }
+            else {
+                // Keep __DATA segments 4K or more aligned
+                addr = align(addr, std::max(p2align, (uint8_t)12));
+            }
+            size_t copySize = std::min((size_t)fileSize, (size_t)sizeOfSections);
+            SegmentMappingInfo info;
+            info.srcSegment          = (uint8_t*)dylib.mh + fileOffset;
+            info.segName             = segName;
+            info.dstCacheAddress     = addr;
+            info.dstCacheOffset      = (uint32_t)(addr - regions[1].address + regions[1].fileOffset);
+            info.dstCacheSegmentSize = (uint32_t)sizeOfSections;
+            info.copySegmentSize     = (uint32_t)copySize;
+            info.srcSegmentIndex     = segIndex;
+            result[dylib.mh].push_back(info);
+            addr += info.dstCacheSegmentSize;
+        });
+    }
+
+    // layout all __DATA_DIRTY segments, sorted
+    addr = align(addr, 12);
+    std::vector<DyldSharedCache::MappedMachO> dirtyDataDylibs = makeSortedDylibs(dylibs, _options.dirtyDataSegmentOrdering);
+    for (const DyldSharedCache::MappedMachO& dylib : dirtyDataDylibs) {
+        dyld3::MachOParser parser(dylib.mh, true);
+        parser.forEachSegment(^(const char* segName, uint32_t fileOffset, uint32_t fileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, uint32_t segIndex, uint64_t sizeOfSections, uint8_t p2align, bool& stop) {
+            if ( protections != (VM_PROT_READ | VM_PROT_WRITE) )
+                return;
+            if ( strcmp(segName, "__DATA_DIRTY") != 0 )
+                return;
+            // Pack __DATA_DIRTY segments
+            addr = align(addr, p2align);
+            size_t copySize = std::min((size_t)fileSize, (size_t)sizeOfSections);
+            SegmentMappingInfo info;
+            info.srcSegment          = (uint8_t*)dylib.mh + fileOffset;
+            info.segName             = segName;
+            info.dstCacheAddress     = addr;
+            info.dstCacheOffset      = (uint32_t)(addr - regions[1].address + regions[1].fileOffset);
+            info.dstCacheSegmentSize = (uint32_t)sizeOfSections;
+            info.copySegmentSize     = (uint32_t)copySize;
+            info.srcSegmentIndex     = segIndex;
+            result[dylib.mh].push_back(info);
+            addr += info.dstCacheSegmentSize;
+        });
+    }
+
+    // align DATA region end
+    uint64_t endDataAddress = align(addr, _archLayout->sharedRegionAlignP2);
+    regions[1].size         = endDataAddress - regions[1].address;
+
+    // start read-only region
+    if ( _archLayout->sharedRegionsAreDiscontiguous )
+        addr = _archLayout->sharedMemoryStart + 0xA0000000;
+    else
+        addr = align((addr + _archLayout->sharedRegionPadding), _archLayout->sharedRegionAlignP2);
+    regions[2].address    = addr;
+    regions[2].fileOffset = regions[1].fileOffset + regions[1].size;
+    regions[2].maxProt    = VM_PROT_READ;
+    regions[2].initProt   = VM_PROT_READ;
+
+    // reserve space for kernel ASLR slide info at start of r/o region
+    if ( _options.cacheSupportsASLR ) {
+        _slideInfoBufferSizeAllocated = align((regions[1].size/4096) * 4, _archLayout->sharedRegionAlignP2); // only need 2 bytes per page
+        _slideInfoFileOffset = regions[2].fileOffset;
+        addr += _slideInfoBufferSizeAllocated;
+    }
+
+    // layout all read-only (but not LINKEDIT) segments
+    for (const DyldSharedCache::MappedMachO& dylib : dylibs) {
+        dyld3::MachOParser parser(dylib.mh, true);
+        parser.forEachSegment(^(const char* segName, uint32_t fileOffset, uint32_t fileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, uint32_t segIndex, uint64_t sizeOfSections, uint8_t p2align, bool& stop) {
+            if ( protections != VM_PROT_READ )
+                return;
+            if ( strcmp(segName, "__LINKEDIT") == 0 )
+                return;
+            // Keep segments segments 4K or more aligned
+            addr = align(addr, std::max(p2align, (uint8_t)12));
+            SegmentMappingInfo info;
+            info.srcSegment          = (uint8_t*)dylib.mh + fileOffset;
+            info.segName             = segName;
+            info.dstCacheAddress     = addr;
+            info.dstCacheOffset      = (uint32_t)(addr - regions[2].address + regions[2].fileOffset);
+            info.dstCacheSegmentSize = (uint32_t)align(sizeOfSections, 12);
+            info.copySegmentSize     = (uint32_t)sizeOfSections;
+            info.srcSegmentIndex     = segIndex;
+            result[dylib.mh].push_back(info);
+            addr += info.dstCacheSegmentSize;
+        });
+    }
+    // layout all LINKEDIT segments (after other read-only segments)
+    for (const DyldSharedCache::MappedMachO& dylib : dylibs) {
+        dyld3::MachOParser parser(dylib.mh, true);
+        parser.forEachSegment(^(const char* segName, uint32_t fileOffset, uint32_t fileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, uint32_t segIndex, uint64_t sizeOfSections, uint8_t p2align, bool& stop) {
+            if ( protections != VM_PROT_READ )
+                return;
+            if ( strcmp(segName, "__LINKEDIT") != 0 )
+                return;
+            // Keep segments segments 4K or more aligned
+            addr = align(addr, std::max(p2align, (uint8_t)12));
+            SegmentMappingInfo info;
+            info.srcSegment          = (uint8_t*)dylib.mh + fileOffset;
+            info.segName             = segName;
+            info.dstCacheAddress     = addr;
+            info.dstCacheOffset      = (uint32_t)(addr - regions[2].address + regions[2].fileOffset);
+            info.dstCacheSegmentSize = (uint32_t)align(sizeOfSections, 12);
+            info.copySegmentSize     = (uint32_t)align(fileSize, 12);
+            info.srcSegmentIndex     = segIndex;
+            result[dylib.mh].push_back(info);
+            addr += info.dstCacheSegmentSize;
+        });
+    }
+    // add room for branch pool linkedits
+    _branchPoolsLinkEditStartAddr = addr;
+    addr += (_branchPoolStarts.size() * _archLayout->branchPoolLinkEditSize);
+
+    // align r/o region end
+    uint64_t endReadOnlyAddress = align(addr, _archLayout->sharedRegionAlignP2);
+    regions[2].size = endReadOnlyAddress - regions[2].address;
+    _currentFileSize = regions[2].fileOffset + regions[2].size;
+
+    // FIXME: Confirm these numbers for all platform/arch combos
+    // assume LINKEDIT optimzation reduces LINKEDITs to %40 of original size
+    if ( _options.excludeLocalSymbols ) {
+        _vmSize = regions[2].address + (regions[2].size * 2 / 5) - regions[0].address;
+    }
+    else {
+        _vmSize = regions[2].address + (regions[2].size * 9 / 10) - regions[0].address;
+    }
+
+    // sort SegmentMappingInfo for each image to be in the same order as original segments
+    for (auto& entry : result) {
+        std::vector<SegmentMappingInfo>& infos = entry.second;
+        std::sort(infos.begin(), infos.end(), [&](const SegmentMappingInfo& a, const SegmentMappingInfo& b) {
+            return a.srcSegmentIndex < b.srcSegmentIndex;
+        });
+    }
+
+    return result;
+}
+
+uint64_t CacheBuilder::pathHash(const char* path)
+{
+    uint64_t sum = 0;
+    for (const char* s=path; *s != '\0'; ++s)
+        sum += sum*4 + *s;
+    return sum;
+}
+
+
+void CacheBuilder::findDylibAndSegment(const void* contentPtr, std::string& foundDylibName, std::string& foundSegName)
+{
+    foundDylibName = "???";
+    foundSegName   = "???";
+    uint32_t cacheOffset = (uint32_t)((uint8_t*)contentPtr - (uint8_t*)_buffer);
+    _buffer->forEachImage(^(const mach_header* mh, const char* installName) {
+        dyld3::MachOParser parser(mh, true);
+        parser.forEachSegment(^(const char* segName, uint32_t fileOffset, uint32_t fileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, bool& stop) {
+            if ( (cacheOffset > fileOffset) && (cacheOffset < (fileOffset+vmSize)) ) {
+                foundDylibName = installName;
+                foundSegName = segName;
+            }
+        });
+    });
+ }
+
+
+template <typename P>
+bool CacheBuilder::makeRebaseChain(uint8_t* pageContent, uint16_t lastLocationOffset, uint16_t offset, const dyld_cache_slide_info2* 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);
+        _diagnostics.error("rebase pointer does not point within cache. lastOffset=0x%04X, seg=%s, dylib=%s\n",
+                            lastLocationOffset, segName.c_str(), dylibName.c_str());
+        return false;
+    }
+    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 <typename P>
+void CacheBuilder::addPageStarts(uint8_t* pageContent, const bool bitmap[], const dyld_cache_slide_info2* info,
+                                std::vector<uint16_t>& pageStarts, std::vector<uint16_t>& 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<P>(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 ) {
+                        _diagnostics.error("rebase overflow in page extras");
+                        return;
+                    }
+                    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 <typename P>
+void CacheBuilder::writeSlideInfoV2()
+{
+    typedef typename P::uint_t    pint_t;
+    typedef typename P::E         E;
+    const uint32_t pageSize = 4096;
+
+    // build one 1024/4096 bool bitmap per page (4KB/16KB) of DATA
+    const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((char*)_buffer + _buffer->header.mappingOffset);
+    uint8_t* const dataStart = (uint8_t*)_buffer + mappings[1].fileOffset;
+    uint8_t* const dataEnd   = dataStart + mappings[1].size;
+    unsigned pageCount = (unsigned)(mappings[1].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) ) {
+            _diagnostics.error("DATA pointer for sliding, out of range\n");
+            free(bitmap);
+            return;
+        }
+        long byteOffset = (long)((uint8_t*)p - dataStart);
+        if ( (byteOffset % 4) != 0 ) {
+            _diagnostics.error("pointer not 4-byte aligned in DATA offset 0x%08lX\n", byteOffset);
+            free(bitmap);
+            return;
+        }
+        long boolIndex = byteOffset / 4;
+        // work around <rdar://24941083> by ignoring pointers to be slid that are NULL on disk
+        if ( *((pint_t*)p) == 0 ) {
+            std::string dylibName;
+            std::string segName;
+            findDylibAndSegment(p, dylibName, segName);
+            _diagnostics.warning("NULL pointer asked to be slid in %s at DATA region offset 0x%04lX of %s", segName.c_str(), byteOffset, dylibName.c_str());
+            continue;
+        }
+        bitmap[boolIndex] = true;
+    }
+
+    // fill in fixed info
+    assert(_slideInfoFileOffset != 0);
+    dyld_cache_slide_info2* info = (dyld_cache_slide_info2*)((uint8_t*)_buffer + _slideInfoFileOffset);
+    info->version    = 2;
+    info->page_size  = pageSize;
+    info->delta_mask = _archLayout->pointerDeltaMask;
+    info->value_add  = (sizeof(pint_t) == 8) ? 0 : _archLayout->sharedMemoryStart;  // only value_add for 32-bit archs
+
+    // set page starts and extras for each page
+    std::vector<uint16_t> pageStarts;
+    std::vector<uint16_t> pageExtras;
+    pageStarts.reserve(pageCount);
+    uint8_t* pageContent = dataStart;;
+    const bool* bitmapForPage = bitmap;
+    for (unsigned i=0; i < pageCount; ++i) {
+        //warning("page[%d]", i);
+        addPageStarts<P>(pageContent, bitmapForPage, info, pageStarts, pageExtras);
+        if ( _diagnostics.hasError() ) {
+            free(bitmap);
+            return;
+        }
+        pageContent += pageSize;
+        bitmapForPage += (sizeof(bool)*(pageSize/4));
+    }
+    free((void*)bitmap);
+
+    // fill in computed info
+    info->page_starts_offset = sizeof(dyld_cache_slide_info2);
+    info->page_starts_count  = (unsigned)pageStarts.size();
+    info->page_extras_offset = (unsigned)(sizeof(dyld_cache_slide_info2)+pageStarts.size()*sizeof(uint16_t));
+    info->page_extras_count  = (unsigned)pageExtras.size();
+    uint16_t* pageStartsBuffer = (uint16_t*)((char*)info + info->page_starts_offset);
+    uint16_t* pageExtrasBuffer = (uint16_t*)((char*)info + info->page_extras_offset);
+    for (unsigned i=0; i < pageStarts.size(); ++i)
+        pageStartsBuffer[i] = pageStarts[i];
+    for (unsigned i=0; i < pageExtras.size(); ++i)
+        pageExtrasBuffer[i] = pageExtras[i];
+    // update header with final size
+    _buffer->header.slideInfoSize = align(info->page_extras_offset + pageExtras.size()*sizeof(uint16_t), _archLayout->sharedRegionAlignP2);
+    if ( _buffer->header.slideInfoSize > _slideInfoBufferSizeAllocated ) {
+        _diagnostics.error("kernel slide info overflow buffer");
+    }
+    //warning("pageCount=%u, page_starts_count=%lu, page_extras_count=%lu", pageCount, pageStarts.size(), pageExtras.size());
+}
+
+
+/*
+void CacheBuilder::writeSlideInfoV1()
+{
+    // build one 128-byte bitmap per page (4096) of DATA
+    uint8_t* const dataStart = (uint8_t*)_buffer.get() + regions[1].fileOffset;
+    uint8_t* const dataEnd   = dataStart + regions[1].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;
+    dyld_cache_slide_info* slideInfo = (dyld_cache_slide_info*)((uint8_t*)_buffer + _slideInfoFileOffset);
+    slideInfo->version          = 1;
+    slideInfo->toc_offset       = sizeof(dyld_cache_slide_info);
+    slideInfo->toc_count        = toc_count;
+    slideInfo->entries_offset   = (slideInfo->toc_offset+2*toc_count+127)&(-128);
+    slideInfo->entries_count    = 0;
+    slideInfo->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->entries_count  = entry_count;
+    ::free((void*)bitmap);
+
+    _buffer.header->slideInfoSize = align(slideInfo->entries_offset + entry_count*entry_size, _archLayout->sharedRegionAlignP2);
+}
+
+*/
+
+void CacheBuilder::fipsSign() {
+    __block bool found = false;
+    _buffer->forEachImage(^(const mach_header* mh, const char* installName) {
+        __block void *hash_location = nullptr;
+        // Return if this is not corecrypto
+        if (strcmp(installName, "/usr/lib/system/libcorecrypto.dylib") != 0) {
+            return;
+        }
+        found = true;
+        auto parser = dyld3::MachOParser(mh, true);
+        parser.forEachLocalSymbol(_diagnostics, ^(const char *symbolName, uint64_t n_value, uint8_t n_type, uint8_t n_sect, uint16_t n_desc, bool &stop) {
+            if (strcmp(symbolName, "_fipspost_precalc_hmac") != 0)
+                return;
+            hash_location = (void *)(n_value - _archLayout->sharedMemoryStart + (uintptr_t)_buffer);
+            stop = true;
+        });
+
+        // Bail out if we did not find the symbol
+        if (hash_location == nullptr) {
+            _diagnostics.warning("Could not find _fipspost_precalc_hmac, skipping FIPS sealing");
+            return;
+        }
+
+        parser.forEachSection(^(const char *segName, const char *sectionName, uint32_t flags, const void *content, size_t size, bool illegalSectionSize, bool &stop) {
+            // FIXME: If we ever implement userspace __TEXT_EXEC this will need to be updated
+            if ( (strcmp(segName, "__TEXT" ) != 0) || (strcmp(sectionName, "__text") != 0) )  {
+                return;
+            }
+
+            if (illegalSectionSize) {
+                _diagnostics.error("FIPS section %s/%s extends beyond the end of the segment", segName, sectionName);
+                return;
+            }
+
+            //We have _fipspost_precalc_hmac and __TEXT,__text, seal it
+            unsigned char hmac_key = 0;
+            CCHmac(kCCHmacAlgSHA256, &hmac_key, 1, content, size, hash_location);
+            stop = true;
+        });
+    });
+
+    if (!found) {
+        _diagnostics.warning("Could not find /usr/lib/system/libcorecrypto.dylib, skipping FIPS sealing");
+    }
+}
+
+void CacheBuilder::codeSign()
+{
+    uint8_t  dscHashType;
+    uint8_t  dscHashSize;
+    uint32_t dscDigestFormat;
+    bool agile = false;
+
+    // select which codesigning hash
+    switch (_options.codeSigningDigestMode) {
+        case DyldSharedCache::Agile:
+            agile = true;
+            // Fall through to SHA1, because the main code directory remains SHA1 for compatibility.
+        case DyldSharedCache::SHA1only:
+            dscHashType     = CS_HASHTYPE_SHA1;
+            dscHashSize     = CS_HASH_SIZE_SHA1;
+            dscDigestFormat = kCCDigestSHA1;
+            break;
+        case DyldSharedCache::SHA256only:
+            dscHashType     = CS_HASHTYPE_SHA256;
+            dscHashSize     = CS_HASH_SIZE_SHA256;
+            dscDigestFormat = kCCDigestSHA256;
+            break;
+        default:
+            _diagnostics.error("codeSigningDigestMode has unknown, unexpected value %d, bailing out.",
+                               _options.codeSigningDigestMode);
+            return;
+    }
+
+    std::string cacheIdentifier = "com.apple.dyld.cache." + _options.archName;
+    if ( _options.dylibsRemovedDuringMastering ) {
+        if ( _options.optimizeStubs  )
+            cacheIdentifier = "com.apple.dyld.cache." + _options.archName + ".release";
+        else
+            cacheIdentifier = "com.apple.dyld.cache." + _options.archName + ".development";
+    }
+    // get pointers into shared cache buffer
+    size_t          inBbufferSize = _currentFileSize;
+    const uint8_t*  inBuffer = (uint8_t*)_buffer;
+    uint8_t*        csBuffer = (uint8_t*)_buffer+inBbufferSize;
+
+    // layout code signature contents
+    uint32_t blobCount     = agile ? 4 : 3;
+    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   idOffset      = offsetof(CS_CodeDirectory, end_withExecSeg);
+    size_t   hashOffset    = idOffset+idSize + dscHashSize*xSlotCount;
+    size_t   hash256Offset = idOffset+idSize + CS_HASH_SIZE_SHA256*xSlotCount;
+    size_t   cdSize        = hashOffset + (slotCount * dscHashSize);
+    size_t   cd256Size     = agile ? hash256Offset + (slotCount * CS_HASH_SIZE_SHA256) : 0;
+    size_t   reqsSize      = 12;
+    size_t   cmsSize       = sizeof(CS_Blob);
+    size_t   cdOffset      = sizeof(CS_SuperBlob) + blobCount*sizeof(CS_BlobIndex);
+    size_t   cd256Offset   = cdOffset + cdSize;
+    size_t   reqsOffset    = cd256Offset + cd256Size; // equals cdOffset + cdSize if not agile
+    size_t   cmsOffset     = reqsOffset + reqsSize;
+    size_t   sbSize        = cmsOffset + cmsSize;
+    size_t   sigSize       = align(sbSize, 14);       // keep whole cache 16KB aligned
+
+    if ( _currentFileSize+sigSize > _allocatedBufferSize ) {
+        _diagnostics.error("cache buffer too small to hold code signature (buffer size=%lldMB, signature size=%ldMB, free space=%lldMB)",
+                            _allocatedBufferSize/1024/1024, sigSize/1024/1024, (_allocatedBufferSize-_currentFileSize)/1024/1024);
+        return;
+    }
+
+    // create overall code signature which is a superblob
+    CS_SuperBlob* sb = reinterpret_cast<CS_SuperBlob*>(csBuffer);
+    sb->magic           = htonl(CSMAGIC_EMBEDDED_SIGNATURE);
+    sb->length          = htonl(sbSize);
+    sb->count           = htonl(blobCount);
+    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);
+    if ( agile ) {
+        sb->index[3].type = htonl(CSSLOT_ALTERNATE_CODEDIRECTORIES + 0);
+        sb->index[3].offset = htonl(cd256Offset);
+    }
+
+    // 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;
+
+    // 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(0x20400);               // supports exec segment
+    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   = 0;                            // not supported anymore
+    cd->teamOffset      = 0;                            // no team ID
+    cd->spare3          = 0;                            // unused (must be zero)
+    cd->codeLimit64     = 0;                            // falls back to codeLimit
+
+    // executable segment info
+    const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((char*)_buffer + _buffer->header.mappingOffset);
+    cd->execSegBase     = htonll(mappings[0].fileOffset); // base of TEXT segment
+    cd->execSegLimit    = htonll(mappings[0].size);     // size of TEXT segment
+    cd->execSegFlags    = 0;                            // not a main binary
+
+    // initialize dynamic fields of Code Directory
+    strcpy((char*)cd + idOffset, cacheIdentifier.c_str());
+
+    // 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);
+
+    CS_CodeDirectory* cd256;
+    uint8_t* hash256Slot;
+    uint8_t* reqsHash256Slot;
+    if ( agile ) {
+        // Note that the assumption here is that the size up to the hashes is the same as for
+        // sha1 code directory, and that they come last, after everything else.
+
+        cd256 = (CS_CodeDirectory*)(((char*)sb)+cd256Offset);
+        cd256->magic           = htonl(CSMAGIC_CODEDIRECTORY);
+        cd256->length          = htonl(cd256Size);
+        cd256->version         = htonl(0x20400);               // supports exec segment
+        cd256->flags           = htonl(kSecCodeSignatureAdhoc);
+        cd256->hashOffset      = htonl(hash256Offset);
+        cd256->identOffset     = htonl(idOffset);
+        cd256->nSpecialSlots   = htonl(xSlotCount);
+        cd256->nCodeSlots      = htonl(slotCount);
+        cd256->codeLimit       = htonl(inBbufferSize);
+        cd256->hashSize        = CS_HASH_SIZE_SHA256;
+        cd256->hashType        = CS_HASHTYPE_SHA256;
+        cd256->platform        = 0;                            // not platform binary
+        cd256->pageSize        = __builtin_ctz(CS_PAGE_SIZE);  // log2(CS_PAGE_SIZE);
+        cd256->spare2          = 0;                            // unused (must be zero)
+        cd256->scatterOffset   = 0;                            // not supported anymore
+        cd256->teamOffset      = 0;                            // no team ID
+        cd256->spare3          = 0;                            // unused (must be zero)
+        cd256->codeLimit64     = 0;                            // falls back to codeLimit
+
+        // executable segment info
+        cd256->execSegBase     = cd->execSegBase;
+        cd256->execSegLimit    = cd->execSegLimit;
+        cd256->execSegFlags    = cd->execSegFlags;
+
+        // initialize dynamic fields of Code Directory
+        strcpy((char*)cd256 + idOffset, cacheIdentifier.c_str());
+
+        // add special slot hashes
+        hash256Slot = (uint8_t*)cd256 + hash256Offset;
+        reqsHash256Slot = &hash256Slot[-CSSLOT_REQUIREMENTS*CS_HASH_SIZE_SHA256];
+        CCDigest(kCCDigestSHA256, (uint8_t*)reqs, sizeof(CS_RequirementsBlob), reqsHash256Slot);
+    }
+    else {
+        cd256 = NULL;
+        hash256Slot = NULL;
+        reqsHash256Slot = NULL;
+    }
+
+    // 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));
+
+    // alter header of cache to record size and location of code signature
+    // do this *before* hashing each page
+    _buffer->header.codeSignatureOffset = inBbufferSize;
+    _buffer->header.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;
+
+        if ( agile ) {
+            CCDigest(kCCDigestSHA256, code, CS_PAGE_SIZE, hash256Slot);
+            hash256Slot += CS_HASH_SIZE_SHA256;
+        }
+        code += CS_PAGE_SIZE;
+    }
+
+    // hash of entire code directory (cdHash) uses same 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(_cdHashFirst, fullCdHash, 20);
+    if ( agile ) {
+        uint8_t fullCdHash256[CS_HASH_SIZE_SHA256];
+        CCDigest(kCCDigestSHA256, (const uint8_t*)cd256, cd256Size, fullCdHash256);
+        // Note: cdHash is defined as first 20 bytes of hash, even for sha256
+        memcpy(_cdHashSecond, fullCdHash256, 20);
+    }
+    else {
+        memset(_cdHashSecond, 0, 20);
+    }
+
+    // increase file size to include newly append code signature
+    _currentFileSize += sigSize;
+}
+
+const bool CacheBuilder::agileSignature()
+{
+    return _options.codeSigningDigestMode == DyldSharedCache::Agile;
+}
+
+static const std::string cdHash(uint8_t hash[20])
+{
+    char buff[48];
+    for (int i = 0; i < 20; ++i)
+        sprintf(&buff[2*i], "%2.2x", hash[i]);
+    return buff;
+}
+
+const std::string CacheBuilder::cdHashFirst()
+{
+    return cdHash(_cdHashFirst);
+}
+
+const std::string CacheBuilder::cdHashSecond()
+{
+    return cdHash(_cdHashSecond);
+}
+
+void CacheBuilder::addCachedDylibsImageGroup(dyld3::ImageProxyGroup* dylibGroup)
+{
+    const dyld3::launch_cache::binary_format::ImageGroup* groupBinary = dylibGroup->makeImageGroupBinary(_diagnostics, _s_neverStubEliminate);
+    if (!groupBinary)
+        return;
+
+    dyld3::launch_cache::ImageGroup group(groupBinary);
+    size_t groupSize = group.size();
+
+    if ( _currentFileSize+groupSize > _allocatedBufferSize ) {
+        _diagnostics.error("cache buffer too small to hold group[0] info (buffer size=%lldMB, group size=%ldMB, free space=%lldMB)",
+                            _allocatedBufferSize/1024/1024, groupSize/1024/1024, (_allocatedBufferSize-_currentFileSize)/1024/1024);
+        return;
+    }
+
+    // append ImageGroup data to read-only region of cache
+    uint8_t* loc = (uint8_t*)_buffer + _currentFileSize;
+    memcpy(loc, groupBinary, groupSize);
+    dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((char*)_buffer + _buffer->header.mappingOffset);
+    _buffer->header.dylibsImageGroupAddr = mappings[2].address + (_currentFileSize - mappings[2].fileOffset);
+    _buffer->header.dylibsImageGroupSize = (uint32_t)groupSize;
+    _currentFileSize += groupSize;
+    free((void*)groupBinary);
+}
+
+
+void CacheBuilder::addCachedOtherDylibsImageGroup(dyld3::ImageProxyGroup* otherGroup)
+{
+    const dyld3::launch_cache::binary_format::ImageGroup* groupBinary = otherGroup->makeImageGroupBinary(_diagnostics);
+    if (!groupBinary)
+        return;
+
+    dyld3::launch_cache::ImageGroup group(groupBinary);
+    size_t groupSize = group.size();
+
+    if ( _currentFileSize+groupSize > _allocatedBufferSize ) {
+        _diagnostics.error("cache buffer too small to hold group[1] info (buffer size=%lldMB, group size=%ldMB, free space=%lldMB)",
+                            _allocatedBufferSize/1024/1024, groupSize/1024/1024, (_allocatedBufferSize-_currentFileSize)/1024/1024);
+        return;
+    }
+
+    // append ImageGroup data to read-only region of cache
+    uint8_t* loc = (uint8_t*)_buffer + _currentFileSize;
+    memcpy(loc, groupBinary, groupSize);
+    dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((char*)_buffer + _buffer->header.mappingOffset);
+    _buffer->header.otherImageGroupAddr = mappings[2].address + (_currentFileSize - mappings[2].fileOffset);
+    _buffer->header.otherImageGroupSize = (uint32_t)groupSize;
+    _currentFileSize += groupSize;
+    free((void*)groupBinary);
+}
+
+void CacheBuilder::addClosures(const std::map<std::string, const dyld3::launch_cache::binary_format::Closure*>& closures)
+{
+    // preflight space needed
+    size_t closuresSpace = 0;
+    for (const auto& entry : closures) {
+        dyld3::launch_cache::Closure closure(entry.second);
+        closuresSpace += closure.size();
+    }
+    size_t freeSpace = _allocatedBufferSize - _currentFileSize;
+    if ( closuresSpace > freeSpace ) {
+        _diagnostics.error("cache buffer too small to hold all closures (buffer size=%lldMB, closures size=%ldMB, free space=%ldMB)",
+                            _allocatedBufferSize/1024/1024, closuresSpace/1024/1024, freeSpace/1024/1024);
+        return;
+    }
+
+    dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((char*)_buffer + _buffer->header.mappingOffset);
+    _buffer->header.progClosuresAddr = mappings[2].address + (_currentFileSize - mappings[2].fileOffset);
+    uint8_t* closuresBase = (uint8_t*)_buffer + _currentFileSize;
+    std::vector<DylibIndexTrie::Entry> closureEntrys;
+    uint32_t currentClosureOffset = 0;
+    for (const auto& entry : closures) {
+        const dyld3::launch_cache::binary_format::Closure* closBuf = entry.second;
+        closureEntrys.push_back(DylibIndexTrie::Entry(entry.first, DylibIndex(currentClosureOffset)));
+        dyld3::launch_cache::Closure closure(closBuf);
+        size_t size = closure.size();
+        assert((size % 4) == 0);
+        memcpy(closuresBase+currentClosureOffset, closBuf, size);
+        currentClosureOffset += size;
+        freeSpace -= size;
+        free((void*)closBuf);
+    }
+    _buffer->header.progClosuresSize = currentClosureOffset;
+    _currentFileSize += currentClosureOffset;
+    freeSpace = _allocatedBufferSize - _currentFileSize;
+
+    // build trie of indexes into closures list
+    DylibIndexTrie closureTrie(closureEntrys);
+    std::vector<uint8_t> trieBytes;
+    closureTrie.emit(trieBytes);
+    while ( (trieBytes.size() % 8) != 0 )
+        trieBytes.push_back(0);
+    if ( trieBytes.size() > freeSpace ) {
+        _diagnostics.error("cache buffer too small to hold all closures trie (buffer size=%lldMB, trie size=%ldMB, free space=%ldMB)",
+                            _allocatedBufferSize/1024/1024, trieBytes.size()/1024/1024, freeSpace/1024/1024);
+        return;
+    }
+    memcpy((uint8_t*)_buffer + _currentFileSize, &trieBytes[0], trieBytes.size());
+    _buffer->header.progClosuresTrieAddr = mappings[2].address + (_currentFileSize - mappings[2].fileOffset);
+    _buffer->header.progClosuresTrieSize = trieBytes.size();
+    _currentFileSize += trieBytes.size();
+}
+
+
diff --git a/dyld3/shared-cache/CacheBuilder.h b/dyld3/shared-cache/CacheBuilder.h
new file mode 100644 (file)
index 0000000..4a6227c
--- /dev/null
@@ -0,0 +1,168 @@
+/*
+ * Copyright (c) 2017 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 CacheBuilder_h
+#define CacheBuilder_h
+
+#include <string>
+#include <vector>
+#include <unordered_map>
+#include <unordered_set>
+
+#include "DyldSharedCache.h"
+#include "Diagnostics.h"
+#include "ImageProxy.h"
+
+
+namespace  dyld3 {
+  namespace launch_cache {
+    namespace binary_format {
+      struct ImageGroup;
+      struct Closure;
+    }
+  }
+}
+
+
+struct CacheBuilder {
+
+                                    CacheBuilder(const DyldSharedCache::CreateOptions& options);
+
+    bool                            build(const std::vector<DyldSharedCache::MappedMachO>&  dylibsToCache,
+                                          const std::vector<DyldSharedCache::MappedMachO>&  otherOsDylibs,
+                                          const std::vector<DyldSharedCache::MappedMachO>&  osExecutables);
+    void                            deleteBuffer();
+    const DyldSharedCache*          buffer() { return _buffer; }
+    size_t                          bufferSize() { return (size_t)_allocatedBufferSize; }
+    std::string                     errorMessage();
+    const std::set<std::string>     warnings();
+    const bool                      agileSignature();
+    const std::string               cdHashFirst();
+    const std::string               cdHashSecond();
+
+    struct SegmentMappingInfo {
+        const void*     srcSegment;
+        const char*     segName;
+        uint64_t        dstCacheAddress;
+        uint32_t        dstCacheOffset;
+        uint32_t        dstCacheSegmentSize;
+        uint32_t        copySegmentSize;
+        uint32_t        srcSegmentIndex;
+    };
+
+private:
+
+    typedef std::unordered_map<const mach_header*, std::vector<SegmentMappingInfo>> SegmentMapping;
+
+    struct ArchLayout
+    {
+        uint64_t    sharedMemoryStart;
+        uint64_t    sharedMemorySize;
+        uint64_t    sharedRegionPadding;
+        uint64_t    pointerDeltaMask;
+        const char* archName;
+        uint32_t    branchPoolTextSize;
+        uint32_t    branchPoolLinkEditSize;
+        uint32_t    branchReach;
+        uint8_t     sharedRegionAlignP2;
+        bool        sharedRegionsAreDiscontiguous;
+        bool        is64;
+    };
+
+    static const ArchLayout  _s_archLayout[];
+    static const char* const _s_neverStubEliminate[];
+
+    std::vector<DyldSharedCache::MappedMachO> makeSortedDylibs(const std::vector<DyldSharedCache::MappedMachO>& dylibs, const std::unordered_map<std::string, unsigned> sortOrder);
+
+    SegmentMapping assignSegmentAddresses(const std::vector<DyldSharedCache::MappedMachO>& dylibs, struct dyld_cache_mapping_info regions[3]);
+
+    bool        cacheOverflow(const dyld_cache_mapping_info regions[3]);
+    void        adjustImageForNewSegmentLocations(const std::vector<uint64_t>& segNewStartAddresses,
+                                                 const std::vector<uint64_t>& segCacheFileOffsets,
+                                                 const std::vector<uint64_t>& segCacheSizes, std::vector<void*>& pointersForASLR);
+
+    void        fipsSign();
+    void        codeSign();
+    uint64_t    pathHash(const char* path);
+    void        writeCacheHeader(const struct dyld_cache_mapping_info regions[3], const std::vector<DyldSharedCache::MappedMachO>& dylibs, const SegmentMapping&);
+    void        copyRawSegments(const std::vector<DyldSharedCache::MappedMachO>& dylibs, const SegmentMapping& mapping);
+    void        adjustAllImagesForNewSegmentLocations(const std::vector<DyldSharedCache::MappedMachO>& dylibs, const SegmentMapping& mapping);
+    void        bindAllImagesInCacheFile(const dyld_cache_mapping_info regions[3]);
+    void        writeSlideInfoV1();
+    void        recomputeCacheUUID(void);
+    void        findDylibAndSegment(const void* contentPtr, std::string& dylibName, std::string& segName);
+
+    void        addCachedDylibsImageGroup(dyld3::ImageProxyGroup*);
+    void        addCachedOtherDylibsImageGroup(dyld3::ImageProxyGroup*);
+    void        addClosures(const std::map<std::string, const dyld3::launch_cache::binary_format::Closure*>& closures);
+
+    template <typename P> void writeSlideInfoV2();
+    template <typename P> bool makeRebaseChain(uint8_t* pageContent, uint16_t lastLocationOffset, uint16_t newOffset, const struct dyld_cache_slide_info2* info);
+    template <typename P> void addPageStarts(uint8_t* pageContent, const bool bitmap[], const struct dyld_cache_slide_info2* info,
+                                             std::vector<uint16_t>& pageStarts, std::vector<uint16_t>& pageExtras);
+
+
+    const DyldSharedCache::CreateOptions&       _options;
+    DyldSharedCache*                            _buffer;
+    Diagnostics                                 _diagnostics;
+    const ArchLayout*                           _archLayout;
+    uint32_t                                    _aliasCount;
+    uint64_t                                    _slideInfoFileOffset;
+    uint64_t                                    _slideInfoBufferSizeAllocated;
+    uint64_t                                    _allocatedBufferSize;
+    uint64_t                                    _currentFileSize;
+    uint64_t                                    _vmSize;
+    std::unordered_map<std::string, uint32_t>   _dataDirtySegsOrder;
+    std::vector<void*>                          _pointersForASLR;
+    dyld3::ImageProxyGroup::PatchTable          _patchTable;
+    std::vector<uint64_t>                       _branchPoolStarts;
+    uint64_t                                    _branchPoolsLinkEditStartAddr;
+    uint8_t                                     _cdHashFirst[20];
+    uint8_t                                     _cdHashSecond[20];
+};
+
+
+// implemented in AdjustDylibSegemnts.cpp
+void        adjustDylibSegments(DyldSharedCache* cache, bool is64, mach_header* mhInCache, const std::vector<CacheBuilder::SegmentMappingInfo>& mappingInfo, std::vector<void*>& pointersForASLR, Diagnostics& diag);
+
+// implemented in OptimizerLinkedit.cpp
+uint64_t    optimizeLinkedit(DyldSharedCache* cache, bool is64, bool dontMapLocalSymbols, bool addAcceleratorTables, const std::vector<uint64_t>& branchPoolOffsets, Diagnostics& diag, dyld_cache_local_symbols_info** localsInfo);
+
+// implemented in OptimizerBranches.cpp
+void        bypassStubs(DyldSharedCache* cache, const std::vector<uint64_t>& branchPoolStartAddrs, const char* const alwaysUsesStubsTo[], Diagnostics& diag);
+
+// implemented in OptimizerObjC.cpp
+void        optimizeObjC(DyldSharedCache* cache, bool is64, bool customerCache, std::vector<void*>& pointersForASLR, Diagnostics& diag);
+
+
+
+inline uint64_t align(uint64_t addr, uint8_t p2)
+{
+    uint64_t mask = (1 << p2);
+    return (addr + mask - 1) & (-mask);
+}
+
+
+
+#endif /* CacheBuilder_h */
diff --git a/dyld3/shared-cache/DyldSharedCache.cpp b/dyld3/shared-cache/DyldSharedCache.cpp
new file mode 100644 (file)
index 0000000..4fbdd23
--- /dev/null
@@ -0,0 +1,328 @@
+/* -*- 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 <dirent.h>
+#include <sys/errno.h>
+#include <sys/fcntl.h>
+#include <sys/param.h>
+#include <mach/mach.h>
+#include <mach-o/loader.h>
+#include <mach-o/fat.h>
+#include <mach-o/dyld_priv.h>
+#include <assert.h>
+#include <unistd.h>
+#include <dlfcn.h>
+#include <CommonCrypto/CommonDigest.h>
+#include <CommonCrypto/CommonDigestSPI.h>
+
+#if !DYLD_IN_PROCESS
+#include <set>
+#include <string>
+#include <vector>
+#include <unordered_map>
+#include <unordered_set>
+#endif
+
+#define NO_ULEB
+#include "MachOParser.h"
+#include "CacheBuilder.h"
+#include "DyldSharedCache.h"
+#include "LaunchCache.h"
+#include "Trie.hpp"
+#include "StringUtils.h"
+
+
+
+#if !DYLD_IN_PROCESS
+DyldSharedCache::CreateResults DyldSharedCache::create(const CreateOptions&             options,
+                                                       const std::vector<MappedMachO>&  dylibsToCache,
+                                                       const std::vector<MappedMachO>&  otherOsDylibs,
+                                                       const std::vector<MappedMachO>&  osExecutables)
+{
+    CreateResults  results;
+    CacheBuilder   cache(options);
+
+    results.overflowed = cache.build(dylibsToCache, otherOsDylibs, osExecutables);
+
+    results.agileSignature = cache.agileSignature();
+    results.cdHashFirst = cache.cdHashFirst();
+    results.cdHashSecond = cache.cdHashSecond();
+    results.warnings = cache.warnings();
+    if ( cache.errorMessage().empty() ) {
+        results.cacheContent = cache.buffer();
+        results.cacheLength  = cache.bufferSize();
+    }
+    else {
+        cache.deleteBuffer();
+        results.cacheContent = nullptr;
+        results.cacheLength  = 0;
+        results.errorMessage = cache.errorMessage();
+    }
+    return results;
+}
+
+bool DyldSharedCache::verifySelfContained(std::vector<MappedMachO>& dylibsToCache, MappedMachO (^loader)(const std::string& runtimePath), std::vector<std::pair<DyldSharedCache::MappedMachO, std::set<std::string>>>& rejected)
+{
+
+    // build map of dylibs
+    __block std::map<std::string, std::set<std::string>> badDylibs;
+    __block std::set<std::string> knownDylibs;
+    for (const DyldSharedCache::MappedMachO& dylib : dylibsToCache) {
+        std::set<std::string> reasons;
+        dyld3::MachOParser parser(dylib.mh);
+        if (parser.canBePlacedInDyldCache(dylib.runtimePath, reasons)) {
+            knownDylibs.insert(dylib.runtimePath);
+            knownDylibs.insert(parser.installName());
+        } else {
+            badDylibs[dylib.runtimePath] = reasons;
+        }
+    }
+
+    // check all dependencies to assure every dylib in cache only depends on other dylibs in cache
+    __block bool doAgain = true;
+    while ( doAgain ) {
+        __block std::vector<DyldSharedCache::MappedMachO> foundMappings;
+        doAgain = false;
+        // scan dylib list making sure all dependents are in dylib list
+        for (const DyldSharedCache::MappedMachO& dylib : dylibsToCache) {
+            if ( badDylibs.count(dylib.runtimePath) != 0 )
+                continue;
+            dyld3::MachOParser parser(dylib.mh);
+            parser.forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool& stop) {
+                if ( knownDylibs.count(loadPath) == 0 ) {
+                    doAgain = true;
+                    MappedMachO foundMapping;
+                    if ( badDylibs.count(loadPath) == 0 )
+                        foundMapping = loader(loadPath);
+                    if ( foundMapping.length == 0 ) {
+                        std::string reason = std::string("Could not find dependency '") + loadPath +"'";
+                        auto i = badDylibs.find(dylib.runtimePath);
+                        if (i == badDylibs.end()) {
+                            std::set<std::string> reasons;
+                            reasons.insert(reason);
+                            badDylibs[dylib.runtimePath] = reasons;
+                        } else {
+                            i->second.insert(reason);
+                        }
+                        knownDylibs.erase(dylib.runtimePath);
+                        dyld3::MachOParser parserBad(dylib.mh);
+                        knownDylibs.erase(parserBad.installName());
+                    }
+                    else {
+                        dyld3::MachOParser foundParser(foundMapping.mh);
+                        std::set<std::string> reasons;
+                        if (foundParser.canBePlacedInDyldCache(foundParser.installName(), reasons)) {
+                            foundMappings.push_back(foundMapping);
+                            knownDylibs.insert(foundMapping.runtimePath);
+                            knownDylibs.insert(foundParser.installName());
+                        } else {
+                            auto i = badDylibs.find(dylib.runtimePath);
+                            if (i == badDylibs.end()) {
+                                badDylibs[dylib.runtimePath] = reasons;
+                            } else {
+                                i->second.insert(reasons.begin(), reasons.end());
+                            }
+                        }
+                   }
+                }
+            });
+        }
+        dylibsToCache.insert(dylibsToCache.end(), foundMappings.begin(), foundMappings.end());
+        // remove bad dylibs
+        const auto badDylibsCopy = badDylibs;
+        dylibsToCache.erase(std::remove_if(dylibsToCache.begin(), dylibsToCache.end(), [&](const DyldSharedCache::MappedMachO& dylib) {
+            auto i = badDylibsCopy.find(dylib.runtimePath);
+            if ( i !=  badDylibsCopy.end()) {
+                rejected.push_back(std::make_pair(dylib, i->second));
+                return true;
+             }
+             else {
+                return false;
+             }
+        }), dylibsToCache.end());
+    }
+
+    return badDylibs.empty();
+}
+#endif
+
+void DyldSharedCache::forEachRegion(void (^handler)(const void* content, uint64_t vmAddr, uint64_t size, uint32_t permissions)) const
+{
+    const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((char*)this + header.mappingOffset);
+    const dyld_cache_mapping_info* mappingsEnd = &mappings[header.mappingCount];
+    for (const dyld_cache_mapping_info* m=mappings; m < mappingsEnd; ++m) {
+        handler((char*)this + m->fileOffset, m->address, m->size, m->initProt);
+    }
+}
+
+void DyldSharedCache::forEachImage(void (^handler)(const mach_header* mh, const char* installName)) const
+{
+    const dyld_cache_image_info*   dylibs   = (dyld_cache_image_info*)((char*)this + header.imagesOffset);
+    const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((char*)this + header.mappingOffset);
+    if ( mappings[0].fileOffset != 0 )
+        return;
+    uint64_t firstImageOffset = 0;
+    uint64_t firstRegionAddress = mappings[0].address;
+    for (uint32_t i=0; i < header.imagesCount; ++i) {
+        const char* dylibPath  = (char*)this + dylibs[i].pathFileOffset;
+        uint64_t offset = dylibs[i].address - firstRegionAddress;
+        if ( firstImageOffset == 0 )
+            firstImageOffset = offset;
+        // skip over aliases
+        if ( dylibs[i].pathFileOffset < firstImageOffset)
+            continue;
+        const mach_header* mh = (mach_header*)((char*)this + offset);
+        handler(mh, dylibPath);
+    }
+}
+
+void DyldSharedCache::forEachImageEntry(void (^handler)(const char* path, uint64_t mTime, uint64_t inode)) const
+{
+    const dyld_cache_image_info*   dylibs   = (dyld_cache_image_info*)((char*)this + header.imagesOffset);
+    const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((char*)this + header.mappingOffset);
+    if ( mappings[0].fileOffset != 0 )
+        return;
+    uint64_t firstImageOffset = 0;
+    uint64_t firstRegionAddress = mappings[0].address;
+    for (uint32_t i=0; i < header.imagesCount; ++i) {
+        const char* dylibPath  = (char*)this + dylibs[i].pathFileOffset;
+        uint64_t offset = dylibs[i].address - firstRegionAddress;
+        if ( firstImageOffset == 0 )
+            firstImageOffset = offset;
+        // skip over aliases
+        if ( dylibs[i].pathFileOffset < firstImageOffset)
+            continue;
+        handler(dylibPath, dylibs[i].modTime, dylibs[i].inode);
+    }
+}
+
+void DyldSharedCache::forEachImageTextSegment(void (^handler)(uint64_t loadAddressUnslid, uint64_t textSegmentSize, const uuid_t dylibUUID, const char* installName)) const
+{
+    // check for old cache without imagesText array
+    if ( header.mappingOffset < 123 )
+        return;
+
+    // walk imageText table and call callback for each entry
+    const dyld_cache_image_text_info* imagesText = (dyld_cache_image_text_info*)((char*)this + header.imagesTextOffset);
+    const dyld_cache_image_text_info* imagesTextEnd = &imagesText[header.imagesTextCount];
+    for (const dyld_cache_image_text_info* p=imagesText; p < imagesTextEnd; ++p) {
+        handler(p->loadAddress, p->textSegmentSize, p->uuid, (char*)this + p->pathOffset);
+    }
+}
+
+
+std::string DyldSharedCache::archName() const
+{
+    const char* archSubString = ((char*)this) + 8;
+    while (*archSubString == ' ')
+        ++archSubString;
+    return archSubString;
+}
+
+
+uint32_t DyldSharedCache::platform() const
+{
+    return header.platform;
+}
+
+#if !DYLD_IN_PROCESS
+std::string DyldSharedCache::mapFile() const
+{
+    __block std::string             result;
+    __block std::vector<uint64_t>   regionStartAddresses;
+    __block std::vector<uint64_t>   regionSizes;
+    __block std::vector<uint64_t>   regionFileOffsets;
+
+    result.reserve(256*1024);
+    forEachRegion(^(const 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*)this);
+        char lineBuffer[256];
+        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 )
+            sprintf(lineBuffer, "mapping  %s %4lluMB 0x%0llX -> 0x%0llX\n", prot, size/(1024*1024), vmAddr, vmAddr+size);
+        else
+            sprintf(lineBuffer, "mapping  %s %4lluKB 0x%0llX -> 0x%0llX\n", prot, size/1024,        vmAddr, vmAddr+size);
+        result += lineBuffer;
+    });
+
+    // TODO:  add linkedit breakdown
+    result += "\n\n";
+
+    forEachImage(^(const mach_header* mh, const char* installName) {
+        result += std::string(installName) + "\n";
+        dyld3::MachOParser parser(mh);
+        parser.forEachSegment(^(const char* segName, uint32_t fileOffset, uint32_t fileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, bool& stop) {
+            char lineBuffer[256];
+            sprintf(lineBuffer, "\t%16s 0x%08llX -> 0x%08llX\n", segName, vmAddr, vmAddr+vmSize);
+            result += lineBuffer;
+        });
+        result += "\n";
+    });
+
+    return result;
+}
+#endif
+
+
+uint64_t DyldSharedCache::unslidLoadAddress() const
+{
+    const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((char*)this + header.mappingOffset);
+    return mappings[0].address;
+}
+
+void DyldSharedCache::getUUID(uuid_t uuid) const
+{
+    memcpy(uuid, header.uuid, sizeof(uuid_t));
+}
+
+uint64_t DyldSharedCache::mappedSize() const
+{
+    __block uint64_t startAddr = 0;
+    __block uint64_t endAddr = 0;
+    forEachRegion(^(const void* content, uint64_t vmAddr, uint64_t size, uint32_t permissions) {
+        if ( startAddr == 0 )
+            startAddr = vmAddr;
+        uint64_t end = vmAddr+size;
+        if ( end > endAddr )
+            endAddr = end;
+    });
+    return (endAddr - startAddr);
+}
+
+
+
+
+
+
+
+
+
+
diff --git a/dyld3/shared-cache/DyldSharedCache.h b/dyld3/shared-cache/DyldSharedCache.h
new file mode 100644 (file)
index 0000000..2d84065
--- /dev/null
@@ -0,0 +1,213 @@
+/*
+ * Copyright (c) 2017 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 DyldSharedCache_h
+#define DyldSharedCache_h
+
+#include <set>
+#include <string>
+#include <vector>
+#include <unordered_map>
+
+#include "dyld_cache_format.h"
+#include "Diagnostics.h"
+#include "MachOParser.h"
+
+
+namespace  dyld3 {
+  namespace launch_cache {
+    namespace binary_format {
+      struct Closure;
+      struct ImageGroup;
+      struct Image;
+    }
+  }
+}
+
+
+class VIS_HIDDEN DyldSharedCache
+{
+public:
+
+    enum CodeSigningDigestMode
+    {
+        SHA256only = 0,
+        SHA1only   = 1,
+        Agile      = 2
+    };
+
+    struct CreateOptions
+    {
+        std::string                                 archName;
+        dyld3::Platform                             platform;
+        bool                                        excludeLocalSymbols;
+        bool                                        optimizeStubs;
+        bool                                        optimizeObjC;
+        CodeSigningDigestMode                       codeSigningDigestMode;
+        bool                                        agileSignatureChooseSHA256CdHash;
+        bool                                        dylibsRemovedDuringMastering;
+        bool                                        inodesAreSameAsRuntime;
+        bool                                        cacheSupportsASLR;
+        bool                                        forSimulator;
+        bool                                        verbose;
+        bool                                        evictLeafDylibsOnOverflow;
+        std::unordered_map<std::string, unsigned>   dylibOrdering;
+        std::unordered_map<std::string, unsigned>   dirtyDataSegmentOrdering;
+        std::vector<std::string>                    pathPrefixes;
+        std::string                                 loggingPrefix;
+    };
+
+    struct MappedMachO
+    {
+                                    MappedMachO()
+                                            : mh(nullptr), length(0), isSetUID(false), protectedBySIP(false), sliceFileOffset(0), modTime(0), inode(0) { }
+                                    MappedMachO(const std::string& path, const mach_header* p, size_t l, bool isu, bool sip, uint64_t o, uint64_t m, uint64_t i)
+                                            : runtimePath(path), mh(p), length(l), isSetUID(isu), protectedBySIP(sip), sliceFileOffset(o), modTime(m), inode(i) { }
+
+        std::string                 runtimePath;
+        const mach_header*          mh;
+        size_t                      length;
+        uint64_t                    isSetUID        :  1,
+                                    protectedBySIP  :  1,
+                                    sliceFileOffset : 62;
+        uint64_t                    modTime;                // only recorded if inodesAreSameAsRuntime
+        uint64_t                    inode;                  // only recorded if inodesAreSameAsRuntime
+    };
+
+    struct CreateResults
+    {
+        const DyldSharedCache*      cacheContent    = nullptr;    // caller needs to vm_deallocate() when done
+        size_t                      cacheLength     = 0;
+        std::string                 errorMessage;
+        std::set<std::string>       warnings;
+        bool                        agileSignature = false;
+        std::string                 cdHashFirst;
+        std::string                 cdHashSecond;
+        bool                        overflowed = false;
+    };
+
+
+    // This function verifies the set of dylibs that will go into the cache are self contained.  That the depend on no dylibs
+    // outset the set.  It will call back the loader function to try to find any mising dylibs.
+    static bool verifySelfContained(std::vector<MappedMachO>& dylibsToCache, MappedMachO (^loader)(const std::string& runtimePath), std::vector<std::pair<DyldSharedCache::MappedMachO, std::set<std::string>>>& excluded);
+
+
+    //
+    // This function is single threaded and creates a shared cache. The cache file is created in-memory.
+    //
+    // Inputs:
+    //      options:        various per-platform flags
+    //      dylibsToCache:  a list of dylibs to include in the cache
+    //      otherOsDylibs:  a list of other OS dylibs and bundle which should have load info added to the cache
+    //      osExecutables:  a list of main executables which should have closures created in the cache
+    //
+    // Returns:
+    //    On success:
+    //         cacheContent: start of the allocated cache buffer which must be vm_deallocated after the caller writes out the buffer.
+    //         cacheLength:  size of the allocated cache buffer
+    //         cdHash:       hash of the code directory of the code blob of the created cache
+    //         warnings:     all warning messsages generated during the creation of the cache
+    //
+    //    On failure:
+    //         cacheContent: nullptr
+    //         errorMessage: the string describing why the cache could not be created
+    //         warnings:     all warning messsages generated before the failure
+    //
+    static CreateResults create(const CreateOptions&             options,
+                                const std::vector<MappedMachO>&  dylibsToCache,
+                                const std::vector<MappedMachO>&  otherOsDylibs,
+                                const std::vector<MappedMachO>&  osExecutables);
+
+
+    //
+    // Returns a text "map" file as a big string
+    //
+    std::string         mapFile() const;
+
+
+    //
+    // Returns the architecture name of the shared cache, e.g. "arm64"
+    //
+    std::string         archName() const;
+
+
+    //
+    // Returns the platform the cache is for
+    //
+    uint32_t            platform() const;
+
+
+    //
+    // Iterates over each dylib in the cache
+    //
+    void                forEachImage(void (^handler)(const mach_header* mh, const char* installName)) const;
+
+
+    //
+    // Iterates over each dylib in the cache
+    //
+    void                forEachImageEntry(void (^handler)(const char* path, uint64_t mTime, uint64_t inode)) const;
+
+
+    //
+    // Iterates over each dylib in the cache
+    //
+    void                forEachImageTextSegment(void (^handler)(uint64_t loadAddressUnslid, uint64_t textSegmentSize, const uuid_t dylibUUID, const char* installName)) const;
+
+
+    //
+    // Iterates over each of the three regions in the cache
+    //
+    void                forEachRegion(void (^handler)(const void* content, uint64_t vmAddr, uint64_t size, uint32_t permissions)) const;
+
+
+    //
+    // returns address the cache would load at if unslid
+    //
+    uint64_t            unslidLoadAddress() const;
+
+
+    //
+    // returns UUID of cache
+    //
+    void                getUUID(uuid_t uuid) const;
+
+
+    //
+    // returns the vm size required to map cache
+    //
+    uint64_t            mappedSize() const;
+
+
+    dyld_cache_header header;
+};
+
+
+
+
+
+
+
+
+#endif /* DyldSharedCache_h */
diff --git a/dyld3/shared-cache/FileAbstraction.hpp b/dyld3/shared-cache/FileAbstraction.hpp
new file mode 100644 (file)
index 0000000..1d73d74
--- /dev/null
@@ -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 <stdint.h>
+#include <string.h>
+#include <libkern/OSByteOrder.h>
+
+#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<LittleEndian>
+//
+//
+//             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<<bitCount)-1)); }
+       static void             setBitsRaw(uint32_t& into, uint32_t value,
+                                               uint8_t firstBit, uint8_t bitCount)     INLINE { uint32_t temp = into; 
+                                                                                                                                                                                       const uint32_t mask = ((1<<bitCount)-1); 
+                                                                                                                                                                                       temp &= ~(mask << (32-firstBit-bitCount)); 
+                                                                                                                                                                                       temp |= ((value & mask) << (32-firstBit-bitCount)); 
+                                                                                                                                                                                       into = temp; }
+       enum { little_endian = 0 };
+};
+
+
+class LittleEndian
+{
+public:
+       static uint16_t get16(const uint16_t& from)                             INLINE { return OSReadLittleInt16(&from, 0); }
+       static void             set16(uint16_t& into, uint16_t value)   INLINE { OSWriteLittleInt16(&into, 0, value); }
+       
+       static uint32_t get32(const uint32_t& from)                             INLINE { return OSReadLittleInt32(&from, 0); }
+       static void             set32(uint32_t& into, uint32_t value)   INLINE { OSWriteLittleInt32(&into, 0, value); }
+       
+       static int32_t  get32(const int32_t& from)                              INLINE { return OSReadLittleInt32(&from, 0); }
+       static void             set32(int32_t& into, int32_t value)             INLINE { OSWriteLittleInt32(&into, 0, value); }
+       
+       static uint64_t get64(const uint64_t& from)                             INLINE { return OSReadLittleInt64(&from, 0); }
+       static void             set64(uint64_t& into, uint64_t value)   INLINE { OSWriteLittleInt64(&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 >> firstBit) & ((1<<bitCount)-1)); }
+       static void             setBitsRaw(uint32_t& into, uint32_t value,
+                                               uint8_t firstBit, uint8_t bitCount)     INLINE {  uint32_t temp = into; 
+                                                                                                                                                                                       const uint32_t mask = ((1<<bitCount)-1); 
+                                                                                                                                                                                       temp &= ~(mask << firstBit); 
+                                                                                                                                                                                       temp |= ((value & mask) << firstBit); 
+                                                                                                                                                                                       into = temp; }
+       enum { little_endian = 1 };
+};
+
+
+template <typename _E>
+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 <typename T>
+    static T round_up(T value) { return (value+3) & ~(T)3; }
+    template <typename T>
+    static T round_down(T value) { return value & ~(T)3; }
+};
+
+
+template <typename _E>
+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 <typename T>
+    static T round_up(T value) { return (value+7) & ~(T)7; }
+    template <typename T>
+    static T round_down(T value) { return value & ~(T)7; }
+};
+
+
+
+
+
+#endif // __FILE_ABSTRACTION__
+
+
diff --git a/dyld3/shared-cache/FileUtils.cpp b/dyld3/shared-cache/FileUtils.cpp
new file mode 100644 (file)
index 0000000..19f02a4
--- /dev/null
@@ -0,0 +1,414 @@
+/*
+ * Copyright (c) 2017 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 <string.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <sys/mman.h>
+#include <dispatch/dispatch.h>
+#include <mach-o/dyld.h>
+#include <System/sys/csr.h>
+#include <rootless.h>
+
+#include <string>
+#include <fstream>
+#include <sstream>
+
+#include "FileUtils.h"
+#include "Diagnostics.h"
+
+#if __MAC_OS_X_VERSION_MIN_REQUIRED < 101200
+extern "C" int rootless_check_trusted_fd(int fd) __attribute__((weak_import));
+#endif
+
+
+void iterateDirectoryTree(const std::string& pathPrefix, const std::string& path, bool (^dirFilter)(const std::string& path), void (^fileCallback)(const std::string& path, const struct stat&), bool processFiles)
+{
+    std::string fullDirPath = pathPrefix + path;
+    DIR* dir = ::opendir(fullDirPath.c_str());
+    if ( dir == nullptr ) {
+        //fprintf(stderr, "can't read 'dir '%s', errno=%d\n", inputPath.c_str(), errno);
+        return;
+    }
+    while (dirent* entry = readdir(dir)) {
+        struct stat statBuf;
+        std::string dirAndFile = path + "/" + entry->d_name;
+        std::string fullDirAndFile = pathPrefix + dirAndFile;
+         switch ( entry->d_type ) {
+            case DT_REG:
+                if ( processFiles ) {
+                    if ( ::lstat(fullDirAndFile.c_str(), &statBuf) == -1 )
+                        break;
+                    if ( ! S_ISREG(statBuf.st_mode)  )
+                        break;
+                    fileCallback(dirAndFile, statBuf);
+                }
+                break;
+            case DT_DIR:
+                if ( strcmp(entry->d_name, ".") == 0 )
+                    break;
+                if ( strcmp(entry->d_name, "..") == 0 )
+                    break;
+                if ( dirFilter(dirAndFile) )
+                    break;
+                iterateDirectoryTree(pathPrefix, dirAndFile, dirFilter, fileCallback);
+                break;
+            case DT_LNK:
+                // don't follow symlinks, dylib will be found through absolute path
+                break;
+        }
+    }
+    ::closedir(dir);
+}
+
+
+bool safeSave(const void* buffer, size_t bufferLen, const std::string& path)
+{
+    std::string pathTemplate = path + "-XXXXXX";
+    size_t templateLen = strlen(pathTemplate.c_str())+2;
+    char pathTemplateSpace[templateLen];
+    strlcpy(pathTemplateSpace, pathTemplate.c_str(), templateLen);
+    int fd = mkstemp(pathTemplateSpace);
+    if ( fd != -1 ) {
+        ssize_t writtenSize = pwrite(fd, buffer, bufferLen, 0);
+        if ( writtenSize == bufferLen ) {
+            ::fchmod(fd, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH); // mkstemp() makes file "rw-------", switch it to "rw-r--r--"
+            if ( ::rename(pathTemplateSpace, path.c_str()) == 0) {
+                ::close(fd);
+                return true; // success
+            }
+        }
+        ::close(fd);
+        ::unlink(pathTemplateSpace);
+    }
+    return false; // failure
+}
+
+const void* mapFileReadOnly(const std::string& path, size_t& mappedSize)
+{
+    struct stat statBuf;
+    if ( ::stat(path.c_str(), &statBuf) != 0 )
+        return nullptr;
+
+    int fd = ::open(path.c_str(), O_RDONLY);
+    if ( fd < 0 )
+        return nullptr;
+
+    const void *p = ::mmap(NULL, (size_t)statBuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+    ::close(fd);
+    if ( p != MAP_FAILED ) {
+        mappedSize = (size_t)statBuf.st_size;
+        return p;
+    }
+
+    return nullptr;
+}
+
+static bool sipIsEnabled()
+{
+    static bool             rootlessEnabled;
+    static dispatch_once_t  onceToken;
+    // Check to make sure file system protections are on at all
+    dispatch_once(&onceToken, ^{
+        rootlessEnabled = (csr_check(CSR_ALLOW_UNRESTRICTED_FS) != 0);
+    });
+    return rootlessEnabled;
+}
+
+bool isProtectedBySIP(const std::string& path)
+{
+    if ( !sipIsEnabled() )
+        return false;
+
+    return (rootless_check_trusted(path.c_str()) == 0);
+}
+
+bool isProtectedBySIP(int fd)
+{
+    if ( !sipIsEnabled() )
+        return false;
+
+#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101200
+    return (rootless_check_trusted_fd(fd) == 0);
+#else
+    // fallback to using rootless_check_trusted
+    char realPath[MAXPATHLEN];
+    if ( fcntl(fd, F_GETPATH, realPath) == 0 )
+        return (rootless_check_trusted(realPath) == 0);
+    return false;
+#endif
+}
+
+bool fileExists(const std::string& path)
+{
+    struct stat statBuf;
+    return ( ::stat(path.c_str(), &statBuf) == 0 );
+}
+
+// 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.
+std::unordered_map<std::string, uint32_t> loadOrderFile(const std::string& orderFile) {
+    std::unordered_map<std::string, uint32_t> 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();
+    }
+
+    return order;
+}
+
+
+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 basePath(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 realPath(const std::string& path)
+{
+    char resolvedPath[PATH_MAX];
+    if (realpath(dirPath(path).c_str(), &resolvedPath[0]) != nullptr) {
+        return std::string(resolvedPath) + "/" + basePath(path);
+    } else {
+        return "";
+    }
+}
+
+std::string realFilePath(const std::string& path)
+{
+    char resolvedPath[PATH_MAX];
+    if ( realpath(path.c_str(), resolvedPath) != nullptr )
+        return std::string(resolvedPath);
+    else
+        return "";
+}
+
+
+std::string normalize_absolute_file_path(std::string path) {
+    std::vector<std::string> components;
+    std::vector<std::string> 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;
+}
+
+
+#if BUILDING_CACHE_BUILDER
+
+FileCache fileCache;
+
+FileCache::FileCache(void)
+{
+    cache_queue = dispatch_queue_create("com.apple.dyld.cache.cache", dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, QOS_CLASS_USER_INITIATED, 0));
+}
+
+void FileCache::preflightCache(Diagnostics& diags, const std::unordered_set<std::string>& paths)
+{
+    for (auto& path : paths) {
+        preflightCache(diags, path);
+    }
+}
+
+void FileCache::preflightCache(Diagnostics& diags, const std::string& path)
+{
+    dispatch_async(cache_queue, ^{
+        std::string normalizedPath = normalize_absolute_file_path(path);
+        if (entries.count(normalizedPath) == 0) {
+            entries[normalizedPath] = fill(diags, normalizedPath);
+        }
+    });
+}
+
+std::pair<uint8_t*, struct stat> FileCache::cacheLoad(Diagnostics& diags, const std::string path)
+{
+    __block bool found = false;
+    __block std::pair<uint8_t*, struct stat> retval;
+    std::string normalizedPath = normalize_absolute_file_path(path);
+    dispatch_sync(cache_queue, ^{
+        auto entry = entries.find(normalizedPath);
+        if (entry != entries.end()) {
+            retval = entry->second;
+            found = true;
+        }
+    });
+
+    if (!found) {
+        auto info = fill(diags, normalizedPath);
+        dispatch_sync(cache_queue, ^{
+            auto entry = entries.find(normalizedPath);
+            if (entry != entries.end()) {
+                retval = entry->second;
+            } else {
+                retval = entries[normalizedPath] = info;
+                retval = info;
+            }
+        });
+    }
+
+    return retval;
+}
+
+//FIXME error handling
+std::pair<uint8_t*, struct stat> FileCache::fill(Diagnostics& diags, const std::string& path)
+{
+    void* buffer_ptr = nullptr;
+    struct stat stat_buf;
+    struct statfs statfs_buf;
+    bool localcopy = true;
+
+    int fd = ::open(path.c_str(), O_RDONLY, 0);
+    if (fd == -1) {
+        diags.verbose("can't open file '%s', errno=%d\n", path.c_str(), errno);
+        return std::make_pair((uint8_t*)(-1), stat_buf);
+    }
+
+    if (fstat(fd, &stat_buf) == -1) {
+        diags.verbose("can't stat open file '%s', errno=%d\n", path.c_str(), errno);
+        ::close(fd);
+        return std::make_pair((uint8_t*)(-1), stat_buf);
+    }
+
+    if (stat_buf.st_size < 4096) {
+        diags.verbose("file too small '%s'\n", path.c_str());
+        ::close(fd);
+        return std::make_pair((uint8_t*)(-1), stat_buf);
+    }
+
+    if(fstatfs(fd, &statfs_buf) == 0) {
+        std::string fsName = statfs_buf.f_fstypename;
+        if (fsName == "hfs" || fsName == "apfs") {
+            localcopy = false;
+        }
+    }
+    
+    if (!localcopy) {
+        buffer_ptr = mmap(NULL, (size_t)stat_buf.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+        if (buffer_ptr == MAP_FAILED) {
+            diags.verbose("mmap() for file at %s failed, errno=%d\n", path.c_str(), errno);
+            ::close(fd);
+            return std::make_pair((uint8_t*)(-1), stat_buf);
+        }
+    } else {
+        buffer_ptr = malloc((size_t)stat_buf.st_size);
+        ssize_t readBytes = pread(fd, buffer_ptr, (size_t)stat_buf.st_size, 0);
+        if (readBytes == -1) {
+            diags.verbose("Network read for file at %s failed, errno=%d\n", path.c_str(), errno);
+            ::close(fd);
+            return std::make_pair((uint8_t*)(-1), stat_buf);
+        } else if (readBytes != stat_buf.st_size) {
+            diags.verbose("Network read udnerrun for file at %s, expected %lld bytes, got  %zd bytes\n", path.c_str(), stat_buf.st_size, readBytes);
+            ::close(fd);
+            return std::make_pair((uint8_t*)(-1), stat_buf);
+        }
+    }
+
+    ::close(fd);
+
+    return std::make_pair((uint8_t*)buffer_ptr, stat_buf);
+}
+
+#endif // BUILDING_CACHE_BUILDER
+
diff --git a/dyld3/shared-cache/FileUtils.h b/dyld3/shared-cache/FileUtils.h
new file mode 100644 (file)
index 0000000..dbf6ae4
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2017 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 FileUtils_h
+#define FileUtils_h
+
+#include <stdint.h>
+
+#include <string>
+#include <vector>
+#include <unordered_map>
+#include <unordered_set>
+#include <dispatch/dispatch.h>
+
+class Diagnostics;
+
+#if BUILDING_CACHE_BUILDER
+struct FileCache {
+    FileCache(void);
+    std::pair<uint8_t*, struct stat> cacheLoad(Diagnostics& diags, const std::string path);
+    void preflightCache(Diagnostics& diags, const std::string& path);
+    void preflightCache(Diagnostics& diags, const std::unordered_set<std::string>& paths);
+
+private:
+    std::pair<uint8_t*, struct stat> fill(Diagnostics& diags, const std::string& path);
+
+    std::unordered_map<std::string, std::pair<uint8_t*, struct stat>> entries;
+    dispatch_queue_t cache_queue;
+};
+
+extern FileCache fileCache;
+#endif
+
+//
+// recursively walk all files in a directory tree
+// symlinks are ignored
+// dirFilter should return true on directories which should not be recursed into
+// callback is called on each regular file found with stat() info about the file
+//
+void iterateDirectoryTree(const std::string& pathPrefix, const std::string& path, bool (^dirFilter)(const std::string& dirPath),
+                          void (^callback)(const std::string& path, const struct stat& statBuf), bool processFiles=true);
+
+
+//
+// writes the buffer to a temp file, then renames the file to the final path
+// returns true on success
+//
+bool safeSave(const void* buffer, size_t bufferLen, const std::string& path);
+
+
+const void* mapFileReadOnly(const std::string& path, size_t& mappedSize);
+
+bool isProtectedBySIP(const std::string& path);
+bool isProtectedBySIP(int fd);
+
+bool fileExists(const std::string& path);
+
+std::unordered_map<std::string, uint32_t> loadOrderFile(const std::string& orderFile);
+
+std::string normalize_absolute_file_path(std::string path);
+std::string basePath(const std::string& path);
+std::string dirPath(const std::string& path);
+std::string realPath(const std::string& path);
+std::string realFilePath(const std::string& path);
+
+std::string toolDir();
+
+
+
+
+#endif // FileUtils_h
diff --git a/dyld3/shared-cache/ImageProxy.cpp b/dyld3/shared-cache/ImageProxy.cpp
new file mode 100644 (file)
index 0000000..fcd6db5
--- /dev/null
@@ -0,0 +1,2366 @@
+/*
+ * Copyright (c) 2017 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 <string.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <sys/errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/mman.h>
+#include <mach/mach_vm.h>
+#include <mach-o/dyld.h>
+#include <mach-o/dyld_priv.h>
+#include <uuid/uuid.h>
+#include <os/log.h>
+
+#include <string>
+#include <vector>
+#include <array>
+
+#include "ImageProxy.h"
+#include "FileUtils.h"
+#include "StringUtils.h"
+#include "MachOParser.h"
+#include "LaunchCacheFormat.h"
+#include "LaunchCacheWriter.h"
+#include "PathOverrides.h"
+#include "libdyldEntryVector.h"
+
+namespace dyld3 {
+
+typedef launch_cache::TargetSymbolValue   TargetSymbolValue;
+
+
+
+///////////////////////////  ImageProxy  ///////////////////////////
+
+ImageProxy::ImageProxy(const mach_header* mh, const BinaryImageData* imageData, uint32_t indexInGroup, bool dyldCacheIsRaw)
+ : _mh(mh), _sliceFileOffset(0), _modTime(0), _inode(0), _imageBinaryData(imageData), _runtimePath(launch_cache::Image(imageData).path()),
+   _groupNum(0), _indexInGroup(indexInGroup), _isSetUID(false), _dyldCacheIsRaw(dyldCacheIsRaw), _platformBinary(false), _overrideOf(ImageRef::weakImportMissing()),
+   _directDependentsSet(false), _deepDependentsSet(false), _initBeforesArraySet(false), _initBeforesComputed(false),
+   _invalid(launch_cache::Image(imageData).isInvalid()), _staticallyReferenced(false), _cwdMustBeThisDir(false)
+{
+}
+
+ImageProxy::ImageProxy(const DyldSharedCache::MappedMachO& mapping, uint32_t groupNum, uint32_t indexInGroup, bool dyldCacheIsRaw)
+ : _mh(mapping.mh), _sliceFileOffset(mapping.sliceFileOffset), _modTime(mapping.modTime), _inode(mapping.inode), _imageBinaryData(nullptr), _runtimePath(mapping.runtimePath),
+   _groupNum(groupNum), _indexInGroup(indexInGroup), _isSetUID(mapping.isSetUID), _dyldCacheIsRaw(dyldCacheIsRaw), _platformBinary(mapping.protectedBySIP),
+   _overrideOf(ImageRef::weakImportMissing()), _directDependentsSet(false), _deepDependentsSet(false), _initBeforesArraySet(false), _initBeforesComputed(false),
+   _invalid(false), _staticallyReferenced(false), _cwdMustBeThisDir(false)
+{
+}
+
+
+void ImageProxy::processRPaths(ImageProxyGroup& owningGroup)
+{
+    // parse LC_RPATH
+    __block std::unordered_set<std::string> rawRpaths;
+    MachOParser parser(_mh, _dyldCacheIsRaw);
+    parser.forEachRPath(^(const char* rpath, bool& stop) {
+        if ( rawRpaths.count(rpath) ) {
+            _diag.warning("duplicate LC_RPATH (%s) in %s", rpath, _runtimePath.c_str());
+            return;
+        }
+        rawRpaths.insert(rpath);
+        std::string thisRPath = rpath;
+        if ( startsWith(thisRPath, "@executable_path/") ) {
+            std::string mainPath = owningGroup.mainProgRuntimePath();
+            if ( mainPath.empty() && parser.isDynamicExecutable() ) {
+                mainPath = _runtimePath;
+            }
+            if ( !mainPath.empty() ) {
+                std::string newPath = mainPath.substr(0, mainPath.rfind('/')+1) + thisRPath.substr(17);
+                std::string normalizedPath = owningGroup.normalizedPath(newPath);
+                if ( fileExists(normalizedPath) )
+                    _rpaths.push_back(normalizedPath);
+                else
+                    _diag.warning("LC_RPATH to nowhere (%s) in %s", rpath, _runtimePath.c_str());
+                char resolvedMainPath[PATH_MAX];
+                if ( (realpath(mainPath.c_str(), resolvedMainPath) != nullptr) && (mainPath.c_str() != resolvedMainPath) ) {
+                    std::string realMainPath = resolvedMainPath;
+                    size_t lastSlashPos = realMainPath.rfind('/');
+                    std::string newRealPath = realMainPath.substr(0, lastSlashPos+1) + thisRPath.substr(17);
+                    if ( realMainPath != mainPath ) {
+                        for (const std::string& pre : owningGroup._buildTimePrefixes) {
+                            std::string aPath = owningGroup.normalizedPath(pre + newRealPath);
+                            if ( fileExists(aPath) ) {
+                                _rpaths.push_back(owningGroup.normalizedPath(newRealPath));
+                            }
+                        }
+                    }
+                }
+            }
+            else {
+                _diag.warning("LC_RPATH uses @executable_path in %s", _runtimePath.c_str());
+            }
+        }
+        else if ( thisRPath == "@executable_path" ) {
+            std::string mainPath = owningGroup.mainProgRuntimePath();
+            if ( mainPath.empty() && parser.isDynamicExecutable() ) {
+                mainPath = _runtimePath;
+            }
+            if ( !mainPath.empty() ) {
+                std::string newPath = mainPath.substr(0, mainPath.rfind('/')+1);
+                std::string normalizedPath = owningGroup.normalizedPath(newPath);
+                _rpaths.push_back(normalizedPath);
+            }
+            else {
+                _diag.warning("LC_RPATH uses @executable_path in %s", _runtimePath.c_str());
+            }
+        }
+        else if ( startsWith(thisRPath, "@loader_path/") ) {
+            size_t lastSlashPos = _runtimePath.rfind('/');
+            std::string newPath = _runtimePath.substr(0, lastSlashPos+1) + thisRPath.substr(13);
+            bool found = false;
+            for (const std::string& pre : owningGroup._buildTimePrefixes) {
+                std::string aPath = owningGroup.normalizedPath(pre + newPath);
+                if ( fileExists(aPath) ) {
+                    _rpaths.push_back(owningGroup.normalizedPath(newPath));
+                    found = true;
+                    break;
+                }
+            }
+            char resolvedPath[PATH_MAX];
+            if ( (realpath(_runtimePath.c_str(), resolvedPath) != nullptr) && (_runtimePath.c_str() != resolvedPath) ) {
+                std::string realRunPath = resolvedPath;
+                lastSlashPos = realRunPath.rfind('/');
+                std::string newRealPath = realRunPath.substr(0, lastSlashPos+1) + thisRPath.substr(13);
+                if ( newRealPath != newPath ) {
+                    for (const std::string& pre : owningGroup._buildTimePrefixes) {
+                        std::string aPath = owningGroup.normalizedPath(pre + newRealPath);
+                        if ( fileExists(aPath) ) {
+                            _rpaths.push_back(owningGroup.normalizedPath(newRealPath));
+                            found = true;
+                            break;
+                        }
+                    }
+                }
+            }
+            if ( !found ) {
+                // even though this path does not exist, we need to add it to must-be-missing paths
+                // in case it shows up at launch time
+                _rpaths.push_back(owningGroup.normalizedPath(newPath));
+                _diag.warning("LC_RPATH to nowhere (%s) in %s", rpath, _runtimePath.c_str());
+            }
+        }
+        else if ( thisRPath == "@loader_path" ) {
+            size_t lastSlashPos = _runtimePath.rfind('/');
+            std::string newPath = _runtimePath.substr(0, lastSlashPos+1);
+            std::string normalizedPath = owningGroup.normalizedPath(newPath);
+            _rpaths.push_back(normalizedPath);
+        }
+        else if ( rpath[0] == '@' ) {
+            _diag.warning("LC_RPATH with unknown @ variable (%s) in %s", rpath, _runtimePath.c_str());
+        }
+        else {
+            if ( rpath[0] == '/' )
+                _diag.warning("LC_RPATH is absolute path (%s) in %s", rpath, _runtimePath.c_str());
+            _rpaths.push_back(rpath);
+        }
+    });
+    //if ( !_rpaths.empty() ) {
+    //    fprintf(stderr, "for %s\n", _runtimePath.c_str());
+    //    for (const std::string& p : _rpaths)
+    //        fprintf(stderr, "   %s\n", p.c_str());
+    //}
+}
+
+void ImageProxy::addDependentsDeep(ImageProxyGroup& owningGroup, RPathChain* prev, bool staticallyReferenced)
+{
+    // mark binaries that are statically referenced and thus will never be unloaded
+    if ( staticallyReferenced )
+        _staticallyReferenced = true;
+
+    if ( _deepDependentsSet )
+        return;
+
+    // find all immediate dependents
+    addDependentsShallow(owningGroup, prev);
+    if ( _diag.hasError() ) {
+        _invalid = true;
+        return;
+    }
+
+    // recurse though each dependent
+    RPathChain rchain = { this, prev, _rpaths };
+    for (ImageProxy* proxy : _dependents) {
+        if ( proxy == nullptr )
+            continue; // skip over weak missing dependents
+        if ( !proxy->_directDependentsSet )
+            proxy->addDependentsDeep(owningGroup, &rchain, staticallyReferenced);
+        if ( proxy->invalid() )
+            _invalid = true;
+    }
+
+    _deepDependentsSet = true;
+}
+
+void ImageProxy::addDependentsShallow(ImageProxyGroup& owningGroup, RPathChain* prev)
+{
+    if ( _directDependentsSet )
+        return;
+
+    MachOParser thisParser(mh(), _dyldCacheIsRaw);
+    dyld3::Platform thisPlatform = thisParser.platform();
+
+    processRPaths(owningGroup);
+    __block RPathChain rchain = { this, prev, _rpaths };
+
+    thisParser.forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool &stop) {
+        if ( (loadPath[0] != '/') && (loadPath[0] != '@') ) {
+            _diag.warning("load path is file system relative (%s) in %s", loadPath, runtimePath().c_str());
+        }
+        Diagnostics depDiag;
+        ImageProxy* dep = owningGroup.findImage(depDiag, loadPath, isWeak, &rchain);
+        if ( (dep == nullptr) || dep->invalid() ) {
+            if (isWeak) {
+                // weak link against a broken dylib, pretend dylib is not there
+                dep = nullptr;
+            } else {
+                if ( depDiag.warnings().empty() ) {
+                    if ( thisParser.header()->filetype == MH_EXECUTE )
+                        _diag.error("required dylib '%s' not found", loadPath);
+                    else
+                        _diag.error("required dylib '%s' not found, needed by '%s'", loadPath, runtimePath().c_str());
+                }
+                else {
+                    std::string allTries;
+                    for (const std::string& warn : depDiag.warnings()) {
+                        if ( allTries.empty() )
+                            allTries = warn;
+                        else
+                            allTries = allTries + ", " + warn;
+                    }
+                    _diag.error("required dylib '%s' not found, needed by '%s'.  Did try: %s", loadPath, runtimePath().c_str(), allTries.c_str());
+                }
+            }
+        }
+        else {
+            MachOParser depParser(dep->mh(), _dyldCacheIsRaw);
+            if ( _diag.noError() ) {
+                // verify found image has compatible version and matching platform
+                dyld3::Platform depPlatform = depParser.platform();
+                if ( depPlatform != thisPlatform ) {
+                    // simulator allows a few macOS libSystem dylibs
+                    if ( !inLibSystem() || !dep->inLibSystem() ) {
+                        _diag.error("found '%s' but it was built for different platform '%s' than required '%s'.  Needed by '%s'", dep->runtimePath().c_str(),
+                                    MachOParser::platformName(depPlatform).c_str(), MachOParser::platformName(thisPlatform).c_str(), runtimePath().c_str());
+                    }
+                }
+            }
+            if ( _diag.noError() ) {
+                // verify compat version
+                const char* installName;
+                uint32_t    foundCompatVers;
+                uint32_t    foundCurrentVers;
+                if ( depParser.header()->filetype != MH_DYLIB ) {
+                    _diag.error("found '%s' which is not a dylib.  Needed by '%s'", dep->runtimePath().c_str(), runtimePath().c_str());
+                }
+                else {
+                    depParser.getDylibInstallName(&installName, &foundCompatVers, &foundCurrentVers);
+                    if ( foundCompatVers < compatVersion ) {
+                        _diag.error("found '%s' which has compat version (%s) which is less than required (%s).  Needed by '%s'", dep->runtimePath().c_str(),
+                                    MachOParser::versionString(foundCompatVers).c_str(), MachOParser::versionString(compatVersion).c_str(), runtimePath().c_str());
+                    }
+                }
+            }
+        }
+        if ( _diag.hasError() ) {
+            stop = true;
+            _invalid = true;
+        }
+        _dependents.push_back(dep);
+        if ( isWeak )
+            _dependentsKind.push_back(launch_cache::Image::LinkKind::weak);
+        else if ( isReExport )
+            _dependentsKind.push_back(launch_cache::Image::LinkKind::reExport);
+        else if ( isUpward )
+            _dependentsKind.push_back(launch_cache::Image::LinkKind::upward);
+        else
+            _dependentsKind.push_back(launch_cache::Image::LinkKind::regular);
+    });
+    _directDependentsSet = true;
+}
+
+bool ImageProxy::inLibSystem() const
+{
+    return startsWith(runtimePath(), "/usr/lib/system/") || startsWith(runtimePath(), "/usr/lib/libSystem.");
+}
+
+void ImageProxy::forEachDependent(void (^handler)(ImageProxy* dep, LinkKind)) const
+{
+    for (int i=0; i < _dependents.size(); ++i) {
+        handler(_dependents[i], _dependentsKind[i]);
+    }
+}
+
+
+bool ImageProxy::findExportedSymbol(Diagnostics& diag, const char* symbolName, MachOParser::FoundSymbol& foundInfo) const
+{
+    MachOParser parser(_mh, _dyldCacheIsRaw);
+    return parser.findExportedSymbol(diag, symbolName, (void*)this, foundInfo, ^(uint32_t depIndex, const char* depLoadPath, void* extra, const mach_header** foundMH, void** foundExtra) {
+        ImageProxy* proxy    = (ImageProxy*)extra;
+        if ( depIndex < proxy->_dependents.size() ) {
+            ImageProxy* depProxy = proxy->_dependents[depIndex];
+            *foundMH    = depProxy->_mh;
+            *foundExtra = (void*)depProxy;
+            return true;
+        }
+        return false;
+    });
+}
+
+bool ImageProxy::InitOrderInfo::beforeHas(ImageRef ref)
+{
+    ImageRef clearRef = ref;
+    clearRef.clearKind();
+    return ( std::find(initBefore.begin(), initBefore.end(), clearRef) != initBefore.end() );
+}
+
+bool ImageProxy::InitOrderInfo::upwardHas(ImageProxy* proxy)
+{
+    return ( std::find(danglingUpward.begin(), danglingUpward.end(), proxy) != danglingUpward.end() );
+}
+
+void ImageProxy::InitOrderInfo::removeRedundantUpwards()
+{
+    danglingUpward.erase(std::remove_if(danglingUpward.begin(), danglingUpward.end(),
+                                        [&](ImageProxy* proxy) {
+                                            ImageRef ref(0, proxy->_groupNum, proxy->_indexInGroup);
+                                            return beforeHas(ref);
+                                        }), danglingUpward.end());
+}
+
+
+//
+// Every image has a list of "init-before" which means if that image was dlopen()ed
+// here is the exact list of images to initialize in the exact order.  This makes
+// the runtime easy.  It just walks the init-before list in order and runs each
+// initializer if it has not already been run.
+//
+// The init-before list for each image is calculated based on the init-before list
+// of each of its dependents.  It simply starts with the list of its first dependent,
+// then appends the list of the next, removing entries already in the list, etc.
+// Lastly if the current image has an initializer, it is appended to its init-before list.
+//
+// To handle cycles, when recursing to get a dependent's init-before list, any image
+// whose list is still being calculated (cycle), just returns its list so far.
+//
+// Explicit upward links are handled in two parts.  First, in the algorithm described above,
+// all upward links are ignored, which works fine as long as anything upward linked is
+// downward linked at some point.  If not, it is called a "dangling upward link". Since
+// nothing depends on those, they are added to the end of the final init-before list.
+//
+
+void ImageProxy::recursiveBuildInitBeforeInfo(ImageProxyGroup& owningGroup)
+{
+    if ( _initBeforesComputed )
+        return;
+    _initBeforesComputed = true; // break cycles
+
+    if ( _imageBinaryData != nullptr ) {
+        assert(_groupNum == 0);
+        // if this is proxy for something in dyld cache, get its list from cache
+        // and parse list into befores and upwards
+        launch_cache::Image image(_imageBinaryData);
+        image.forEachInitBefore(^(launch_cache::binary_format::ImageRef ref) {
+            if ( (LinkKind)ref.kind() == LinkKind::upward ) {
+                ImageProxyGroup* groupP = &owningGroup;
+                while (groupP->_groupNum != 0)
+                    groupP = groupP->_nextSearchGroup;
+                launch_cache::ImageGroup dyldCacheGroup(groupP->_basedOn);
+                launch_cache::Image      dyldCacheImage = dyldCacheGroup.image(ref.indexInGroup());
+                Diagnostics diag;
+                ImageProxy* p = groupP->findAbsoluteImage(diag, dyldCacheImage.path(), false, false);
+                if ( diag.noError() )
+                    _initBeforesInfo.danglingUpward.push_back(p);
+            }
+            else {
+                _initBeforesInfo.initBefore.push_back(ref);
+            }
+        });
+    }
+    else {
+        // calculate init-before list for this image by merging init-before's of all its dependent dylibs
+        unsigned depIndex = 0;
+        for (ImageProxy* depProxy : _dependents) {
+            if ( depProxy == nullptr ) {
+                assert(_dependentsKind[depIndex] == LinkKind::weak);
+            }
+            else {
+                if ( _dependentsKind[depIndex] == LinkKind::upward ) {
+                    // if this upward link is already in the list, we ignore it.  Otherwise add to front of list
+                    if ( _initBeforesInfo.upwardHas(depProxy) ) {
+                        // already in upward list, do nothing
+                    }
+                    else {
+                        ImageRef ref(0, depProxy->_groupNum, depProxy->_indexInGroup);
+                        if ( _initBeforesInfo.beforeHas(ref) ) {
+                            // already in before list, do nothing
+                        }
+                        else {
+                            // add to upward list
+                            _initBeforesInfo.danglingUpward.push_back(depProxy);
+                        }
+                    }
+                }
+                else {
+                    // compute init-befores of downward dependents
+                    depProxy->recursiveBuildInitBeforeInfo(owningGroup);
+                    // merge befores from this downward link into current befores list
+                    for (ImageRef depInit : depProxy->_initBeforesInfo.initBefore) {
+                        if ( !_initBeforesInfo.beforeHas(depInit) )
+                            _initBeforesInfo.initBefore.push_back(depInit);
+                    }
+                    // merge upwards from this downward link into current befores list
+                    for (ImageProxy* upProxy : depProxy->_initBeforesInfo.danglingUpward) {
+                        ImageRef ref(0, upProxy->_groupNum, upProxy->_indexInGroup);
+                        if ( _initBeforesInfo.beforeHas(ref) ) {
+                            // already in current initBefore list, so ignore this upward
+                        }
+                        else if ( _initBeforesInfo.upwardHas(upProxy) ) {
+                            // already in current danglingUpward list, so ignore this upward
+                        }
+                        else {
+                            // append to current danglingUpward list
+                            _initBeforesInfo.danglingUpward.push_back(upProxy);
+                        }
+                    }
+                }
+            }
+            ++depIndex;
+        }
+        // eliminate any upward links added to befores list by some other dependent
+        _initBeforesInfo.removeRedundantUpwards();
+
+        // if this images has initializer(s) (or +load), add it to list
+        MachOParser parser(_mh, _dyldCacheIsRaw);
+        Diagnostics diag;
+        if ( parser.hasInitializer(diag) || parser.hasPlusLoadMethod(diag) ) {
+            launch_cache::binary_format::ImageRef ref(0, _groupNum, _indexInGroup);
+            _initBeforesInfo.initBefore.push_back(ref);
+        }
+
+        //fprintf(stderr, "info for (%d, %d) %s\n",  _group, _index, _runtimePath.c_str());
+        //for (ImageRef ref : _initBeforesInfo.initBefore)
+        //     fprintf(stderr, "   ref = {%d, %d, %d}\n", ref.kind(), ref.group(), ref.index());
+        //for (ImageProxy* p : _initBeforesInfo.danglingUpward)
+        //     fprintf(stderr, "   up = %s\n", p->runtimePath().c_str());
+    }
+}
+
+void ImageProxy::convertInitBeforeInfoToArray(ImageProxyGroup& owningGroup)
+{
+    if ( _initBeforesInfo.danglingUpward.empty() ) {
+        _initBeforesArray = _initBeforesInfo.initBefore;
+    }
+    else {
+        for (ImageRef ref : _initBeforesInfo.initBefore)
+            _initBeforesArray.push_back(ref);
+        bool inLibSys = inLibSystem();
+        for (ImageProxy* proxy : _initBeforesInfo.danglingUpward) {
+            // ignore upward dependendencies between stuff within libSystem.dylib
+            if ( inLibSys && proxy->inLibSystem() )
+                continue;
+            proxy->getInitBeforeList(owningGroup);
+            for (ImageRef depInit : proxy->_initBeforesInfo.initBefore) {
+                if ( std::find(_initBeforesArray.begin(), _initBeforesArray.end(), depInit) == _initBeforesArray.end() )
+                    _initBeforesArray.push_back(depInit);
+            }
+            ImageRef ref(0, proxy->_groupNum, proxy->_indexInGroup);
+            if ( std::find(_initBeforesArray.begin(), _initBeforesArray.end(), ref) == _initBeforesArray.end() )
+                _initBeforesArray.push_back(ref);
+        }
+    }
+    //fprintf(stderr, "final init-before info for %s\n", _runtimePath.c_str());
+    //for (ImageRef ref : _initBeforesArray) {
+    //    fprintf(stderr, "   ref = {%d, %d, %d}\n", ref.linkKind, ref.group, ref.index);
+    //}
+}
+
+const std::vector<ImageRef>& ImageProxy::getInitBeforeList(ImageProxyGroup& owningGroup)
+{
+    if ( !_initBeforesArraySet ) {
+        _initBeforesArraySet = true; // break cycles
+        recursiveBuildInitBeforeInfo(owningGroup);
+        convertInitBeforeInfoToArray(owningGroup);
+    }
+    return _initBeforesArray;
+}
+
+ImageProxy::FixupInfo ImageProxy::buildFixups(Diagnostics& diag, uint64_t cacheUnslideBaseAddress, launch_cache::ImageGroupWriter& groupWriter) const
+{
+    __block ImageProxy::FixupInfo info;
+    MachOParser image(_mh, _dyldCacheIsRaw);
+
+    // add fixup for each rebase
+    __block bool rebaseError = false;
+    image.forEachRebase(diag, ^(uint32_t segIndex, uint64_t segOffset, uint8_t type, bool& stop) {
+        dyld3::launch_cache::ImageGroupWriter::FixupType fixupType = launch_cache::ImageGroupWriter::FixupType::rebase;
+        switch ( type ) {
+            case REBASE_TYPE_POINTER:
+                fixupType = launch_cache::ImageGroupWriter::FixupType::rebase;
+                break;
+            case REBASE_TYPE_TEXT_ABSOLUTE32:
+                fixupType = launch_cache::ImageGroupWriter::FixupType::rebaseText;
+                info.hasTextRelocs = true;
+                break;
+            case REBASE_TYPE_TEXT_PCREL32:
+                diag.error("pcrel text rebasing not supported");
+                stop = true;
+                rebaseError = true;
+                break;
+            default:
+                diag.error("unknown rebase type");
+                stop = true;
+                rebaseError = true;
+                break;
+        }
+        info.fixups.push_back({segIndex, segOffset, fixupType, TargetSymbolValue::makeInvalid()});
+        //fprintf(stderr, "rebase: segIndex=%d, segOffset=0x%0llX, type=%d\n", segIndex, segOffset, type);
+    });
+    if ( diag.hasError() )
+        return FixupInfo();
+
+    // add fixup for each bind
+    image.forEachBind(diag, ^(uint32_t segIndex, uint64_t segOffset, uint8_t type, int libOrdinal,
+                              uint64_t addend, const char* symbolName, bool weakImport, bool lazy, bool& stop) {
+        launch_cache::ImageGroupWriter::FixupType fixupType;
+        switch ( type ) {
+            case BIND_TYPE_POINTER:
+                if ( lazy )
+                    fixupType = launch_cache::ImageGroupWriter::FixupType::pointerLazyBind;
+                else
+                    fixupType = launch_cache::ImageGroupWriter::FixupType::pointerBind;
+                break;
+            case BIND_TYPE_TEXT_ABSOLUTE32:
+                fixupType = launch_cache::ImageGroupWriter::FixupType::bindText;
+                info.hasTextRelocs = true;
+                break;
+            case BIND_TYPE_TEXT_PCREL32:
+                fixupType = launch_cache::ImageGroupWriter::FixupType::bindTextRel;
+                info.hasTextRelocs = true;
+                break;
+            case BIND_TYPE_IMPORT_JMP_REL32:
+                fixupType = launch_cache::ImageGroupWriter::FixupType::bindImportJmpRel;
+                break;
+           default:
+                diag.error("malformed executable, unknown bind type (%d)", type);
+                stop = true;
+                return;
+        }
+        const ImageProxy*    depProxy    = nullptr;
+        bool                isWeakDylib = false;
+        if ( libOrdinal == BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE ) {
+            // -bundle_loader symbols cannot be bound ahead of time, we must look them up at load time
+            uint32_t imagePathPoolOffset   =  groupWriter.addString("@main");
+            uint32_t imageSymbolPoolOffset =  groupWriter.addString(symbolName);
+            info.fixups.push_back({segIndex, segOffset, fixupType, TargetSymbolValue::makeDynamicGroupValue(imagePathPoolOffset, imageSymbolPoolOffset, weakImport)});
+            return;
+        }
+        else if ( libOrdinal == BIND_SPECIAL_DYLIB_FLAT_LOOKUP ) {
+            // -dynamic_lookup symbols cannot be bound ahead of time, we must look them up at load time
+            uint32_t imagePathPoolOffset   =  groupWriter.addString("@flat");
+            uint32_t imageSymbolPoolOffset =  groupWriter.addString(symbolName);
+            info.fixups.push_back({segIndex, segOffset, fixupType, TargetSymbolValue::makeDynamicGroupValue(imagePathPoolOffset, imageSymbolPoolOffset, weakImport)});
+            return;
+        }
+        else if ( libOrdinal == BIND_SPECIAL_DYLIB_SELF ) {
+            depProxy = this;
+        }
+        else if ( (libOrdinal >= 1) && (libOrdinal <= _dependents.size()) ) {
+            isWeakDylib = (_dependentsKind[libOrdinal-1] == LinkKind::weak);
+            depProxy    = _dependents[libOrdinal-1];
+        }
+        else {
+            diag.error("ordinal %d not supported", libOrdinal);
+            stop = true;
+            return;
+        }
+        if ( depProxy != nullptr ) {
+            MachOParser::FoundSymbol foundInfo;
+            if ( depProxy->findExportedSymbol(diag, symbolName, foundInfo) ) {
+                MachOParser implDylib(foundInfo.foundInDylib, _dyldCacheIsRaw);
+                switch ( foundInfo.kind ) {
+                    case MachOParser::FoundSymbol::Kind::headerOffset:
+                    case MachOParser::FoundSymbol::Kind::resolverOffset:
+                        if ( implDylib.inDyldCache() ) {
+                            uint32_t cacheOffset = (uint32_t)(implDylib.preferredLoadAddress() + foundInfo.value - cacheUnslideBaseAddress + addend);
+                            info.fixups.push_back({segIndex, segOffset, fixupType, TargetSymbolValue::makeSharedCacheOffset(cacheOffset)});
+                        }
+                        else {
+                            ImageProxy* foundProxy   = (ImageProxy*)(foundInfo.foundExtra);
+                            bool isIndirectGroupNum  = foundProxy->_groupNum >= 128;
+                            uint32_t groupNum        = isIndirectGroupNum ? groupWriter.addIndirectGroupNum(foundProxy->_groupNum) : foundProxy->_groupNum;
+                            info.fixups.push_back({segIndex, segOffset, fixupType, TargetSymbolValue::makeGroupValue(groupNum, foundProxy->_indexInGroup, foundInfo.value+addend, isIndirectGroupNum)});
+                        }
+                        break;
+                   case MachOParser::FoundSymbol::Kind::absolute:
+                        if (((((intptr_t)(foundInfo.value+addend)) << 2) >> 2) != (foundInfo.value+addend)) {
+                            diag.error("absolute value %lld not supported", foundInfo.value+addend);
+                            stop = true;
+                            return;
+                        }
+                        info.fixups.push_back({segIndex, segOffset, fixupType, TargetSymbolValue::makeAbsolute(foundInfo.value+addend)});
+                        break;
+                }
+            }
+            else {
+                if ( !weakImport ) {
+                    diag.error("symbol '%s' not found, expected in '%s'", symbolName, depProxy->runtimePath().c_str());
+                    stop = true;
+                }
+                // mark fixup needs to set fixup location to zero
+                info.fixups.push_back({segIndex, segOffset, fixupType, TargetSymbolValue::makeAbsolute(0)});
+            }
+        }
+        else {
+            if ( isWeakDylib ) {
+                // dylib not found and is weak, set pointers into it to zero
+                info.fixups.push_back({segIndex, segOffset, fixupType, TargetSymbolValue::makeAbsolute(0)});
+            }
+            else {
+                diag.error("dylib ordinal %d not found and not weak", libOrdinal);
+                stop = true;
+            }
+         }
+    });
+    if ( diag.hasError() )
+        return FixupInfo();
+
+    uint32_t weakDefPathPoolOffset = groupWriter.addString("@weak_def");
+    image.forEachWeakDef(diag, ^(bool strongDef, uint32_t segIndex, uint64_t segOffset, uint64_t addend, const char* symbolName, bool& stop) {
+        if ( strongDef )
+            return;
+        // find fixup for that location and change it to be a @weakdef dynamic target
+        bool altered = false;
+        for (FixUp& fixup : info.fixups) {
+            if ( (fixup.segOffset == segOffset) && (fixup.segIndex == segIndex) ) {
+                uint32_t symbolPoolOffset =  groupWriter.addString(symbolName);
+                fixup.type   = launch_cache::ImageGroupWriter::FixupType::pointerBind;
+                fixup.target = TargetSymbolValue::makeDynamicGroupValue(weakDefPathPoolOffset, symbolPoolOffset, false);
+                altered = true;
+            }
+        }
+        if ( !altered ) {
+            if ( image.isSlideable() ) {
+                fprintf(stderr, "weak def for %s can't find underlying rebase/bind in %s\n", symbolName, runtimePath().c_str());
+            }
+            else {
+                // no-pie executable does not have rebase for weak-def fixup, so add fixup
+                uint32_t symbolPoolOffset =  groupWriter.addString(symbolName);
+                info.fixups.push_back({segIndex, segOffset, launch_cache::ImageGroupWriter::FixupType::pointerBind, TargetSymbolValue::makeDynamicGroupValue(weakDefPathPoolOffset, symbolPoolOffset, false)} );
+            }
+        }
+
+    });
+
+    return info;
+}
+
+
+void ImageProxy::setOverrideOf(uint32_t groupNum, uint32_t indexInGroup)
+{
+    _overrideOf = ImageRef(0, groupNum, indexInGroup);
+}
+
+
+static bool alreadyInList(const std::vector<ImageProxy*>& imageList, ImageProxy* image)
+{
+    for (ImageProxy* proxy : imageList) {
+        if ( proxy == image )
+            return true;
+    }
+    return false;
+}
+
+void ImageProxy::addToFlatLookup(std::vector<ImageProxy*>& imageList)
+{
+    // add all images shallow
+    bool addedSomething = false;
+    for (ImageProxy* dep : _dependents) {
+        if ( dep == nullptr )
+            continue;
+        if ( !alreadyInList(imageList, dep) ) {
+            imageList.push_back(dep);
+            addedSomething = true;
+        }
+    }
+    // recurse
+    if ( addedSomething ) {
+        for (ImageProxy* dep : _dependents) {
+            if ( dep == nullptr )
+                continue;
+            dep->addToFlatLookup(imageList);
+        }
+    }
+}
+
+
+///////////////////////////  ImageProxyGroup  ///////////////////////////
+
+
+class StringPool
+{
+public:
+    uint32_t            add(const std::string& str);
+    size_t              size() const   { return _buffer.size(); }
+    const char*         buffer() const { return &_buffer[0]; }
+    void                align();
+private:
+    std::vector<char>                           _buffer;
+    std::unordered_map<std::string, uint32_t>   _existingEntries;
+};
+
+uint32_t StringPool::add(const std::string& str)
+{
+    auto pos = _existingEntries.find(str);
+    if ( pos != _existingEntries.end() )
+        return pos->second;
+    size_t len = str.size() + 1;
+    size_t offset = _buffer.size();
+    _buffer.insert(_buffer.end(), &str[0], &str[len]);
+    _existingEntries[str] = (uint32_t)offset;
+    assert(offset < 0xFFFF);
+    return (uint32_t)offset;
+}
+
+void StringPool::align()
+{
+    while ( (_buffer.size() % 4) != 0 )
+        _buffer.push_back('\0');
+}
+
+ImageProxyGroup::ImageProxyGroup(uint32_t groupNum, const DyldCacheParser& dyldCache, const launch_cache::binary_format::ImageGroup* basedOn,
+                                 ImageProxyGroup* next, const std::string& mainProgRuntimePath,
+                                 const std::vector<const BinaryImageGroupData*>& knownGroups,
+                                 const std::vector<std::string>& buildTimePrefixes,
+                                 const std::vector<std::string>& envVars,
+                                 bool stubsEliminated, bool dylibsExpectedOnDisk, bool inodesAreSameAsRuntime)
+    : _pathOverrides(envVars), _patchTable(nullptr), _basedOn(basedOn), _dyldCache(dyldCache), _nextSearchGroup(next), _groupNum(groupNum),
+      _stubEliminated(stubsEliminated), _dylibsExpectedOnDisk(dylibsExpectedOnDisk), _inodesAreSameAsRuntime(inodesAreSameAsRuntime),
+      _knownGroups(knownGroups), _buildTimePrefixes(buildTimePrefixes), _mainProgRuntimePath(mainProgRuntimePath), _platform(Platform::unknown)
+{
+    _archName = dyldCache.cacheHeader()->archName();
+    _platform = (Platform)(dyldCache.cacheHeader()->platform());
+}
+
+
+ImageProxyGroup::~ImageProxyGroup()
+{
+    for (DyldSharedCache::MappedMachO& mapping : _ownedMappings ) {
+        vm_deallocate(mach_task_self(), (vm_address_t)mapping.mh, mapping.length);
+    }
+    for (ImageProxy* proxy : _images) {
+        delete proxy;
+    }
+}
+
+
+std::string ImageProxyGroup::normalizedPath(const std::string& path)
+{
+    for (const std::string& prefix : _buildTimePrefixes) {
+        std::string fullPath = prefix + path;
+        if ( fileExists(fullPath) ) {
+            if ( (fullPath.find("/../") != std::string::npos) || (fullPath.find("//") != std::string::npos) || (fullPath.find("/./") != std::string::npos) ) {
+                char resolvedPath[PATH_MAX];
+                if ( realpath(fullPath.c_str(), resolvedPath) != nullptr ) {
+                    std::string resolvedUnPrefixed = &resolvedPath[prefix.size()];
+                    return resolvedUnPrefixed;
+                }
+            }
+            break;
+        }
+    }
+    return path;
+}
+
+
+ImageProxy* ImageProxyGroup::findImage(Diagnostics& diag, const std::string& runtimeLoadPath, bool canBeMissing, ImageProxy::RPathChain* rChain)
+{
+    __block ImageProxy* result = nullptr;
+    _pathOverrides.forEachPathVariant(runtimeLoadPath.c_str(), _platform, ^(const char* possiblePath, bool& stop) {
+        if ( startsWith(possiblePath, "@rpath/") ) {
+            std::string trailing = &possiblePath[6];
+            for (const ImageProxy::RPathChain* cur=rChain; cur != nullptr; cur = cur->prev) {
+                for (const std::string& rpath : cur->rpaths) {
+                    std::string aPath = rpath + trailing;
+                    result = findAbsoluteImage(diag, aPath, true, false);
+                    if ( result != nullptr ) {
+                        _pathToProxy[runtimeLoadPath] = result;
+                        stop = true;
+                        return;
+                    }
+                }
+            }
+            // if cannot be found via current stack of rpaths, check if already found
+            auto pos = _pathToProxy.find(possiblePath);
+            if ( pos != _pathToProxy.end() ) {
+                result = pos->second;
+                stop = true;
+                return;
+            }
+        }
+        else if ( startsWith(possiblePath, "@loader_path/") ) {
+            std::string loaderFile = rChain->inProxy->runtimePath();
+            size_t lastSlash = loaderFile.rfind('/');
+            if ( lastSlash != std::string::npos ) {
+                std::string loaderDir = loaderFile.substr(0, lastSlash);
+                std::string newPath = loaderDir + &possiblePath[12];
+                result = findAbsoluteImage(diag, newPath, canBeMissing, false);
+                if ( result != nullptr ) {
+                    _pathToProxy[runtimeLoadPath] = result;
+                    stop = true;
+                    return;
+                }
+            }
+        }
+        else if ( startsWith(possiblePath, "@executable_path/") ) {
+            for (const ImageProxy::RPathChain* cur=rChain; cur != nullptr; cur = cur->prev) {
+                if ( cur->inProxy->mh()->filetype == MH_EXECUTE ) {
+                    std::string mainProg = cur->inProxy->runtimePath();
+                    size_t lastSlash = mainProg.rfind('/');
+                    if ( lastSlash != std::string::npos ) {
+                        std::string mainDir = mainProg.substr(0, lastSlash);
+                        std::string newPath = mainDir + &possiblePath[16];
+                        result = findAbsoluteImage(diag, newPath, canBeMissing, false);
+                        if ( result != nullptr ) {
+                            _pathToProxy[runtimeLoadPath] = result;
+                            stop = true;
+                            return;
+                        }
+                    }
+                }
+            }
+        }
+        else {
+            // load command is full path to dylib
+            result = findAbsoluteImage(diag, possiblePath, canBeMissing, false);
+            if ( result != nullptr ) {
+                stop = true;
+                return;
+            }
+        }
+    });
+
+    // when building closure, check if an added dylib is an override for something in the cache
+    if ( (result != nullptr) && (_groupNum > 1) && !result->isProxyForCachedDylib() ) {
+        for (ImageProxyGroup* grp = this; grp != nullptr; grp = grp->_nextSearchGroup) {
+            if ( grp->_basedOn == nullptr )
+                continue;
+            uint32_t indexInGroup;
+            launch_cache::ImageGroup imageGroup(grp->_basedOn);
+            if ( imageGroup.findImageByPath(runtimeLoadPath.c_str(), indexInGroup) ) {
+                result->setOverrideOf(imageGroup.groupNum(), indexInGroup);
+                break;
+            }
+        }
+    }
+
+    return result;
+}
+
+
+bool ImageProxyGroup::builtImageStillValid(const launch_cache::Image& image)
+{
+    // only do checks when running on system
+    if ( _buildTimePrefixes.size() != 1 )
+        return true;
+    if ( _buildTimePrefixes.front().size() != 0 )
+        return true;
+    if ( _platform != MachOParser::currentPlatform() )
+        return true;
+
+    struct stat statBuf;
+    bool expectedOnDisk   = image.group().dylibsExpectedOnDisk();
+    bool overridableDylib = image.overridableDylib();
+    bool cachedDylib      = !image.isDiskImage();
+    bool fileFound        = ( ::stat(image.path(), &statBuf) == 0 );
+
+    if ( cachedDylib ) {
+        if ( expectedOnDisk ) {
+            if ( fileFound ) {
+                // macOS case: verify dylib file info matches what it was when cache was built
+                return ( (image.fileModTime() == statBuf.st_mtime) && (image.fileINode() == statBuf.st_ino) );
+            }
+            else {
+                // macOS case: dylib missing
+                return false;
+            }
+        }
+        else {
+            if ( fileFound ) {
+                if ( overridableDylib ) {
+                    // iOS case: internal install with dylib root
+                    return false;
+                }
+                else {
+                    // iOS case: customer install, ignore dylib on disk
+                    return true;
+                }
+            }
+            else {
+                // iOS case: cached dylib not on disk as expected
+                return true;
+            }
+        }
+    }
+    else {
+        if ( fileFound ) {
+            if ( image.validateUsingModTimeAndInode() ) {
+                // macOS case: verify dylib file info matches what it was when cache was built
+                return ( (image.fileModTime() == statBuf.st_mtime) && (image.fileINode() == statBuf.st_ino) );
+            }
+            else {
+                // FIXME: need to verify file cdhash
+                return true;
+            }
+        }
+        else {
+            // dylib not on disk as expected
+            return false;
+        }
+    }
+}
+
+ImageProxy* ImageProxyGroup::findAbsoluteImage(Diagnostics& diag, const std::string& runtimeLoadPath, bool canBeMissing, bool makeErrorMessage, bool pathIsAlreadyReal)
+{
+    auto pos = _pathToProxy.find(runtimeLoadPath);
+    if ( pos != _pathToProxy.end() )
+        return pos->second;
+
+    // see if this ImageProxyGroup is a proxy for an ImageGroup from the dyld shared cache
+    if ( _basedOn != nullptr ) {
+        uint32_t foundIndex;
+        launch_cache::ImageGroup imageGroup(_basedOn);
+        if ( imageGroup.findImageByPath(runtimeLoadPath.c_str(), foundIndex) ) {
+            launch_cache::Image image = imageGroup.image(foundIndex);
+            if ( builtImageStillValid(image) ) {
+                ImageProxy* proxy = nullptr;
+                if ( _groupNum == 0 ) {
+                    const mach_header* mh = (mach_header*)((uint8_t*)(_dyldCache.cacheHeader()) + image.cacheOffset());
+                    proxy = new ImageProxy(mh, image.binaryData(), foundIndex, _dyldCache.cacheIsMappedRaw());
+                }
+                else {
+                    DyldSharedCache::MappedMachO* mapping = addMappingIfValidMachO(diag, runtimeLoadPath);
+                    if ( mapping != nullptr ) {
+                        proxy = new ImageProxy(*mapping, _groupNum, foundIndex, false);
+                    }
+                }
+                if ( proxy != nullptr ) {
+                    _pathToProxy[runtimeLoadPath] = proxy;
+                    _images.push_back(proxy);
+                    if ( runtimeLoadPath != image.path() ) {
+                        // lookup path is an alias, add real path too
+                        _pathToProxy[image.path()] = proxy;
+                    }
+                    return proxy;
+                }
+            }
+        }
+    }
+
+    if ( _nextSearchGroup != nullptr ) {
+        ImageProxy* result = _nextSearchGroup->findAbsoluteImage(diag, runtimeLoadPath, true, false);
+        if ( result != nullptr )
+            return result;
+    }
+
+    // see if this is a symlink to a dylib
+    if ( !pathIsAlreadyReal ) {
+        for (const std::string& prefix : _buildTimePrefixes) {
+            std::string fullPath = prefix + runtimeLoadPath;
+            if ( endsWith(prefix, "/") )
+                fullPath = prefix.substr(0, prefix.size()-1) + runtimeLoadPath;
+            if ( fileExists(fullPath) ) {
+                std::string resolvedPath = realFilePath(fullPath);
+                if ( !resolvedPath.empty() && (resolvedPath!= fullPath) ) {
+                    std::string resolvedRuntimePath = resolvedPath.substr(prefix.size());
+                    ImageProxy* proxy = findAbsoluteImage(diag, resolvedRuntimePath, true, false, true);
+                    if ( proxy != nullptr )
+                        return proxy;
+                }
+            }
+        }
+    }
+
+    if ( (_groupNum >= 2) && (_basedOn == nullptr) ) {
+        if ( (runtimeLoadPath[0] != '/') && (runtimeLoadPath[0] != '@') ) {
+            for (ImageProxy* aProxy : _images) {
+                if ( endsWith(aProxy->runtimePath(), runtimeLoadPath) ) {
+                    aProxy->setCwdMustBeThisDir();
+                    return aProxy;
+                }
+            }
+        }
+
+        DyldSharedCache::MappedMachO* mapping = addMappingIfValidMachO(diag, runtimeLoadPath);
+        if ( mapping != nullptr ) {
+            ImageProxy* proxy = new ImageProxy(*mapping, _groupNum, (uint32_t)_images.size(), false);
+            _pathToProxy[runtimeLoadPath] = proxy;
+            _images.push_back(proxy);
+            return proxy;
+        }
+    }
+
+    if ( !canBeMissing && makeErrorMessage ) {
+        if ( diag.warnings().empty() ) {
+            if ( diag.hasError() ) {
+                std::string orgMsg = diag.errorMessage();
+                diag.error("'%s' %s", runtimeLoadPath.c_str(), orgMsg.c_str());
+            }
+            else {
+                diag.error("could not find '%s'", runtimeLoadPath.c_str());
+            }
+        }
+        else {
+            std::string allTries;
+            for (const std::string& warn : diag.warnings()) {
+                if ( allTries.empty() )
+                    allTries = warn;
+                else
+                    allTries = allTries + ", " + warn;
+            }
+            diag.clearWarnings();
+            diag.error("could not use '%s'. Did try: %s", runtimeLoadPath.c_str(), allTries.c_str());
+        }
+    }
+
+    // record locations not found so it can be verified they are still missing at runtime
+    _mustBeMissingFiles.insert(runtimeLoadPath);
+
+    return nullptr;
+}
+
+
+DyldSharedCache::MappedMachO* ImageProxyGroup::addMappingIfValidMachO(Diagnostics& diag, const std::string& runtimePath, bool ignoreMainExecutables)
+{
+    bool fileFound = false;
+    for (const std::string& prefix : _buildTimePrefixes) {
+        std::string fullPath = prefix + runtimePath;
+        struct stat statBuf;
+        if ( stat(fullPath.c_str(), &statBuf) != 0 )
+            continue;
+        fileFound = true;
+        // map whole file and determine if it is mach-o or a fat file
+        int fd = ::open(fullPath.c_str(), O_RDONLY);
+        if ( fd < 0 ) {
+            diag.warning("file not open()able '%s' errno=%d", fullPath.c_str(), errno);
+            continue;
+        }
+        size_t len = (size_t)statBuf.st_size;
+        size_t offset = 0;
+        const void* p = ::mmap(NULL, len, PROT_READ, MAP_PRIVATE, fd, 0);
+        if ( p != MAP_FAILED ) {
+            size_t sliceLen;
+            size_t sliceOffset;
+            bool missingSlice;
+            Diagnostics fatDiag;
+            if ( FatUtil::isFatFileWithSlice(fatDiag, p, len, _archName, sliceOffset, sliceLen, missingSlice) ) {
+                // unmap whole file
+                ::munmap((void*)p, len);
+                // remap just slice
+                p = ::mmap(NULL, sliceLen, PROT_READ, MAP_PRIVATE, fd, sliceOffset);
+                if ( p != MAP_FAILED ) {
+                    offset = sliceOffset;
+                    len    = sliceLen;
+                }
+            }
+            else if ( fatDiag.hasError() ) {
+                diag.warning("%s", fatDiag.errorMessage().c_str());
+            }
+            if ( (p != MAP_FAILED) && !missingSlice && MachOParser::isValidMachO(diag, _archName, _platform, p, len, fullPath, ignoreMainExecutables) ) {
+                bool issetuid = (statBuf.st_mode & (S_ISUID|S_ISGID));
+                bool sip = false; // FIXME
+                _ownedMappings.emplace_back(runtimePath, (mach_header*)p, len, issetuid, sip, offset, statBuf.st_mtime, statBuf.st_ino);
+                ::close(fd);
+                return &_ownedMappings.back();
+            }
+            else if (p != MAP_FAILED) {
+                ::munmap((void*)p, len);
+            }
+        }
+        ::close(fd);
+    }
+    if ( !fileFound )
+        diag.warning("file not found '%s'", runtimePath.c_str());
+
+    return nullptr;
+}
+
+static bool dontExamineDir(const std::string& dirPath)
+{
+    return endsWith(dirPath, ".app") || endsWith(dirPath, ".xctoolchain") || endsWith(dirPath, ".sdk") || endsWith(dirPath, ".platform");
+}
+
+void ImageProxyGroup::addExtraMachOsInBundle(const std::string& appDir)
+{
+    iterateDirectoryTree("", appDir, ^(const std::string& dirPath) { return dontExamineDir(dirPath); }, ^(const std::string& path, const struct stat& statBuf) {
+        // ignore files that don't have 'x' bit set (all runnable mach-o files do)
+        const bool hasXBit = ((statBuf.st_mode & S_IXOTH) == S_IXOTH);
+        if ( !hasXBit )
+            return;
+
+        // ignore files too small
+        if ( statBuf.st_size < 0x1000 )
+            return;
+
+        // if the file is mach-o, add to list
+        if ( _pathToProxy.find(path) == _pathToProxy.end() ) {
+            Diagnostics  machoDiag;
+            DyldSharedCache::MappedMachO* mapping = addMappingIfValidMachO(machoDiag, path, true);
+            if ( mapping != nullptr ) {
+                ImageProxy* proxy = new ImageProxy(*mapping, _groupNum, (uint32_t)_images.size(), false);
+                if ( proxy != nullptr ) {
+                    _pathToProxy[path] = proxy;
+                    _images.push_back(proxy);
+                }
+            }
+        }
+    });
+}
+
+// used when building dyld shared cache
+ImageProxyGroup* ImageProxyGroup::makeDyldCacheDylibsGroup(Diagnostics& diag, const DyldCacheParser& dyldCache,
+                                                           const std::vector<DyldSharedCache::MappedMachO>& cachedDylibs,
+                                                           const std::vector<std::string>& buildTimePrefixes,
+                                                           const PatchTable& patchTable, bool stubEliminated, bool dylibsExpectedOnDisk)
+{
+    std::vector<std::string> emptyEnvVars; // Note: this method only used when constructing dyld cache where envs are not used
+    std::vector<const BinaryImageGroupData*> noExistingGroups;
+    ImageProxyGroup* groupProxy = new ImageProxyGroup(0, dyldCache, nullptr, nullptr, "", noExistingGroups, buildTimePrefixes, emptyEnvVars, stubEliminated, dylibsExpectedOnDisk);
+    groupProxy->_patchTable = &patchTable;
+
+    // add every dylib in shared cache to _images
+    uint32_t indexInGroup = 0;
+    for (const DyldSharedCache::MappedMachO& mapping : cachedDylibs) {
+        ImageProxy* proxy = new ImageProxy(mapping, 0, indexInGroup++, true);
+        groupProxy->_images.push_back(proxy);
+        groupProxy->_pathToProxy[mapping.runtimePath] = proxy;
+    }
+
+    // verify libdyld is compatible
+    ImageRef libdyldEntryImageRef = ImageRef::makeEmptyImageRef();
+    uint32_t libdyldEntryOffset;
+    groupProxy->findLibdyldEntry(diag, libdyldEntryImageRef, libdyldEntryOffset);
+    if ( diag.hasError() ) {
+        delete groupProxy;
+        return nullptr;
+    }
+
+    // wire up dependents
+    bool hadError = false;
+    for (size_t i=0; i < groupProxy->_images.size(); ++i) {
+        // note: addDependentsShallow() can append to _images, so can't use regular iterator
+        ImageProxy* proxy = groupProxy->_images[i];
+        proxy->addDependentsShallow(*groupProxy);
+        if ( proxy->diagnostics().hasError() ) {
+            hadError = true;
+            diag.copy(proxy->diagnostics());
+            break;
+        }
+    }
+
+    if ( hadError ) {
+        delete groupProxy;
+        return nullptr;
+    }
+
+    return groupProxy;
+}
+
+
+// used when building dyld shared cache
+ImageProxyGroup* ImageProxyGroup::makeOtherOsGroup(Diagnostics& diag, const DyldCacheParser& dyldCache, ImageProxyGroup* cachedDylibsGroup,
+                                                   const std::vector<DyldSharedCache::MappedMachO>& otherDylibsAndBundles,
+                                                   bool inodesAreSameAsRuntime, const std::vector<std::string>& buildTimePrefixes)
+{
+    std::vector<std::string> emptyEnvVars; // Note: this method only used when constructing dyld cache where envs are not used
+    const BinaryImageGroupData* cachedDylibsGroupData = dyldCache.cachedDylibsGroup();
+    std::vector<const BinaryImageGroupData*> existingGroups = { cachedDylibsGroupData };
+    ImageProxyGroup          dyldCacheDylibProxyGroup(0, dyldCache, cachedDylibsGroupData, nullptr,           "", existingGroups, buildTimePrefixes, emptyEnvVars);
+    ImageProxyGroup* groupProxy = new ImageProxyGroup(1, dyldCache, nullptr,               cachedDylibsGroup, "", existingGroups, buildTimePrefixes, emptyEnvVars,
+                                                      false, true, inodesAreSameAsRuntime);
+
+    // add every dylib/bundle in "other: list to _images
+    uint32_t indexInGroup = 0;
+    for (const DyldSharedCache::MappedMachO& mapping : otherDylibsAndBundles) {
+        ImageProxy* proxy = new ImageProxy(mapping, 1, indexInGroup++, false);
+        groupProxy->_images.push_back(proxy);
+        groupProxy->_pathToProxy[mapping.runtimePath] = proxy;
+    }
+
+    // wire up dependents
+    for (size_t i=0; i < groupProxy->_images.size(); ++i) {
+        // note: addDependentsShallow() can append to _images, so can't use regular iterator
+        ImageProxy* proxy = groupProxy->_images[i];
+        // note: other-dylibs can only depend on dylibs in this group or group 0, so no need for deep dependents
+        proxy->addDependentsShallow(*groupProxy);
+        if ( proxy->diagnostics().hasError() ) {
+            diag.warning("adding dependents to %s: %s", proxy->runtimePath().c_str(), proxy->diagnostics().errorMessage().c_str());
+            proxy->markInvalid();
+        }
+    }
+    // propagate invalidness
+    __block bool somethingInvalid;
+    do {
+        somethingInvalid = false;
+        for (ImageProxy* proxy : groupProxy->_images) {
+            proxy->forEachDependent(^(ImageProxy* dep, LinkKind) {
+                if ( (dep != nullptr) && dep->invalid() && !proxy->invalid()) {
+                    proxy->markInvalid();
+                    somethingInvalid = true;
+                }
+            });
+        }
+    } while (somethingInvalid);
+
+    return groupProxy;
+}
+
+// used by closured for dlopen of unknown dylibs
+const BinaryImageGroupData* ImageProxyGroup::makeDlopenGroup(Diagnostics& diag, const DyldCacheParser& dyldCache, uint32_t groupNum,
+                                                             const std::vector<const BinaryImageGroupData*>& existingGroups,
+                                                             const std::string& imagePath, const std::vector<std::string>& envVars)
+{
+    const std::vector<std::string>& noBuildTimePrefixes = {""};
+    ImageProxyGroup dyldCacheDylibProxyGroup(0, dyldCache, existingGroups[0], nullptr,                   "",        existingGroups, noBuildTimePrefixes, envVars);
+    ImageProxyGroup dyldCacheOtherProxyGroup(1, dyldCache, nullptr,           &dyldCacheDylibProxyGroup, "",        existingGroups, noBuildTimePrefixes, envVars);
+    ImageProxyGroup dlopenGroupProxy(groupNum,  dyldCache, nullptr,           &dyldCacheOtherProxyGroup, imagePath, existingGroups, noBuildTimePrefixes, envVars, false, true, true);
+
+    DyldSharedCache::MappedMachO* topMapping = dlopenGroupProxy.addMappingIfValidMachO(diag, imagePath, true);
+    if ( topMapping == nullptr ) {
+        if ( diag.noError() ) {
+            const std::set<std::string>& warnings = diag.warnings();
+            if ( warnings.empty() )
+                diag.error("no loadable mach-o in %s", imagePath.c_str());
+            else
+                diag.error("%s", (*warnings.begin()).c_str());
+        }
+        return nullptr;
+    }
+
+    ImageProxy* topImageProxy = new ImageProxy(*topMapping, groupNum, 0, false);
+    if ( topImageProxy == nullptr ) {
+        diag.error("can't find slice matching dyld cache in %s", imagePath.c_str());
+        return nullptr;
+    }
+    dlopenGroupProxy._images.push_back(topImageProxy);
+    dlopenGroupProxy._pathToProxy[imagePath] = topImageProxy;
+
+    // add all dylibs needed by dylib and are not in dyld cache
+    topImageProxy->addDependentsDeep(dlopenGroupProxy, nullptr, false);
+    if ( topImageProxy->diagnostics().hasError() ) {
+        diag.copy(topImageProxy->diagnostics());
+        return nullptr;
+    }
+
+    const BinaryImageGroupData* result = dlopenGroupProxy.makeImageGroupBinary(diag);
+
+    return result;
+}
+
+
+// used when building dyld shared cache
+BinaryClosureData* ImageProxyGroup::makeClosure(Diagnostics& diag, const DyldCacheParser& dyldCache, ImageProxyGroup* cachedDylibsGroup,
+                                                ImageProxyGroup* otherOsDylibs, const DyldSharedCache::MappedMachO& mainProgMapping,
+                                                bool inodesAreSameAsRuntime, const std::vector<std::string>& buildTimePrefixes)
+{
+    // _basedOn can not be set until ImageGroup is built
+    if ( cachedDylibsGroup->_basedOn == nullptr ) {
+        cachedDylibsGroup->_basedOn = dyldCache.cachedDylibsGroup();
+    }
+    const BinaryImageGroupData* cachedDylibsGroupData = dyldCache.cachedDylibsGroup();
+    const BinaryImageGroupData* otherDylibsGroupData = dyldCache.otherDylibsGroup();
+    std::vector<const BinaryImageGroupData*> existingGroups = { cachedDylibsGroupData, otherDylibsGroupData };
+    std::vector<std::string> emptyEnvVars; // Note: this method only used when constructing dyld cache where envs are not used
+    ImageProxyGroup mainClosureGroupProxy(2, dyldCache, nullptr, otherOsDylibs, mainProgMapping.runtimePath, existingGroups, buildTimePrefixes,
+                                          emptyEnvVars, false, true, inodesAreSameAsRuntime);
+
+    ImageProxy* mainProxy = new ImageProxy(mainProgMapping, 2, 0, false);
+    if ( mainProxy == nullptr ) {
+        diag.error("can't find slice matching dyld cache in %s", mainProgMapping.runtimePath.c_str());
+        return nullptr;
+    }
+    mainClosureGroupProxy._images.push_back(mainProxy);
+    mainClosureGroupProxy._pathToProxy[mainProgMapping.runtimePath] = mainProxy;
+
+    return mainClosureGroupProxy.makeClosureBinary(diag, mainProxy, false);
+}
+
+
+bool ImageProxyGroup::addInsertedDylibs(Diagnostics& diag)
+{
+    __block bool success = true;
+    _pathOverrides.forEachInsertedDylib(^(const char* dylibPath) {
+        ImageProxy* insertProxy = findAbsoluteImage(diag, dylibPath, false, true);
+        if ( insertProxy == nullptr )
+            success = false;
+    });
+    return success;
+}
+
+static DyldCacheParser findDyldCache(Diagnostics& diag, const ClosureBuffer::CacheIdent& cacheIdent, task_t requestor, bool* dealloc)
+{
+    *dealloc = false;
+#if !defined(__MAC_OS_X_VERSION_MIN_REQUIRED) || (__MAC_OS_X_VERSION_MIN_REQUIRED >= 101300)
+    size_t currentCacheSize;
+    const DyldSharedCache* currentCache = (const DyldSharedCache*)_dyld_get_shared_cache_range(&currentCacheSize);
+    if ( currentCache != nullptr ) {
+        uuid_t currentCacheUUID;
+        currentCache->getUUID(currentCacheUUID);
+        if ( memcmp(currentCacheUUID, cacheIdent.cacheUUID, 16) == 0 ) 
+            return DyldCacheParser((const DyldSharedCache*)currentCache, false);
+    }
+#endif
+    if ( requestor == mach_task_self() ) {
+        // handle dyld_closure_util case where -cache_file option maps raw cache file into this process
+        const DyldSharedCache* altCache = (DyldSharedCache*)cacheIdent.cacheAddress;
+        uuid_t altCacheUUID;
+        altCache->getUUID(altCacheUUID);
+        if ( memcmp(altCacheUUID, cacheIdent.cacheUUID, 16) == 0 )
+            return DyldCacheParser(altCache, true); // only one cache can be mapped into process, so this must be raw
+        else
+            diag.error("dyld cache uuid has changed");
+    }
+#if BUILDING_CLOSURED
+    else {
+        // handle case where requestor to closured is running with a different dyld cache that closured
+        uint8_t cacheBuffer[4096];
+        mach_vm_size_t actualReadSize = sizeof(cacheBuffer);
+        kern_return_t r;
+        r = mach_vm_read_overwrite(requestor, cacheIdent.cacheAddress, sizeof(cacheBuffer), (vm_address_t)&cacheBuffer, &actualReadSize);
+        if ( r != KERN_SUCCESS ) {
+            diag.error("unable to read cache header from requesting process (addr=0x%llX), kern err=%d", cacheIdent.cacheAddress, r);
+            return DyldCacheParser(nullptr, false);
+        }
+        const dyld_cache_header* header = (dyld_cache_header*)cacheBuffer;
+        const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)(cacheBuffer + header->mappingOffset);
+        vm_address_t bufferAddress = 0;
+        r = vm_allocate(mach_task_self(), &bufferAddress, (long)cacheIdent.cacheMappedSize, VM_FLAGS_ANYWHERE);
+        if ( r != KERN_SUCCESS ) {
+            diag.error("unable to allocate space to copy custom dyld cache (size=0x%llX), kern err=%d", cacheIdent.cacheMappedSize, r);
+            return DyldCacheParser(nullptr, false);
+        }
+        uint64_t slide =  cacheIdent.cacheAddress - mappings[0].address;
+        for (int i=0; i < 3; ++i) {
+            mach_vm_address_t mappedAddress = bufferAddress + (mappings[i].address - mappings[0].address);
+            mach_vm_size_t    mappedSize    = mappings[i].size;
+            vm_prot_t         curProt       = VM_PROT_READ;
+            vm_prot_t         maxProt       = VM_PROT_READ;
+            r = mach_vm_remap(mach_task_self(), &mappedAddress, mappedSize, 0, VM_FLAGS_FIXED | VM_FLAGS_OVERWRITE,
+                              requestor, mappings[i].address+slide, true, &curProt, &maxProt, VM_INHERIT_NONE);
+            if ( r != KERN_SUCCESS ) {
+                 diag.error("unable to mach_vm_remap region %d custom dyld cache (request addr=0x%llX, size=0x%llX), kern err=%d, localBuffer=0x%lX, localMapTarget=0x%llX",
+                           i, mappings[i].address+slide, mappedSize, r, (long)bufferAddress, mappedAddress);
+                 return DyldCacheParser(nullptr, false);
+            }
+            if ( curProt != VM_PROT_READ )
+                vm_protect(mach_task_self(), (long)mappedAddress, (long)mappedSize, false, VM_PROT_READ);
+       }
+       *dealloc = true;
+       return DyldCacheParser((DyldSharedCache*)bufferAddress, false);  // assumes cache in other process is mapped as three regions
+    }
+#endif
+    return DyldCacheParser(nullptr, false);
+}
+
+BinaryClosureData* ImageProxyGroup::makeClosure(Diagnostics& diag, const ClosureBuffer& buffer, task_t requestor, const std::vector<std::string>& buildTimePrefixes)
+{
+    // unpack buffer
+    bool deallocCacheCopy;
+    DyldCacheParser dyldCache = findDyldCache(diag, buffer.cacheIndent(), requestor, &deallocCacheCopy);
+    if ( diag.hasError() )
+        return nullptr;
+    const char* mainProg = buffer.targetPath();
+    std::vector<std::string> envVars;
+    int envCount = buffer.envVarCount();
+    const char* envVarCStrings[envCount];
+    buffer.copyImageGroups(envVarCStrings);
+    for (int i=0; i < envCount; ++i) {
+        envVars.push_back(envVarCStrings[i]);
+    }
+
+    // make ImageProxyGroups: 0, 1, 2
+    const BinaryImageGroupData* cachedDylibsGroupData = dyldCache.cachedDylibsGroup();
+    const BinaryImageGroupData* otherDylibsGroupData  = dyldCache.otherDylibsGroup();
+    std::vector<std::string> realBuildTimePrefixes;
+    for (const std::string& prefix : buildTimePrefixes)  {
+        char resolvedPath[PATH_MAX];
+        if ( realpath(prefix.c_str(), resolvedPath) != nullptr )
+            realBuildTimePrefixes.push_back(resolvedPath);
+        else
+            realBuildTimePrefixes.push_back(prefix);
+    }
+    std::vector<const BinaryImageGroupData*> existingGroups = { cachedDylibsGroupData, otherDylibsGroupData };
+    ImageProxyGroup dyldCacheDylibProxyGroup(0, dyldCache, cachedDylibsGroupData, nullptr,                   "",       existingGroups, realBuildTimePrefixes, envVars);
+    ImageProxyGroup dyldCacheOtherProxyGroup(1, dyldCache, otherDylibsGroupData,  &dyldCacheDylibProxyGroup, "",       existingGroups, realBuildTimePrefixes, envVars);
+    ImageProxyGroup mainClosureGroupProxy(   2, dyldCache, nullptr,               &dyldCacheOtherProxyGroup, mainProg, existingGroups, realBuildTimePrefixes, envVars, false, true, true);
+
+    // add any DYLD_INSERTED_LIBRARIES then main program into closure
+    BinaryClosureData* result = nullptr;
+    if ( mainClosureGroupProxy.addInsertedDylibs(diag) ) {
+        ImageProxy* proxy = mainClosureGroupProxy.findAbsoluteImage(diag, mainProg, false, true);
+        if ( proxy != nullptr ) {
+            // build closure
+            result = mainClosureGroupProxy.makeClosureBinary(diag, proxy, false);
+        }
+    }
+
+    // if client has a different cache, unmap our copy
+    if ( deallocCacheCopy )
+        vm_deallocate(mach_task_self(), (vm_address_t)dyldCache.cacheHeader(), (long)buffer.cacheIndent().cacheMappedSize);
+
+    return result;
+}
+
+ClosureBuffer closured_CreateImageGroup(const ClosureBuffer& input)
+{
+    Diagnostics diag;
+    const BinaryImageGroupData* newGroup = ImageProxyGroup::makeDlopenGroup(diag, input, mach_task_self(), {""});
+
+    if ( diag.noError() ) {
+        // on success return the ImageGroup binary in the ClosureBuffer
+        dyld3::ClosureBuffer result(newGroup);
+        free((void*)newGroup);
+        return result;
+    }
+    else {
+        // on failure return the error message in the ClosureBuffer
+        dyld3::ClosureBuffer err(diag.errorMessage().c_str());
+        return err;
+    }
+}
+
+const BinaryImageGroupData* ImageProxyGroup::makeDlopenGroup(Diagnostics& diag, const ClosureBuffer& buffer, task_t requestor, const std::vector<std::string>& buildTimePrefixes)
+{
+    // unpack buffer
+    bool deallocCacheCopy;
+    DyldCacheParser dyldCache = findDyldCache(diag, buffer.cacheIndent(), requestor, &deallocCacheCopy);
+    if ( diag.hasError() )
+        return nullptr;
+
+    const char* targetDylib = buffer.targetPath();
+    std::vector<std::string> envVars;
+    int envCount = buffer.envVarCount();
+    const char* envVarCStrings[envCount];
+    buffer.copyImageGroups(envVarCStrings);
+    for (int i=0; i < envCount; ++i) {
+        envVars.push_back(envVarCStrings[i]);
+    }
+    uint32_t groupCount = buffer.imageGroupCount() + 2;
+    const launch_cache::BinaryImageGroupData* groupDataPtrs[groupCount];
+    groupDataPtrs[0] = dyldCache.cachedDylibsGroup();
+    groupDataPtrs[1] = dyldCache.otherDylibsGroup();
+    buffer.copyImageGroups(&groupDataPtrs[2]);
+
+    // build an ImageProxyGroup for each existing group, and one for new group being constructed
+    std::vector<const launch_cache::BinaryImageGroupData*> existingGroups;
+    std::vector<std::unique_ptr<ImageProxyGroup>> proxies;
+    ImageProxyGroup* prevProxy = nullptr;
+    for (uint32_t i=0; i < groupCount; ++i) {
+        const launch_cache::BinaryImageGroupData* groupData = groupDataPtrs[i];
+        existingGroups.push_back(groupData);
+        launch_cache::ImageGroup group(groupData);
+        uint32_t groupNum = group.groupNum();
+        assert(groupNum == proxies.size());
+        proxies.emplace_back(new ImageProxyGroup(groupNum, dyldCache, groupData, prevProxy, "", existingGroups, buildTimePrefixes, envVars));
+        prevProxy = proxies.back().get();
+    }
+    ImageProxyGroup dlopenGroupProxy(groupCount, dyldCache, nullptr, prevProxy, targetDylib, existingGroups, buildTimePrefixes, envVars);
+
+    // find and mmap() top level dylib
+    DyldSharedCache::MappedMachO* topMapping = dlopenGroupProxy.addMappingIfValidMachO(diag, targetDylib, true);
+    if ( topMapping == nullptr ) {
+        std::string allWarnings;
+        for (const std::string& warn : diag.warnings()) {
+            if ( allWarnings.empty() )
+                allWarnings = warn;
+            else
+                allWarnings = allWarnings + ", " + warn;
+        }
+        diag.clearWarnings();
+        diag.error("%s", allWarnings.c_str());
+        if ( deallocCacheCopy )
+            vm_deallocate(mach_task_self(), (vm_address_t)dyldCache.cacheHeader(), (long)buffer.cacheIndent().cacheMappedSize);
+        return nullptr;
+    }
+
+    // make ImageProxy for top level dylib
+    ImageProxy* topImageProxy = new ImageProxy(*topMapping, groupCount, 0, false);
+    if ( topImageProxy == nullptr ) {
+        diag.error("can't find slice matching dyld cache in %s", targetDylib);
+        if ( deallocCacheCopy )
+            vm_deallocate(mach_task_self(), (vm_address_t)dyldCache.cacheHeader(), (long)buffer.cacheIndent().cacheMappedSize);
+        return nullptr;
+    }
+    dlopenGroupProxy._images.push_back(topImageProxy);
+    dlopenGroupProxy._pathToProxy[targetDylib] = topImageProxy;
+
+    // add all dylibs needed by dylib and are not in dyld cache
+    topImageProxy->addDependentsDeep(dlopenGroupProxy, nullptr, false);
+    if ( topImageProxy->diagnostics().hasError() ) {
+        diag.copy(topImageProxy->diagnostics());
+        if ( deallocCacheCopy )
+            vm_deallocate(mach_task_self(), (vm_address_t)dyldCache.cacheHeader(), (long)buffer.cacheIndent().cacheMappedSize);
+        return nullptr;
+    }
+
+    // construct ImageGroup from ImageProxies
+    const BinaryImageGroupData* result = dlopenGroupProxy.makeImageGroupBinary(diag);
+
+    // clean up
+    if ( deallocCacheCopy )
+        vm_deallocate(mach_task_self(), (vm_address_t)dyldCache.cacheHeader(), (long)buffer.cacheIndent().cacheMappedSize);
+
+    return result;
+}
+
+
+
+
+// Used by closured and dyld_closure_util
+BinaryClosureData* ImageProxyGroup::makeClosure(Diagnostics& diag, const DyldCacheParser& dyldCache,
+                                                const std::string& mainProg, bool includeDylibsInDir,
+                                                const std::vector<std::string>& buildTimePrefixes,
+                                                const std::vector<std::string>& envVars)
+{
+    const BinaryImageGroupData* cachedDylibsGroupData = dyldCache.cachedDylibsGroup();
+    const BinaryImageGroupData* otherDylibsGroupData  = dyldCache.otherDylibsGroup();
+    std::vector<std::string> realBuildTimePrefixes;
+    for (const std::string& prefix : buildTimePrefixes)  {
+        char resolvedPath[PATH_MAX];
+        if ( realpath(prefix.c_str(), resolvedPath) != nullptr )
+            realBuildTimePrefixes.push_back(resolvedPath);
+        else
+            realBuildTimePrefixes.push_back(prefix);
+    }
+    std::vector<const BinaryImageGroupData*> existingGroups = { cachedDylibsGroupData, otherDylibsGroupData };
+    ImageProxyGroup dyldCacheDylibProxyGroup(0, dyldCache, cachedDylibsGroupData, nullptr,                   "",       existingGroups, realBuildTimePrefixes, envVars);
+    ImageProxyGroup dyldCacheOtherProxyGroup(1, dyldCache, otherDylibsGroupData,  &dyldCacheDylibProxyGroup, "",       existingGroups, realBuildTimePrefixes, envVars);
+    ImageProxyGroup mainClosureGroupProxy(   2, dyldCache, nullptr,               &dyldCacheOtherProxyGroup, mainProg, existingGroups, realBuildTimePrefixes, envVars, false, true, true);
+
+    // add any DYLD_INSERTED_LIBRARIES into closure
+    if ( !mainClosureGroupProxy.addInsertedDylibs(diag) )
+        return nullptr;
+
+    ImageProxy* proxy = mainClosureGroupProxy.findAbsoluteImage(diag, mainProg, false, true);
+    if ( proxy == nullptr )
+        return nullptr;
+
+    return mainClosureGroupProxy.makeClosureBinary(diag, proxy, includeDylibsInDir);
+}
+
+const char* sSkipPrograms_macOS[] = {
+    "/Applications/iBooks.app/Contents/MacOS/iBooks",
+};
+
+const char* sSkipPrograms_embeddedOSes[] = {
+    "/sbin/launchd",
+    "/usr/local/sbin/launchd.debug",
+    "/usr/local/sbin/launchd.development"
+};
+
+BinaryClosureData* ImageProxyGroup::makeClosureBinary(Diagnostics& diag, ImageProxy* mainProgProxy, bool includeDylibsInDir)
+{
+    assert(mainProgProxy != nullptr);
+    assert(_images.size() >= 1);
+
+    // check black list
+    if ( _platform == Platform::macOS ) {
+        for (const char* skipProg : sSkipPrograms_macOS) {
+            if ( mainProgProxy->runtimePath() == skipProg ) {
+                diag.error("black listed program");
+                return nullptr;
+            }
+        }
+    } else {
+        for (const char* skipProg : sSkipPrograms_embeddedOSes) {
+            if ( mainProgProxy->runtimePath() == skipProg ) {
+                diag.error("black listed program");
+                return nullptr;
+            }
+        }
+    }
+
+    _mainExecutableIndex = (uint32_t)_images.size() - 1;
+    // add all dylibs needed by main excutable and are not in dyld cache
+    mainProgProxy->addDependentsDeep(*this, nullptr, true);
+    if ( mainProgProxy->diagnostics().hasError() ) {
+        diag.copy(mainProgProxy->diagnostics());
+        return nullptr;
+    }
+
+    // if main program is in .app bundle, look for other mach-o files to add to closure for use by dlopen
+    bool isAppMainExecutable = false;
+    std::string appDir;
+    std::string leafName = basePath(mainProgProxy->runtimePath());
+    size_t posAppX = mainProgProxy->runtimePath().rfind(std::string("/") + leafName + ".appex/");
+    size_t posApp  = mainProgProxy->runtimePath().rfind(std::string("/") + leafName + ".app/");
+    if (  posAppX != std::string::npos ) {
+        appDir = mainProgProxy->runtimePath().substr(0, posAppX+leafName.size()+7);
+        isAppMainExecutable = true;
+    }
+    else if ( posApp != std::string::npos ) {
+        appDir = mainProgProxy->runtimePath().substr(0, posApp+leafName.size()+5);
+        isAppMainExecutable = true;
+    }
+    if ( isAppMainExecutable ) {
+        addExtraMachOsInBundle(appDir);
+        for (size_t i=0; i < _images.size(); ++i) {
+            // note: addDependentsDeep() can append to _images, so can't use regular iterator
+            ImageProxy* aProxy = _images[i];
+            ImageProxy::RPathChain base = { aProxy, nullptr, mainProgProxy->rpaths() };
+            aProxy->addDependentsDeep(*this, &base, false);
+            if ( aProxy->diagnostics().hasError() ) {
+                aProxy->markInvalid();
+                diag.warning("%s could not be added to closure because %s", aProxy->runtimePath().c_str(), aProxy->diagnostics().errorMessage().c_str());
+            }
+        }
+    }
+    else if ( includeDylibsInDir ) {
+        size_t pos = mainProgProxy->runtimePath().rfind('/');
+        if ( pos != std::string::npos ) {
+            std::string mainDir = mainProgProxy->runtimePath().substr(0, pos);
+            addExtraMachOsInBundle(mainDir);
+            for (size_t i=0; i < _images.size(); ++i) {
+                // note: addDependentsDeep() can append to _images, so can't use regular iterator
+                ImageProxy* aProxy = _images[i];
+                aProxy->addDependentsDeep(*this, nullptr, false);
+            }
+        }
+    }
+
+    // add addition dependents of any inserted libraries
+    if ( _mainExecutableIndex != 0 ) {
+        for (uint32_t i=0; i < _mainExecutableIndex; ++i) {
+            _images[i]->addDependentsDeep(*this, nullptr, true);
+            if ( _images[i]->diagnostics().hasError() )
+                return nullptr;
+        }
+    }
+
+    // gather warnings from all statically dependent images
+    for (ImageProxy* proxy : _images) {
+        if ( !proxy->staticallyReferenced() && proxy->diagnostics().hasError() )
+            continue;
+        diag.copy(proxy->diagnostics());
+        if ( diag.hasError() ) {
+            return nullptr;
+        }
+    }
+
+    // get program entry
+    MachOParser mainExecutableParser(mainProgProxy->mh(), _dyldCache.cacheIsMappedRaw());
+    bool usesCRT;
+    uint32_t entryOffset;
+    mainExecutableParser.getEntry(entryOffset, usesCRT);
+
+    // build ImageGroupWriter
+    launch_cache::ImageGroupWriter groupWriter(_groupNum, mainExecutableParser.uses16KPages(), mainExecutableParser.is64(), _dylibsExpectedOnDisk, _inodesAreSameAsRuntime);
+    populateGroupWriter(diag, groupWriter);
+    if ( diag.hasError() )
+        return nullptr;
+
+    // pre-compute libSystem and libdyld into closure
+    ImageRef libdyldEntryImageRef = ImageRef::makeEmptyImageRef();
+    uint32_t libdyldEntryOffset;
+    findLibdyldEntry(diag, libdyldEntryImageRef, libdyldEntryOffset);
+    if ( diag.hasError() )
+        return nullptr;
+    ImageRef libSystemImageRef = ImageRef::makeEmptyImageRef();
+
+    findLibSystem(diag, mainExecutableParser.isSimulatorBinary(), libSystemImageRef);
+    if ( diag.hasError() )
+        return nullptr;
+
+    // build info about missing files and env vars
+    __block StringPool            stringPool;
+    __block std::vector<uint32_t> envVarOffsets;
+    std::vector<uint16_t>         missingFileComponentOffsets;
+    stringPool.add(" ");
+    for (const std::string& path : _mustBeMissingFiles) {
+        size_t start = 1;
+        size_t slashPos = path.find('/', start);
+        while (slashPos != std::string::npos) {
+            std::string component = path.substr(start, slashPos - start);
+            uint16_t offset = stringPool.add(component);
+            missingFileComponentOffsets.push_back(offset);
+            start = slashPos + 1;
+            slashPos = path.find('/', start);
+        }
+        std::string lastComponent = path.substr(start);
+        uint16_t offset = stringPool.add(lastComponent);
+        missingFileComponentOffsets.push_back(offset);
+        missingFileComponentOffsets.push_back(0);  // mark end of a path
+    }
+    missingFileComponentOffsets.push_back(0);  // mark end of all paths
+    if ( missingFileComponentOffsets.size() & 1 )
+        missingFileComponentOffsets.push_back(0);  // 4-byte align array
+    __block uint32_t envVarCount = 0;
+    _pathOverrides.forEachEnvVar(^(const char* envVar) {
+       envVarOffsets.push_back(stringPool.add(envVar));
+       ++envVarCount;
+    });
+
+    // 4-byte align string pool size
+    stringPool.align();
+
+    // malloc a buffer and fill in ImageGroup part
+    uint32_t groupSize             = groupWriter.size();
+    uint32_t missingFilesArraySize = (uint32_t)((missingFileComponentOffsets.size()*sizeof(uint16_t) + 3) & (-4));
+    uint32_t envVarsSize           = (uint32_t)(envVarOffsets.size()*sizeof(uint32_t));
+    uint32_t stringPoolSize        = (uint32_t)stringPool.size();
+    size_t allocSize = sizeof(launch_cache::binary_format::Closure)
+                     + groupSize
+                     + missingFilesArraySize
+                     + envVarsSize
+                     + stringPoolSize;
+    BinaryClosureData* clo = (BinaryClosureData*)malloc(allocSize);
+    groupWriter.finalizeTo(diag, _knownGroups, &clo->group);
+    launch_cache::ImageGroup cloGroup(&clo->group);
+    launch_cache::Image      mainImage(cloGroup.imageBinary(_mainExecutableIndex));
+
+    uint32_t maxImageLoadCount = groupWriter.maxLoadCount(diag, _knownGroups, &clo->group);
+
+    if ( mainImage.isInvalid() ) {
+        free((void*)clo);
+        diag.error("depends on invalid dylib");
+        return nullptr;
+    }
+
+    // fill in closure attributes
+    clo->magic                          = launch_cache::binary_format::Closure::magicV1;
+    clo->usesCRT                        = usesCRT;
+    clo->isRestricted                   = mainProgProxy->isSetUID() || mainExecutableParser.isRestricted();
+    clo->usesLibraryValidation          = mainExecutableParser.usesLibraryValidation();
+    clo->padding                        = 0;
+    clo->missingFileComponentsOffset    = offsetof(launch_cache::binary_format::Closure, group) + groupSize;
+    clo->dyldEnvVarsOffset              = clo->missingFileComponentsOffset + missingFilesArraySize;
+    clo->dyldEnvVarsCount               = envVarCount;
+    clo->stringPoolOffset               = clo->dyldEnvVarsOffset + envVarsSize;
+    clo->stringPoolSize                 = stringPoolSize;
+    clo->libSystemRef                   = libSystemImageRef;
+    clo->libDyldRef                     = libdyldEntryImageRef;
+    clo->libdyldVectorOffset            = libdyldEntryOffset;
+    clo->mainExecutableIndexInGroup     = _mainExecutableIndex;
+    clo->mainExecutableEntryOffset      = entryOffset;
+    clo->initialImageCount              = maxImageLoadCount;
+    _dyldCache.cacheHeader()->getUUID(clo->dyldCacheUUID);
+
+    if ( !mainExecutableParser.getCDHash(clo->mainExecutableCdHash) ) {
+        // if no code signature, fill in 16-bytes with UUID then 4 bytes of zero
+        bzero(clo->mainExecutableCdHash, 20);
+        mainExecutableParser.getUuid(clo->mainExecutableCdHash);
+    }
+    if ( missingFilesArraySize != 0 )
+        memcpy((uint8_t*)clo + clo->missingFileComponentsOffset, &missingFileComponentOffsets[0], missingFileComponentOffsets.size()*sizeof(uint16_t));
+    if ( envVarsSize != 0 )
+        memcpy((uint8_t*)clo + clo->dyldEnvVarsOffset, &envVarOffsets[0], envVarsSize);
+    if ( stringPool.size() != 0 )
+        memcpy((uint8_t*)clo + clo->stringPoolOffset, stringPool.buffer(), stringPool.size());
+
+    return clo;
+}
+
+const BinaryImageGroupData* ImageProxyGroup::makeImageGroupBinary(Diagnostics& diag, const char* const neverEliminateStubs[])
+{
+    const bool continueIfErrors = (_groupNum == 1);
+    bool uses16KPages = true;
+    bool is64 = true;
+    if ( !_images.empty() ) {
+        MachOParser firstParser(_images.front()->mh(), _dyldCache.cacheIsMappedRaw());
+        uses16KPages = firstParser.uses16KPages();
+        is64         = firstParser.is64();
+    }
+    launch_cache::ImageGroupWriter groupWriter(_groupNum, uses16KPages, is64, _dylibsExpectedOnDisk, _inodesAreSameAsRuntime);
+    populateGroupWriter(diag, groupWriter, neverEliminateStubs);
+    if ( diag.hasError() )
+        return nullptr;
+
+    // malloc a buffer and fill in ImageGroup part
+    BinaryImageGroupData* groupData = (BinaryImageGroupData*)malloc(groupWriter.size());
+    groupWriter.finalizeTo(diag, _knownGroups, groupData);
+
+    if ( !continueIfErrors && groupWriter.isInvalid(0) ) {
+        free((void*)groupData);
+        diag.error("depends on invalid dylib");
+        return nullptr;
+    }
+
+    return groupData;
+}
+
+
+void ImageProxyGroup::findLibdyldEntry(Diagnostics& diag, ImageRef& ref, uint32_t& vmOffsetInLibDyld)
+{
+    Diagnostics libDyldDiag;
+    ImageProxy* libDyldProxy = findImage(libDyldDiag, "/usr/lib/system/libdyld.dylib", false, nullptr);
+    if ( libDyldProxy == nullptr ) {
+        diag.error("can't find libdyld.dylib");
+        return;
+    }
+    ref = ImageRef(0, libDyldProxy->groupNum(), libDyldProxy->indexInGroup());
+
+    // find offset of "dyld3::entryVectorForDyld" in libdyld.dylib
+    Diagnostics entryDiag;
+    MachOParser::FoundSymbol dyldEntryInfo;
+    MachOParser libDyldParser(libDyldProxy->mh(), _dyldCache.cacheIsMappedRaw());
+    if ( !libDyldParser.findExportedSymbol(entryDiag, "__ZN5dyld318entryVectorForDyldE", nullptr, dyldEntryInfo, nullptr) ) {
+        diag.error("can't find dyld entry point into libdyld.dylib");
+        return;
+    }
+    vmOffsetInLibDyld = (uint32_t)dyldEntryInfo.value;
+    const LibDyldEntryVector* entry = (LibDyldEntryVector*)(libDyldParser.content(vmOffsetInLibDyld));
+    if ( entry == nullptr ) {
+        diag.error("dyld entry point at offset 0x%0X not found in libdyld.dylib", vmOffsetInLibDyld);
+        return;
+    }
+    if ( entry->vectorVersion != LibDyldEntryVector::kCurrentVectorVersion )
+        diag.error("libdyld.dylib vector version is incompatible with this dyld cache builder");
+    else if ( entry->binaryFormatVersion != launch_cache::binary_format::kFormatVersion )
+        diag.error("libdyld.dylib closures binary format version is incompatible with this dyld cache builder");
+}
+
+void ImageProxyGroup::findLibSystem(Diagnostics& diag, bool forSimulator, ImageRef& ref)
+{
+    Diagnostics libSysDiag;
+    ImageProxy* libSystemProxy = findImage(libSysDiag, forSimulator ? "/usr/lib/libSystem.dylib" : "/usr/lib/libSystem.B.dylib" , false, nullptr);
+    if ( libSystemProxy == nullptr ) {
+        diag.error("can't find libSystem.dylib");
+        return;
+    }
+    ref = ImageRef(0, libSystemProxy->groupNum(), libSystemProxy->indexInGroup());
+}
+
+
+std::vector<ImageProxy*> ImageProxyGroup::flatLookupOrder()
+{
+    std::vector<ImageProxy*> results;
+    // start with main executable and any inserted dylibs
+    for (uint32_t i=0; i <= _mainExecutableIndex; ++i)
+        results.push_back(_images[i]);
+
+    // recursive add dependents of main executable
+    _images[_mainExecutableIndex]->addToFlatLookup(results);
+
+    // recursive add dependents of any inserted dylibs
+    for (uint32_t i=0; i < _mainExecutableIndex; ++i)
+        _images[i]->addToFlatLookup(results);
+
+    return results;
+}
+
+void ImageProxyGroup::populateGroupWriter(Diagnostics& diag, launch_cache::ImageGroupWriter& groupWriter, const char* const neverEliminateStubs[])
+{
+    const bool buildingDylibsInCache = (_groupNum == 0);
+    const bool continueIfErrors      = (_groupNum == 1);
+
+    std::unordered_set<std::string>  neverStubEliminate;
+    if ( neverEliminateStubs != nullptr ) {
+        for (const char* const* nes=neverEliminateStubs; *nes != nullptr; ++nes)
+            neverStubEliminate.insert(*nes);
+    }
+    
+    // pass 1: add all images
+    const uint64_t cacheUnslideBaseAddress = _dyldCache.cacheHeader()->unslidLoadAddress();
+    const uint32_t imageCount = (uint32_t)_images.size();
+    groupWriter.setImageCount(imageCount);
+    for (uint32_t i=0; i < imageCount; ++i) {
+        MachOParser imageParser(_images[i]->mh(), _dyldCache.cacheIsMappedRaw());
+        assert((imageParser.inDyldCache() == buildingDylibsInCache) && "all images must be same type");
+        // add info for each image
+        groupWriter.setImagePath(i, _images[i]->runtimePath().c_str());
+        groupWriter.setImageIsBundle(i, (imageParser.fileType() == MH_BUNDLE));
+        bool hasObjC = imageParser.hasObjC();
+        groupWriter.setImageHasObjC(i, hasObjC);
+        bool isEncrypted = imageParser.isEncrypted();
+        groupWriter.setImageIsEncrypted(i, isEncrypted);
+        bool mayHavePlusLoad = false;
+        if ( hasObjC ) {
+            mayHavePlusLoad = isEncrypted || imageParser.hasPlusLoadMethod(diag);
+            groupWriter.setImageMayHavePlusLoads(i, mayHavePlusLoad);
+        }
+        groupWriter.setImageHasWeakDefs(i, imageParser.hasWeakDefs());
+        groupWriter.setImageMustBeThisDir(i, _images[i]->cwdMustBeThisDir());
+        groupWriter.setImageIsPlatformBinary(i, _images[i]->isPlatformBinary());
+        groupWriter.setImageOverridableDylib(i, !_stubEliminated || (neverStubEliminate.count(_images[i]->runtimePath()) != 0));
+        uuid_t uuid;
+        if ( imageParser.getUuid(uuid) )
+            groupWriter.setImageUUID(i, uuid);
+        if ( _inodesAreSameAsRuntime ) {
+            groupWriter.setImageFileMtimeAndInode(i, _images[i]->fileModTime(), _images[i]->fileInode());
+        }
+        else {
+            uint8_t cdHash[20];
+            if ( !imageParser.getCDHash(cdHash) )
+                bzero(cdHash, 20);
+            // if image is not code signed, cdHash filled with all zeros
+            groupWriter.setImageCdHash(i, cdHash);
+        }
+        if ( !buildingDylibsInCache ) {
+            groupWriter.setImageSliceOffset(i, _images[i]->sliceFileOffset());
+            uint32_t fairPlayTextOffset;
+            uint32_t fairPlaySize;
+            if ( imageParser.isFairPlayEncrypted(fairPlayTextOffset, fairPlaySize) )
+                groupWriter.setImageFairPlayRange(i, fairPlayTextOffset, fairPlaySize);
+            uint32_t codeSigOffset;
+            uint32_t codeSigSize;
+            if ( imageParser.hasCodeSignature(codeSigOffset, codeSigSize) )
+                groupWriter.setImageCodeSignatureLocation(i, codeSigOffset, codeSigSize);
+        }
+        groupWriter.setImageDependentsCount(i, imageParser.dependentDylibCount());
+        // add segments to image
+        groupWriter.setImageSegments(i, imageParser, cacheUnslideBaseAddress);
+        // add initializers to image
+        __block std::vector<uint32_t> initOffsets;
+        imageParser.forEachInitializer(diag, ^(uint32_t offset) {
+            initOffsets.push_back(offset);
+        });
+        groupWriter.setImageInitializerOffsets(i, initOffsets);
+        if ( diag.hasError() && !continueIfErrors ) {
+            return;
+        }
+        // add DOFs to image
+        __block std::vector<uint32_t> dofOffsets;
+        imageParser.forEachDOFSection(diag, ^(uint32_t offset) {
+            dofOffsets.push_back(offset);
+        });
+        groupWriter.setImageDOFOffsets(i, dofOffsets);
+        if ( diag.hasError() && !continueIfErrors ) {
+            return;
+        }
+        bool neverUnload = false;
+        if ( buildingDylibsInCache )
+            neverUnload = true;
+        if ( _images[i]->staticallyReferenced() )
+            neverUnload = true;
+        if ( imageParser.hasObjC() && (imageParser.fileType() == MH_DYLIB) )
+            neverUnload = true;
+        if ( imageParser.hasThreadLocalVariables() )
+            neverUnload = true;
+        if ( !dofOffsets.empty() )
+            neverUnload = true;
+        groupWriter.setImageNeverUnload(i, neverUnload);
+        if ( _images[i]->invalid() )
+            groupWriter.setImageInvalid(i);
+        // record if this is an override of an OS dylib
+        ImageRef stdRef = _images[i]->overrideOf();
+        if ( stdRef != ImageRef::weakImportMissing() ) {
+            ImageRef thisImageRef(0, _groupNum, i);
+            groupWriter.addImageIsOverride(stdRef, thisImageRef);
+        }
+
+        // add alias if runtimepath does not match installName
+        if ( imageParser.fileType() == MH_DYLIB ) {
+            const char* installName = imageParser.installName();
+            if ( installName[0] == '/' ) {
+                if ( _images[i]->runtimePath() != installName ) {
+                    // add install name as an alias
+                    groupWriter.addImageAliasPath(i, installName);
+                }
+            }
+            // IOKit.framework on embedded uses not flat bundle, but clients dlopen() it as if it were flat
+            if ( buildingDylibsInCache && (_platform != Platform::macOS) && (_images[i]->runtimePath() == "/System/Library/Frameworks/IOKit.framework/Versions/A/IOKit") ) {
+                groupWriter.addImageAliasPath(i, "/System/Library/Frameworks/IOKit.framework/IOKit");
+            }
+        }
+    }
+
+    // pass 2: add all dependencies (now that we have indexes defined)
+    for (uint32_t i=0; (i < imageCount) && diag.noError(); ++i) {
+        // add dependents to image
+        __block uint32_t depIndex = 0;
+        _images[i]->forEachDependent(^(ImageProxy* dep, LinkKind kind) {
+            if ( dep == nullptr ) {
+                if ( kind == LinkKind::weak )
+                    groupWriter.setImageDependent(i, depIndex, launch_cache::binary_format::ImageRef::weakImportMissing());
+                else
+                    groupWriter.setImageInvalid(i);
+            }
+            else {
+                launch_cache::binary_format::ImageRef ref((uint8_t)kind, dep->groupNum(), dep->indexInGroup());
+                groupWriter.setImageDependent(i, depIndex, ref);
+            }
+            ++depIndex;
+        });
+    }
+
+    // pass 3: invalidate any images dependent on invalid images)
+    if ( continueIfErrors ) {
+        const launch_cache::binary_format::ImageRef missingRef = launch_cache::binary_format::ImageRef::weakImportMissing();
+        __block bool somethingInvalidated = false;
+        do {
+            somethingInvalidated = false;
+            for (uint32_t i=0; i < imageCount; ++i) {
+                if ( groupWriter.isInvalid(i) )
+                    continue;
+                uint32_t depCount = groupWriter.imageDependentsCount(i);
+                for (uint32_t depIndex=0; depIndex < depCount; ++depIndex) {
+                    launch_cache::binary_format::ImageRef ref = groupWriter.imageDependent(i, depIndex);
+                    if ( ref == missingRef )
+                        continue;
+                    if ( ref.groupNum() == _groupNum ) {
+                        if ( groupWriter.isInvalid(ref.indexInGroup()) ) {
+                            // this image depends on something invalid, so mark it invalid
+                            //fprintf(stderr, "warning: image %s depends on invalid %s\n", _images[i]->runtimePath().c_str(), _images[ref.index()]->runtimePath().c_str());
+                            groupWriter.setImageInvalid(i);
+                            somethingInvalidated = true;
+                            break;
+                        }
+                    }
+                }
+            }
+        } while (somethingInvalidated);
+    }
+
+    // pass 4: add fixups for each image, if needed
+    bool someBadFixups = false;
+    if ( !buildingDylibsInCache ) {
+        // compute fix ups for all images
+        __block std::vector<ImageProxy::FixupInfo> fixupInfos;
+        fixupInfos.resize(imageCount);
+        for (uint32_t imageIndex=0; imageIndex < imageCount; ++imageIndex) {
+            if ( groupWriter.isInvalid(imageIndex) )
+                continue;
+            Diagnostics fixupDiag;
+            fixupInfos[imageIndex] = _images[imageIndex]->buildFixups(fixupDiag, cacheUnslideBaseAddress, groupWriter);
+            if ( fixupDiag.hasError() ) {
+                // disable image in group
+                someBadFixups = true;
+                groupWriter.setImageInvalid(imageIndex);
+                if ( continueIfErrors ) {
+                    diag.warning("fixup problem in %s: %s", _images[imageIndex]->runtimePath().c_str(), fixupDiag.errorMessage().c_str());
+                    continue;
+                }
+                else {
+                    diag.error("fixup problem in %s: %s", _images[imageIndex]->runtimePath().c_str(), fixupDiag.errorMessage().c_str());
+                    return;
+                }
+            }
+        }
+        // if building closure, build patches to shared cache
+        if ( _groupNum == 2) {
+            std::unordered_set<ImageProxy*> staticImagesWithWeakDefs;
+            ImageProxyGroup* cacheGroup = _nextSearchGroup->_nextSearchGroup;
+            assert(cacheGroup->_basedOn != nullptr);
+            launch_cache::ImageGroup dyldCacheGroup(cacheGroup->_basedOn);
+            for (uint32_t imageIndex=0; imageIndex < imageCount; ++imageIndex) {
+                if ( groupWriter.isInvalid(imageIndex) )
+                    continue;
+                ImageProxy* thisProxy = _images[imageIndex];
+                // Only process interposing info on dylibs statically linked into closure
+                if ( !thisProxy->staticallyReferenced() )
+                    continue;
+                MachOParser imageParser(thisProxy->mh(), _dyldCache.cacheIsMappedRaw());
+                // if any images in closure interpose on something in dyld cache, record the cache patches needed
+                imageParser.forEachInterposingTuple(diag, ^(uint32_t segIndex, uint64_t replacementSegOffset, uint64_t replaceeSegOffset, uint64_t replacementContent, bool& tupleStop) {
+                    if ( _groupNum != 2 ) {
+                        groupWriter.setImageInvalid(imageIndex);
+                        return;
+                    }
+                    TargetSymbolValue interposeReplacee    = TargetSymbolValue::makeInvalid();
+                    TargetSymbolValue interposeReplacement = TargetSymbolValue::makeInvalid();
+                    for (const FixUp& fixup : fixupInfos[imageIndex].fixups) {
+                        if ( fixup.segIndex != segIndex )
+                            continue;
+                        if ( fixup.segOffset == replacementSegOffset ) {
+                            if ( fixup.type == launch_cache::ImageGroupWriter::FixupType::rebase ) {
+                                uint64_t offsetInImage = replacementContent - imageParser.preferredLoadAddress();
+                                interposeReplacement = TargetSymbolValue::makeGroupValue(2, imageIndex, offsetInImage, false);
+                            }
+                            else {
+                                diag.warning("bad interposing implementation in %s", _images[imageIndex]->runtimePath().c_str());
+                                return;
+                            }
+                        }
+                        else if ( fixup.segOffset ==  replaceeSegOffset ) {
+                            if ( fixup.type == launch_cache::ImageGroupWriter::FixupType::pointerBind ) {
+                                interposeReplacee = fixup.target;
+                            }
+                            else {
+                                diag.warning("bad interposing target in %s", _images[imageIndex]->runtimePath().c_str());
+                                return;
+                            }
+                        }
+                    }
+                    // scan through fixups of other images in closure looking to see what functions this entry references
+                    for (uint32_t otherIndex=0; otherIndex < imageCount; ++otherIndex) {
+                        if ( otherIndex == imageIndex )
+                            continue;
+                        for (FixUp& fixup : fixupInfos[otherIndex].fixups) {
+                            switch ( fixup.type ) {
+                                case launch_cache::ImageGroupWriter::FixupType::pointerBind:
+                                case launch_cache::ImageGroupWriter::FixupType::pointerLazyBind:
+                                    // alter fixup to use interposed function instead of requested
+                                    if ( fixup.target == interposeReplacee )
+                                        fixup.target = interposeReplacement;
+                                    break;
+                                case launch_cache::ImageGroupWriter::FixupType::rebase:
+                                case launch_cache::ImageGroupWriter::FixupType::rebaseText:
+                                case launch_cache::ImageGroupWriter::FixupType::ignore:
+                                case launch_cache::ImageGroupWriter::FixupType::bindText:
+                                case launch_cache::ImageGroupWriter::FixupType::bindTextRel:
+                                case launch_cache::ImageGroupWriter::FixupType::bindImportJmpRel:
+                                   break;
+                            }
+                        }
+                    }
+                    if ( interposeReplacee.isInvalid() || interposeReplacement.isInvalid() ) {
+                        diag.error("malformed interposing section in %s", _images[imageIndex]->runtimePath().c_str());
+                        tupleStop = true;
+                        return;
+                    }
+                    // record any overrides in shared cache that will need to be applied at launch time
+                    uint64_t offsetInCache;
+                    if ( interposeReplacee.isSharedCacheTarget(offsetInCache) ) {
+                        uint32_t patchTableIndex;
+                        if ( dyldCacheGroup.hasPatchTableIndex((uint32_t)offsetInCache, patchTableIndex) ) {
+                            uint32_t    replacementGroupNum;
+                            uint32_t    replacementIndexInGroup;
+                            uint64_t    replacementOffsetInImage;
+                            assert(interposeReplacement.isGroupImageTarget(replacementGroupNum, replacementIndexInGroup, replacementOffsetInImage));
+                            assert(replacementGroupNum == 2);
+                            assert(replacementIndexInGroup < (1 << 8));
+                            assert(replacementOffsetInImage < 0xFFFFFFFFULL);
+                            DyldCacheOverride cacheOverride;
+                            cacheOverride.patchTableIndex = patchTableIndex;
+                            cacheOverride.imageIndex      = replacementIndexInGroup;
+                            cacheOverride.imageOffset     = replacementOffsetInImage;
+                            _cacheOverrides.push_back(cacheOverride);
+                        }
+                    }
+                });
+                if ( diag.hasError() && !continueIfErrors ) {
+                    return;
+                }
+                // if any dylibs in the closure override a dyld cache dylib, then record the cache patches needed
+                ImageRef overrideOf = thisProxy->overrideOf();
+                if ( (overrideOf != ImageRef::makeEmptyImageRef()) && (overrideOf.groupNum() == 0) ) {
+                     //fprintf(stderr, "need to patch %s into cache\n", thisProxy->runtimePath().c_str());
+                    const launch_cache::Image imageInCache = dyldCacheGroup.image(overrideOf.indexInGroup());
+                    const mach_header* imageInCacheMH = (mach_header*)((char*)(_dyldCache.cacheHeader()) + imageInCache.cacheOffset());
+                    MachOParser inCacheParser(imageInCacheMH, _dyldCache.cacheIsMappedRaw());
+                    // walk all exported symbols in dylib in cache
+                    inCacheParser.forEachExportedSymbol(diag, ^(const char* symbolName, uint64_t imageOffset, bool isReExport, bool &stop) {
+                        if ( isReExport )
+                            return;
+                        uint32_t cacheOffsetOfSymbol = (uint32_t)(imageInCache.cacheOffset() + imageOffset);
+                        //fprintf(stderr, "  patch cache offset 0x%08X which is %s\n", cacheOffsetOfSymbol, symbolName);
+                        // for each exported symbol, see if it is in patch table (used by something else in cache)
+                        uint32_t patchTableIndex;
+                        if ( dyldCacheGroup.hasPatchTableIndex(cacheOffsetOfSymbol, patchTableIndex) ) {
+                            //fprintf(stderr, "  need patch cache offset 0x%08X\n", cacheOffsetOfSymbol);
+                            // lookup address of symbol in override dylib and add patch info
+                            MachOParser::FoundSymbol foundInfo;
+                            if ( imageParser.findExportedSymbol(diag, symbolName, nullptr, foundInfo, nullptr) ) {
+                                DyldCacheOverride cacheOverride;
+                                assert(patchTableIndex < (1 << 24));
+                                assert(thisProxy->indexInGroup() < (1 << 8));
+                                assert(foundInfo.value < (1ULL << 32));
+                                cacheOverride.patchTableIndex = patchTableIndex;
+                                cacheOverride.imageIndex      = thisProxy->indexInGroup();
+                                cacheOverride.imageOffset     = foundInfo.value;
+                                _cacheOverrides.push_back(cacheOverride);
+                            }
+                        }
+                    });
+                }
+                // save off all images in closure with weak defines
+                if ( thisProxy->mh()->flags & (MH_WEAK_DEFINES|MH_BINDS_TO_WEAK) ) {
+                    staticImagesWithWeakDefs.insert(thisProxy);
+                }
+            }
+            // if any dylibs in the closure override a weak symbol in a cached dylib, then record the cache patches needed
+            if ( !staticImagesWithWeakDefs.empty() ) {
+                // build list of all weak def symbol names
+                __block std::unordered_map<std::string, DyldCacheOverride> weakSymbols;
+                for (ImageProxy* proxy : staticImagesWithWeakDefs ) {
+                    MachOParser weakDefParser(proxy->mh(), _dyldCache.cacheIsMappedRaw());
+                    weakDefParser.forEachWeakDef(diag, ^(bool strongDef, uint32_t segIndex, uint64_t segOffset, uint64_t addend, const char* symbolName, bool& stop) {
+                        weakSymbols[symbolName] = { 0, 0, 0 };
+                    });
+                }
+                // do a flat namespace walk of all images
+                std::vector<ImageProxy*> flatSearchOrder = flatLookupOrder();
+                for (ImageProxy* proxy : flatSearchOrder) {
+                    // only look at images that participate in weak coalescing
+                    if ( (proxy->mh()->flags & (MH_WEAK_DEFINES|MH_BINDS_TO_WEAK)) == 0 )
+                        continue;
+                    // look only at images in closure
+                    if ( proxy->groupNum() == 2 ) {
+                        MachOParser weakDefParser(proxy->mh(), _dyldCache.cacheIsMappedRaw());
+                        // check if this closure image defines any of the not-yet found weak symbols
+                        for (auto& entry : weakSymbols ) {
+                            if ( entry.second.imageOffset != 0 )
+                                continue;
+                            Diagnostics weakDiag;
+                            MachOParser::FoundSymbol foundInfo;
+                            if ( weakDefParser.findExportedSymbol(weakDiag, entry.first.c_str(), nullptr, foundInfo, nullptr) ) {
+                                assert(proxy->indexInGroup() < (1 << 8));
+                                assert(foundInfo.value < (1ULL << 32));
+                                entry.second.imageIndex  = proxy->indexInGroup();
+                                entry.second.imageOffset = foundInfo.value;
+                            }
+                        }
+                    }
+                }
+                for (ImageProxy* proxy : flatSearchOrder) {
+                    // only look at images that participate in weak coalescing
+                    if ( (proxy->mh()->flags & (MH_WEAK_DEFINES|MH_BINDS_TO_WEAK)) == 0 )
+                        continue;
+                    // look only at images in dyld cache
+                    if ( proxy->groupNum() == 0 ) {
+                        const launch_cache::Image imageInCache = dyldCacheGroup.image(proxy->indexInGroup());
+                        MachOParser inCacheParser(proxy->mh(), _dyldCache.cacheIsMappedRaw());
+                        Diagnostics cacheDiag;
+                        for (auto& entry : weakSymbols) {
+                            if ( entry.second.imageOffset == 0 )
+                                continue;
+                            Diagnostics weakDiag;
+                            MachOParser::FoundSymbol foundInfo;
+                            if ( inCacheParser.findExportedSymbol(weakDiag, entry.first.c_str(), nullptr, foundInfo, nullptr) ) {
+                                uint32_t cacheOffsetOfSymbol = (uint32_t)(imageInCache.cacheOffset() + foundInfo.value);
+                                // see if this symbol is in patch table (used by something else in cache)
+                                uint32_t patchTableIndex;
+                                if ( dyldCacheGroup.hasPatchTableIndex(cacheOffsetOfSymbol, patchTableIndex) ) {
+                                    //fprintf(stderr, "  need patch cache offset 0x%08X\n", cacheOffsetOfSymbol);
+                                    DyldCacheOverride cacheOverride;
+                                    cacheOverride.patchTableIndex = patchTableIndex;
+                                    cacheOverride.imageIndex      = entry.second.imageIndex;
+                                    cacheOverride.imageOffset     = entry.second.imageOffset;
+                                    _cacheOverrides.push_back(cacheOverride);
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        // record fixups for each image
+        for (uint32_t imageIndex=0; imageIndex < imageCount; ++imageIndex) {
+            groupWriter.setImageFixups(diag, imageIndex, fixupInfos[imageIndex].fixups, fixupInfos[imageIndex].hasTextRelocs);
+        }
+    }
+
+    // pass 5: invalidate any images dependent on invalid images)
+    if ( someBadFixups && continueIfErrors ) {
+        __block bool somethingInvalidated = false;
+        do {
+            somethingInvalidated = false;
+            for (uint32_t i=0; i < imageCount; ++i) {
+                if ( groupWriter.isInvalid(i) )
+                    continue;
+                uint32_t depCount = groupWriter.imageDependentsCount(i);
+                for (uint32_t depIndex=0; depIndex < depCount; ++depIndex) {
+                    launch_cache::binary_format::ImageRef ref = groupWriter.imageDependent(i, depIndex);
+                    if ( ref.groupNum() == _groupNum ) {
+                        if ( groupWriter.isInvalid(ref.indexInGroup()) ) {
+                            // this image depends on something invalid, so mark it invalid
+                            //fprintf(stderr, "warning: image %s depends on invalid %s\n", _images[i]->runtimePath().c_str(), _images[ref.index()]->runtimePath().c_str());
+                            groupWriter.setImageInvalid(i);
+                            somethingInvalidated = true;
+                            break;
+                        }
+                    }
+                }
+            }
+        } while (somethingInvalidated);
+    }
+
+    // pass 6: compute initializer lists for each image
+    const bool log = false;
+    for (uint32_t imageIndex=0; imageIndex < imageCount; ++imageIndex) {
+        if ( groupWriter.isInvalid(imageIndex) )
+            continue;
+
+        auto inits = _images[imageIndex]->getInitBeforeList(*this);
+        if ( log && buildingDylibsInCache ) {
+            fprintf(stderr, "%s\n   init list: ", _images[imageIndex]->runtimePath().c_str());
+            for (launch_cache::binary_format::ImageRef ref : inits) {
+                if ( ref.groupNum() == 0 ) {
+                    std::string dep = _images[ref.indexInGroup()]->runtimePath();
+                    size_t off = dep.rfind('/');
+                    fprintf(stderr, "%s, ", dep.substr(off+1).c_str());
+                }
+            }
+            fprintf(stderr, "\n");
+        }
+        groupWriter.setImageInitBefore(imageIndex, inits);
+    }
+
+    // pass 7: compute DOFs
+    for (uint32_t imageIndex=0; imageIndex < imageCount; ++imageIndex) {
+        if ( groupWriter.isInvalid(imageIndex) )
+            continue;
+
+        auto inits = _images[imageIndex]->getInitBeforeList(*this);
+        if ( log && buildingDylibsInCache ) {
+            fprintf(stderr, "%s\n   DOFs: ", _images[imageIndex]->runtimePath().c_str());
+            for (launch_cache::binary_format::ImageRef ref : inits) {
+                if ( ref.groupNum() == 0 ) {
+                    std::string dep = _images[ref.indexInGroup()]->runtimePath();
+                    size_t off = dep.rfind('/');
+                    fprintf(stderr, "%s, ", dep.substr(off+1).c_str());
+                }
+            }
+            fprintf(stderr, "\n");
+        }
+        groupWriter.setImageInitBefore(imageIndex, inits);
+    }
+
+    // pass 8: add patch table entries iff this is dyld cache ImageGroup
+    assert(buildingDylibsInCache == (_patchTable != nullptr));
+    if ( _patchTable != nullptr ) {
+        for (uint32_t i=0; i < imageCount; ++i) {
+            const auto pos = _patchTable->find(_images[i]->mh());
+            if ( pos != _patchTable->end() ) {
+                for (const auto& entry : pos->second ) {
+                    uint32_t defFunctionOffset = entry.first;
+                    groupWriter.setImagePatchLocations(i, defFunctionOffset, entry.second);
+                }
+            }
+        }
+    }
+
+    // if this is a main closure group with an interposing dylib, add cache overrides
+    if ( !_cacheOverrides.empty() ) {
+        groupWriter.setGroupCacheOverrides(_cacheOverrides);
+    }
+
+    // align string pool
+    groupWriter.alignStringPool();
+}
+
+
+
+} // namespace dyld3
+
+
diff --git a/dyld3/shared-cache/ImageProxy.h b/dyld3/shared-cache/ImageProxy.h
new file mode 100644 (file)
index 0000000..35bf42c
--- /dev/null
@@ -0,0 +1,256 @@
+/*
+ * Copyright (c) 2017 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 ImageProxy_h
+#define ImageProxy_h
+
+#include <stdint.h>
+
+#include <string>
+#include <vector>
+#include <set>
+#include <unordered_map>
+
+#include "DyldSharedCache.h"
+#include "Diagnostics.h"
+#include "LaunchCache.h"
+#include "LaunchCacheWriter.h"
+#include "PathOverrides.h"
+#include "ClosureBuffer.h"
+#include "DyldCacheParser.h"
+
+
+namespace dyld3 {
+
+typedef launch_cache::binary_format::Image              BinaryImageData;
+typedef launch_cache::binary_format::ImageGroup         BinaryImageGroupData;
+typedef launch_cache::binary_format::Closure            BinaryClosureData;
+typedef launch_cache::binary_format::ImageRef           ImageRef;
+typedef launch_cache::Image::LinkKind                   LinkKind;
+typedef launch_cache::ImageGroupWriter::FixUp           FixUp;
+typedef launch_cache::binary_format::DyldCacheOverride  DyldCacheOverride;
+typedef launch_cache::ImageGroupList                    ImageGroupList;
+
+
+
+
+class ImageProxyGroup;
+
+class ImageProxy
+{
+public:
+                            ImageProxy(const mach_header* mh, const BinaryImageData* image, uint32_t indexInGroup, bool dyldCacheIsRaw);
+                            ImageProxy(const DyldSharedCache::MappedMachO& mapping, uint32_t groupNum, uint32_t indexInGroup, bool dyldCacheIsRaw);
+
+    struct RPathChain {
+        ImageProxy*                         inProxy;
+        const RPathChain*                   prev;
+        const std::vector<std::string>&     rpaths;
+    };
+
+    struct InitOrderInfo {
+        bool                     beforeHas(ImageRef);
+        bool                     upwardHas(ImageProxy*);
+        void                     removeRedundantUpwards();
+        std::vector<ImageRef>    initBefore;
+        std::vector<ImageProxy*> danglingUpward;
+    };
+
+    struct FixupInfo {
+        std::vector<FixUp>  fixups;
+        bool                hasTextRelocs = false;
+    };
+
+    void                    recursiveBuildInitBeforeInfo(ImageProxyGroup& owningGroup);
+    void                    addDependentsShallow(ImageProxyGroup& owningGroup, RPathChain* chain=nullptr);
+    void                    addDependentsDeep(ImageProxyGroup& owningGroup, RPathChain* chain, bool staticallyReferenced);
+    void                    markInvalid() { _invalid = true; }
+
+    uint32_t                groupNum() const                { return _groupNum; }
+    uint32_t                indexInGroup() const            { return _indexInGroup; }
+    const mach_header*      mh() const                      { return _mh; }
+    const std::string&      runtimePath() const             { return _runtimePath; }
+    uint64_t                sliceFileOffset() const         { return _sliceFileOffset; }
+    uint64_t                fileModTime() const             { return _modTime; }
+    uint64_t                fileInode() const               { return _inode; }
+    bool                    isSetUID() const                { return _isSetUID; }
+    bool                    invalid() const                 { return _invalid; }
+    bool                    staticallyReferenced() const    { return _staticallyReferenced; }
+    bool                    cwdMustBeThisDir() const        { return _cwdMustBeThisDir; }
+    bool                    isPlatformBinary() const        { return _platformBinary; }
+    bool                    isProxyForCachedDylib() const   { return _imageBinaryData != nullptr; }
+    const Diagnostics&      diagnostics() const             { return _diag; }
+    ImageRef                overrideOf() const              { return _overrideOf; }
+    bool                    inLibSystem() const;
+    void                    setCwdMustBeThisDir()           { _cwdMustBeThisDir = true; }
+    void                    setPlatformBinary()             { _platformBinary = true; }
+    void                    setOverrideOf(uint32_t groupNum, uint32_t indexInGroup);
+    void                    checkIfImageOverride(const std::string& runtimeLoadPath);
+    void                    forEachDependent(void (^handler)(ImageProxy* dep, LinkKind)) const;
+    FixupInfo               buildFixups(Diagnostics& diag, uint64_t cacheUnslideBaseAddress, launch_cache::ImageGroupWriter& groupWriter) const;
+    bool                    findExportedSymbol(Diagnostics& diag, const char* symbolName, MachOParser::FoundSymbol& foundInfo) const;
+    void                    convertInitBeforeInfoToArray(ImageProxyGroup& owningGroup);
+    void                    addToFlatLookup(std::vector<ImageProxy*>& imageList);
+    const std::vector<ImageRef>&    getInitBeforeList(ImageProxyGroup& owningGroup);
+    const std::vector<std::string>& rpaths() { return _rpaths; }
+
+private:
+    void                    processRPaths(ImageProxyGroup& owningGroup);
+
+    const mach_header*     const _mh;
+    uint64_t               const _sliceFileOffset;
+    uint64_t               const _modTime;
+    uint64_t               const _inode;
+    const BinaryImageData* const _imageBinaryData;    // only used if proxy is for image in shared cache
+    std::string            const _runtimePath;
+    bool                   const _isSetUID;
+    bool                   const _dyldCacheIsRaw;
+    uint32_t               const _groupNum;
+    uint32_t               const _indexInGroup;
+    bool                         _platformBinary;
+    Diagnostics                  _diag;
+    std::vector<ImageProxy*>     _dependents;
+    std::vector<LinkKind>        _dependentsKind;
+    std::vector<std::string>     _rpaths;
+    InitOrderInfo                _initBeforesInfo;
+    std::vector<ImageRef>        _initBeforesArray;
+    ImageRef                     _overrideOf;
+    bool                         _directDependentsSet;
+    bool                         _deepDependentsSet;
+    bool                         _initBeforesArraySet;
+    bool                         _initBeforesComputed;
+    bool                         _invalid;
+    bool                         _staticallyReferenced;
+    bool                         _cwdMustBeThisDir;
+};
+
+
+class ImageProxyGroup
+{
+public:
+                                    ~ImageProxyGroup();
+
+
+    typedef std::unordered_map<const mach_header*, std::unordered_map<uint32_t, std::unordered_set<uint32_t>>> PatchTable;
+
+
+    // used when building dyld shared cache
+    static ImageProxyGroup*         makeDyldCacheDylibsGroup(Diagnostics& diag, const DyldCacheParser& dyldCache, const std::vector<DyldSharedCache::MappedMachO>& cachedDylibs,
+                                                             const std::vector<std::string>& buildTimePrefixes, const PatchTable& patchTable,
+                                                             bool stubEliminated, bool dylibsExpectedOnDisk);
+
+    // used when building dyld shared cache
+    static ImageProxyGroup*         makeOtherOsGroup(Diagnostics& diag, const DyldCacheParser& dyldCache, ImageProxyGroup* cachedDylibsGroup,
+                                                     const std::vector<DyldSharedCache::MappedMachO>& otherDylibsAndBundles,
+                                                     bool inodesAreSameAsRuntime, const std::vector<std::string>& buildTimePrefixes);
+
+     const BinaryImageGroupData*    makeImageGroupBinary(Diagnostics& diag, const char* const neverEliminateStubs[]=nullptr);
+
+    // used when building dyld shared cache
+    static BinaryClosureData*       makeClosure(Diagnostics& diag, const DyldCacheParser& dyldCache, ImageProxyGroup* cachedDylibsGroup,
+                                                ImageProxyGroup* otherOsDylibs, const DyldSharedCache::MappedMachO& mainProg,
+                                                bool inodesAreSameAsRuntime, const std::vector<std::string>& buildTimePrefixes);
+
+    // used by closured for dlopen of unknown dylibs
+    static const BinaryImageGroupData* makeDlopenGroup(Diagnostics& diag, const DyldCacheParser& dyldCache, uint32_t groupNum,
+                                                       const std::vector<const BinaryImageGroupData*>& existingGroups,
+                                                       const std::string& imagePath, const std::vector<std::string>& envVars);
+
+    static const BinaryImageGroupData* makeDlopenGroup(Diagnostics& diag, const ClosureBuffer& buffer, task_t requestor, const std::vector<std::string>& buildTimePrefixes={});
+
+    static BinaryClosureData*          makeClosure(Diagnostics& diag, const ClosureBuffer& buffer, task_t requestor, const std::vector<std::string>& buildTimePrefixes={});
+
+
+    //
+    // Creates a binary launch closure for the specified main executable.
+    // Used by closured and dyld_closure_util
+    //
+    // The closure is allocated with malloc().  Use free() to release when done.
+    // The size of the closure can be determined using Closure::size().
+    // If the closure cannot be built (e.g. app needs a symbol not exported by a framework),
+    // the reason for the failure is returned as a string in the diag parameter.
+    // The mainProgRuntimePath path is the path the program will be at runtime.
+    // The buildTimePrefixes is a list of prefixes to add to each path during closure
+    // creation to find the files at buildtime.
+    //
+   static BinaryClosureData*       makeClosure(Diagnostics& diag, const DyldCacheParser& dyldCache,
+                                               const std::string& mainProgRuntimePath, bool includeDylibsInDir,
+                                               const std::vector<std::string>& buildTimePrefixes={},
+                                               const std::vector<std::string>& envVars={});
+
+
+private:
+    friend class ImageProxy;
+
+                                    ImageProxyGroup(uint32_t groupNum, const DyldCacheParser& dyldCache, const BinaryImageGroupData* basedOn,
+                                                    ImageProxyGroup* next, const std::string& mainProgRuntimePath,
+                                                    const std::vector<const BinaryImageGroupData*>& knownGroups,
+                                                    const std::vector<std::string>& buildTimePrefixes,
+                                                    const std::vector<std::string>& envVars,
+                                                    bool stubsEliminated=false, bool dylibsExpectedOnDisk=true, bool inodesAreSameAsRuntime=true);
+
+    ImageProxy*                     findImage(Diagnostics& diag, const std::string& runtimePath, bool canBeMissing, ImageProxy::RPathChain*);
+    ImageProxy*                     findAbsoluteImage(Diagnostics& diag, const std::string& runtimePath, bool canBeMissing, bool makeErrorMessage, bool pathIsReal=false);
+    bool                            builtImageStillValid(const launch_cache::Image& image);
+    const std::string&              mainProgRuntimePath() { return _mainProgRuntimePath; }
+    DyldSharedCache::MappedMachO*   addMappingIfValidMachO(Diagnostics& diag, const std::string& runtimePath, bool ignoreMainExecutables=false);
+    BinaryClosureData*              makeClosureBinary(Diagnostics& diag, ImageProxy* mainProg, bool includeDylibsInDir);
+    void                            findLibdyldEntry(Diagnostics& diag, ImageRef& ref, uint32_t& offset);
+    void                            findLibSystem(Diagnostics& diag, bool sim, ImageRef& ref);
+    void                            populateGroupWriter(Diagnostics& diag, launch_cache::ImageGroupWriter& groupWriter, const char* const neverEliminateStubs[]=nullptr);
+    std::string                     normalizedPath(const std::string& path);
+    void                            addExtraMachOsInBundle(const std::string& appDir);
+    bool                            addInsertedDylibs(Diagnostics& diag);
+    std::vector<ImageProxy*>        flatLookupOrder();
+
+    PathOverrides                                   _pathOverrides;
+    const BinaryImageGroupData*                     _basedOn;   // if not null, then lazily populate _images
+    const PatchTable*                               _patchTable;
+    ImageProxyGroup*  const                         _nextSearchGroup;
+    const DyldCacheParser                           _dyldCache;
+    uint32_t const                                  _groupNum;
+    bool const                                      _stubEliminated;
+    bool const                                      _dylibsExpectedOnDisk;
+    bool const                                      _inodesAreSameAsRuntime;
+    uint32_t                                        _mainExecutableIndex;
+    std::vector<const BinaryImageGroupData*>        _knownGroups;
+    std::vector<ImageProxy*>                        _images;
+    std::unordered_map<std::string, ImageProxy*>    _pathToProxy;
+    std::vector<DyldSharedCache::MappedMachO>       _ownedMappings;
+    std::vector<std::string>                        _buildTimePrefixes;
+    std::vector<DyldCacheOverride>                  _cacheOverrides;
+    std::string                                     _mainProgRuntimePath;
+    std::string                                     _archName;
+    Platform                                        _platform;
+    std::set<std::string>                           _mustBeMissingFiles;
+};
+
+
+
+
+
+}
+
+#endif // ImageProxy_h
diff --git a/dyld3/shared-cache/MachOFileAbstraction.hpp b/dyld3/shared-cache/MachOFileAbstraction.hpp
new file mode 100644 (file)
index 0000000..a15100f
--- /dev/null
@@ -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 <mach-o/loader.h>
+#include <mach-o/nlist.h>
+#include <mach-o/reloc.h>
+#include <mach/machine.h>
+
+// 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 <typename P>
+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 <typename P> struct macho_segment_content {};
+template <> struct macho_segment_content<Pointer32<BigEndian> >    { segment_command   fields; enum { CMD = LC_SEGMENT         }; };
+template <> struct macho_segment_content<Pointer64<BigEndian> >           { segment_command_64 fields; enum { CMD = LC_SEGMENT_64      }; };
+template <> struct macho_segment_content<Pointer32<LittleEndian> > { segment_command   fields; enum { CMD = LC_SEGMENT         }; };
+template <> struct macho_segment_content<Pointer64<LittleEndian> > { segment_command_64        fields; enum { CMD = LC_SEGMENT_64      }; };
+
+template <typename P>
+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<P>::CMD
+       };
+
+       typedef typename P::E           E;
+private:
+       macho_segment_content<P>        segment;
+};
+
+
+//
+// mach-o section 
+//
+template <typename P> struct macho_section_content {};
+template <> struct macho_section_content<Pointer32<BigEndian> >    { section   fields; };
+template <> struct macho_section_content<Pointer64<BigEndian> >           { section_64 fields; };
+template <> struct macho_section_content<Pointer32<LittleEndian> > { section   fields; };
+template <> struct macho_section_content<Pointer64<LittleEndian> > { section_64        fields; };
+
+template <typename P>
+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<P>        section;
+};
+
+
+//
+// mach-o dylib load command
+//
+template <typename P>
+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 <typename P>
+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 <typename P>
+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 <typename P>
+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 <typename P>
+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 <typename P>
+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 <typename P>
+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 <typename P> struct macho_routines_content {};
+template <> struct macho_routines_content<Pointer32<BigEndian> >    { routines_command         fields; enum { CMD = LC_ROUTINES        }; };
+template <> struct macho_routines_content<Pointer64<BigEndian> >       { routines_command_64   fields; enum { CMD = LC_ROUTINES_64     }; };
+template <> struct macho_routines_content<Pointer32<LittleEndian> > { routines_command         fields; enum { CMD = LC_ROUTINES        }; };
+template <> struct macho_routines_content<Pointer64<LittleEndian> > { routines_command_64      fields; enum { CMD = LC_ROUTINES_64     }; };
+
+template <typename P>
+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<P>::CMD
+       };
+private:
+       macho_routines_content<P>       routines;
+};
+
+
+//
+// mach-o symbol table load command
+//
+template <typename P>
+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 <typename P>
+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 <typename P>
+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 <typename P>
+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 <typename P>
+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 <typename P> struct macho_nlist_content {};
+template <> struct macho_nlist_content<Pointer32<BigEndian> >    { struct nlist                fields; };
+template <> struct macho_nlist_content<Pointer64<BigEndian> >   { struct nlist_64      fields; };
+template <> struct macho_nlist_content<Pointer32<LittleEndian> > { struct nlist                fields; };
+template <> struct macho_nlist_content<Pointer64<LittleEndian> > { struct nlist_64     fields; };
+
+template <typename P>
+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<P>  entry;
+};
+
+
+
+//
+// mach-o relocation info
+//
+template <typename P>
+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 <typename P>
+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 <typename P> struct macho_header_content {};
+template <> struct macho_header_content<Pointer32<BigEndian> >    { mach_header                fields; };
+template <> struct macho_header_content<Pointer64<BigEndian> >   { mach_header_64      fields; };
+template <> struct macho_header_content<Pointer32<LittleEndian> > { mach_header                fields; };
+template <> struct macho_header_content<Pointer64<LittleEndian> > { mach_header_64     fields; };
+
+template <typename P>
+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<P>* getSegment(const char *segname) const
+    {
+        const macho_load_command<P>* cmds = (macho_load_command<P>*)((uint8_t*)this + sizeof(macho_header<P>));
+        uint32_t cmd_count = this->ncmds();
+        const macho_load_command<P>* cmd = cmds;
+        for (uint32_t i = 0; i < cmd_count; ++i) {
+            if ( cmd->cmd() == macho_segment_command<P>::CMD ) {
+                const macho_segment_command<P>* segcmd = (macho_segment_command<P>*)cmd;
+                if (0 == strncmp(segname, segcmd->segname(), 16)) {
+                    return segcmd;
+              }
+            }
+            cmd = (macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
+        }
+        return NULL;
+    }
+
+    const macho_section<P>* getSection(const char *segname, const char *sectname) const
+    {
+       const  macho_segment_command<P>* segcmd = getSegment(segname);
+        if (!segcmd) return NULL;
+
+        const macho_section<P>* sectcmd = (macho_section<P>*)(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<P>* getLoadCommand(int query) const
+    {
+        const macho_load_command<P>* cmds = (macho_load_command<P>*)((uint8_t*)this + sizeof(macho_header<P>));
+        uint32_t cmd_count = this->ncmds();
+        const macho_load_command<P>* cmd = cmds;
+        for (uint32_t i = 0; i < cmd_count; ++i) {
+            if ( cmd->cmd() == query ) {
+                return cmd;
+            }
+            cmd = (macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
+        }
+        return NULL;
+    }
+
+       typedef typename P::E           E;
+private:
+       macho_header_content<P> header;
+};
+
+
+
+//
+// compressed dyld info load command
+//
+template <typename P>
+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 |= (((int64_t)(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/dyld3/shared-cache/Manifest.h b/dyld3/shared-cache/Manifest.h
new file mode 100644 (file)
index 0000000..3348cfa
--- /dev/null
@@ -0,0 +1,229 @@
+/*
+ * Copyright (c) 2017 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 Manifest_h
+#define Manifest_h
+
+#include <map>
+#include <set>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <unordered_map>
+#include <unordered_set>
+
+#include <assert.h>
+#include <uuid/uuid.h>
+
+#import <Foundation/Foundation.h>
+
+#include "MachOParser.h"
+#include "DyldSharedCache.h"
+#include "Diagnostics.h"
+
+extern std::string toolDir();
+
+namespace dyld3 {
+
+struct BuildQueueEntry {
+    DyldSharedCache::CreateOptions            options;
+    std::vector<DyldSharedCache::MappedMachO> dylibsForCache;
+    std::vector<DyldSharedCache::MappedMachO> otherDylibsAndBundles;
+    std::vector<DyldSharedCache::MappedMachO> mainExecutables;
+    std::string                               outputPath;
+    std::set<std::string>                     configNames;
+};
+
+struct Manifest {
+    struct UUIDInfo {
+        const mach_header* mh;
+        uint64_t sliceFileOffset;
+        std::size_t size;
+        std::string runtimePath;
+        std::string buildPath;
+        std::string installName;
+        std::string arch;
+        UUID uuid;
+        UUIDInfo(const mach_header* M, std::size_t S, uint64_t SO, UUID U, std::string A, std::string RP, std::string BP, std::string IN)
+            : mh(M), size(S), arch(A), uuid(U), runtimePath(RP), buildPath(BP), installName(IN), sliceFileOffset(SO) {}
+        UUIDInfo() : UUIDInfo(nullptr, 0, 0, UUID(), "", "", "", "") {}
+    };
+
+    struct Project {
+        std::vector<std::string> sources;
+    };
+
+    struct File {
+        MachOParser* parser;
+        File(MachOParser* P)
+            : parser(P)
+        {
+        }
+    };
+
+    struct SegmentInfo {
+        std::string name;
+        uint64_t    startAddr;
+        uint64_t    endAddr;
+    };
+
+    struct CacheInfo {
+        std::vector<SegmentInfo> regions;
+        std::string              cdHash;
+    };
+
+    struct CacheImageInfo {
+        bool                     included;
+        std::string              exclusionInfo;
+        UUID                     uuid;
+        std::string              installname;
+        std::vector<SegmentInfo> segments;
+        CacheImageInfo(void)
+            : included(true)
+        {
+        }
+    };
+
+    struct Results {
+        std::string failure;
+        std::map<UUID, CacheImageInfo> dylibs;
+        std::map<UUID, CacheImageInfo> bundles;
+        std::map<UUID, CacheImageInfo> executables;
+
+        std::set<std::string> warnings;
+        CacheInfo             developmentCache;
+        CacheInfo             productionCache;
+        CacheImageInfo& dylibForInstallname(const std::string& installname);
+        void exclude(MachOParser* parser, const std::string& reason);
+        void exclude(Manifest& manifest, const UUID& uuid, const std::string& reason);
+    };
+
+    struct Architecture {
+        mutable Results     results;
+
+        bool operator==(const Architecture& O) const;
+        bool operator!=(const Architecture& other) const;
+    };
+
+    struct Configuration {
+        std::string                         platformName;
+        std::string                         device;
+        std::string                         disposition;
+        std::string                         metabomTag;
+        std::set<std::string>               metabomTags;
+        std::set<std::string>               metabomExcludeTags;
+        std::set<std::string>               metabomRestrictTags;
+        std::set<std::string>               restrictedInstallnames;
+        std::map<std::string, Architecture> architectures;
+
+        bool operator==(const Configuration& O) const;
+        bool operator!=(const Configuration& other) const;
+        const Architecture& architecture(const std::string& architecture) const;
+        void forEachArchitecture(std::function<void(const std::string& archName)> lambda) const;
+    };
+
+    const std::map<std::string, Project>& projects();
+    const Configuration& configuration(const std::string& configuration) const;
+    void forEachConfiguration(std::function<void(const std::string& configName)> lambda) const;
+
+    void addProjectSource(const std::string& project, const std::string& source, bool first = false);
+
+    const std::string projectPath(const std::string& projectName);
+    const bool        empty(void);
+    const std::string dylibOrderFile() const;
+    void setDylibOrderFile(const std::string& dylibOrderFile);
+
+    const std::string dirtyDataOrderFile() const;
+    void setDirtyDataOrderFile(const std::string& dirtyDataOrderFile);
+
+    const std::string metabomFile() const;
+    void setMetabomFile(const std::string& metabomFile);
+
+    const Platform platform() const;
+    void setPlatform(const Platform platform);
+
+    const std::string& build() const;
+    void setBuild(const std::string& build);
+    const uint32_t version() const;
+    void setVersion(const uint32_t manifestVersion);
+    bool normalized;
+
+    Manifest(Diagnostics& D, const std::string& path);
+    Manifest(Diagnostics& D, const std::string& path, const std::set<std::string>& overlays);
+
+    BuildQueueEntry makeQueueEntry(const std::string& outputPath, const std::set<std::string>& configs, const std::string& arch, bool optimizeStubs, const std::string& prefix, bool verbose);
+
+    void write(const std::string& path);
+    void writeJSON(const std::string& path);
+    void        canonicalize(void);
+    void        calculateClosure();
+    MachOParser parserForUUID(const UUID& uuid) const;
+    const std::string buildPathForUUID(const UUID& uuid);
+    const std::string runtimePathForUUID(const UUID& uuid);
+    DyldSharedCache::MappedMachO machoForPathAndArch(const std::string& path, const std::string& arch) const;
+    void remove(const std::string& config, const std::string& arch);
+    const std::string removeLargestLeafDylib(const std::set<std::string>& configurations, const std::string& architecture);
+    void runConcurrently(dispatch_queue_t queue, dispatch_semaphore_t concurrencyLimitingSemaphore, std::function<void(const std::string configuration, const std::string architecture)> lambda);
+    bool filterForConfig(const std::string& configName);
+
+private:
+    NSDictionary*    _manifestDict;
+    Diagnostics&      _diags;
+    std::map<UUID, UUIDInfo> _uuidMap;
+    std::map<std::pair<std::string, std::string>, UUID> _installNameMap;
+    static dispatch_queue_t _identifierQueue;
+    uint32_t                _manifestVersion;
+    std::string             _build;
+    std::string             _dylibOrderFile;
+    std::string             _dirtyDataOrderFile;
+    std::string             _metabomFile;
+    Platform                _platform;
+    std::map<std::string, Project>               _projects;
+    std::map<std::string, Configuration>         _configurations;
+    std::map<std::string, std::set<std::string>> _metabomTagMap;
+    std::map<std::string, std::set<std::string>> _metabomExcludeTagMap;
+    std::map<std::string, std::set<std::string>> _metabomRestrictedTagMap;
+
+    std::vector<DyldSharedCache::MappedMachO> dylibsForCache(const std::string& configuration, const std::string& architecture);
+    std::vector<DyldSharedCache::MappedMachO> otherDylibsAndBundles(const std::string& configuration, const std::string& architecture);
+    std::vector<DyldSharedCache::MappedMachO> mainExecutables(const std::string& configuration, const std::string& architecture);
+
+    const UUIDInfo& infoForUUID(const UUID& uuid) const;
+    const UUIDInfo infoForInstallNameAndarch(const std::string& installName, const std::string arch) const;
+    void insert(std::vector<DyldSharedCache::MappedMachO>& mappedMachOs, const CacheImageInfo& imageInfo);
+    bool loadParser(const void* p, size_t size, uint64_t sliceOffset, const std::string& runtimePath, const std::string& buildPath, const std::set<std::string>& architectures);
+    bool loadParsers(const std::string& pathToMachO, const std::string& runtimePath, const std::set<std::string>& architectures);
+    void removeDylib(MachOParser parser, const std::string& reason, const std::string& configuration, const std::string& architecture,
+        std::unordered_set<UUID>& processedIdentifiers);
+    void dedupeDispositions();
+    void calculateClosure(const std::string& configuration, const std::string& architecture);
+    void canonicalizeDylib(const std::string& installname);
+    template <typename P>
+    void canonicalizeDylib(const std::string& installname, const uint8_t* p);
+    void addImplicitAliases(void);
+};
+}
+
+#endif /* Manifest_h */
diff --git a/dyld3/shared-cache/Manifest.mm b/dyld3/shared-cache/Manifest.mm
new file mode 100644 (file)
index 0000000..3dd9f33
--- /dev/null
@@ -0,0 +1,1134 @@
+/*
+ * Copyright (c) 2017 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 "C" {
+#include <Bom/Bom.h>
+#include <Metabom/MBTypes.h>
+#include <Metabom/MBEntry.h>
+#include <Metabom/MBMetabom.h>
+#include <Metabom/MBIterator.h>
+};
+
+#include <algorithm>
+#include <CommonCrypto/CommonDigest.h>
+#include <CommonCrypto/CommonDigestSPI.h>
+#include <Foundation/Foundation.h>
+
+#include "MachOFileAbstraction.hpp"
+#include "FileAbstraction.hpp"
+#include "Trie.hpp"
+#include "FileUtils.h"
+#include "StringUtils.h"
+
+#include <mach-o/loader.h>
+#include <mach-o/fat.h>
+
+#include <array>
+#include <vector>
+
+#include "Manifest.h"
+
+namespace {
+//FIXME this should be in a class
+static inline NSString* cppToObjStr(const std::string& str) { return [NSString stringWithUTF8String:str.c_str()]; }
+
+template <class Set1, class Set2>
+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;
+}
+
+//hACK: If we declare this in manifest
+static NSDictionary* gManifestDict;
+
+} /* Anonymous namespace */
+
+namespace dyld3 {
+void Manifest::Results::exclude(MachOParser* parser, const std::string& reason)
+{
+    auto dylibUUID = parser->uuid();
+    dylibs[dylibUUID].uuid = dylibUUID;
+    dylibs[dylibUUID].installname = parser->installName();
+    dylibs[dylibUUID].included = false;
+    dylibs[dylibUUID].exclusionInfo = reason;
+}
+
+void Manifest::Results::exclude(Manifest& manifest, const UUID& uuid, const std::string& reason)
+{
+    auto parser = manifest.parserForUUID(uuid);
+    dylibs[uuid].uuid = uuid;
+    dylibs[uuid].installname = parser.installName();
+    dylibs[uuid].included = false;
+    dylibs[uuid].exclusionInfo = reason;
+}
+
+Manifest::CacheImageInfo& Manifest::Results::dylibForInstallname(const std::string& installname)
+{
+    auto i = find_if(dylibs.begin(), dylibs.end(), [&installname](std::pair<UUID, CacheImageInfo> d) { return d.second.installname == installname; });
+    assert(i != dylibs.end());
+    return i->second;
+}
+
+bool Manifest::Architecture::operator==(const Architecture& O) const
+{
+    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
+                || Odylib->second.uuid != dylib.second.uuid)
+                return false;
+        }
+    }
+
+    for (const 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
+                || dylib->second.uuid != Odylib.second.uuid)
+                return false;
+        }
+    }
+
+    for (auto& bundle : results.bundles) {
+        if (bundle.second.included) {
+            auto Obundle = O.results.bundles.find(bundle.first);
+            if (Obundle == O.results.bundles.end()
+                || Obundle->second.included == false
+                || Obundle->second.uuid != bundle.second.uuid)
+                return false;
+        }
+    }
+
+    for (const auto& Obundle : O.results.bundles) {
+        if (Obundle.second.included) {
+            auto bundle = results.bundles.find(Obundle.first);
+            if (bundle == results.bundles.end()
+                || bundle->second.included == false
+                || bundle->second.uuid != Obundle.second.uuid)
+                return false;
+        }
+    }
+
+    for (auto& executable : results.executables) {
+        if (executable.second.included) {
+            auto Oexecutable = O.results.executables.find(executable.first);
+            if (Oexecutable == O.results.executables.end()
+                || Oexecutable->second.included == false
+                || Oexecutable->second.uuid != executable.second.uuid)
+                return false;
+        }
+    }
+
+    for (const auto& Oexecutable : O.results.executables) {
+        if (Oexecutable.second.included) {
+            auto executable = results.executables.find(Oexecutable.first);
+            if (executable == results.executables.end()
+                || executable->second.included == false
+                || executable->second.uuid != Oexecutable.second.uuid)
+                return false;
+        }
+    }
+
+    return true;
+}
+
+bool Manifest::Configuration::operator==(const Configuration& O) const
+{
+    return architectures == O.architectures;
+}
+
+bool Manifest::Configuration::operator!=(const Configuration& other) const { return !(*this == other); }
+
+const Manifest::Architecture& Manifest::Configuration::architecture(const std::string& architecture) const
+{
+    assert(architectures.find(architecture) != architectures.end());
+    return architectures.find(architecture)->second;
+}
+
+void Manifest::Configuration::forEachArchitecture(std::function<void(const std::string& archName)> lambda) const
+{
+    for (const auto& architecutre : architectures) {
+        lambda(architecutre.first);
+    }
+}
+
+bool Manifest::Architecture::operator!=(const Architecture& other) const { return !(*this == other); }
+
+const std::map<std::string, Manifest::Project>& Manifest::projects()
+{
+    return _projects;
+}
+
+const Manifest::Configuration& Manifest::configuration(const std::string& configuration) const
+{
+    assert(_configurations.find(configuration) != _configurations.end());
+    return _configurations.find(configuration)->second;
+}
+
+void Manifest::forEachConfiguration(std::function<void(const std::string& configName)> lambda) const
+{
+    for (const auto& configuration : _configurations) {
+        lambda(configuration.first);
+    }
+}
+
+void Manifest::addProjectSource(const std::string& project, const std::string& source, bool first)
+{
+    auto& sources = _projects[project].sources;
+    if (std::find(sources.begin(), sources.end(), source) == sources.end()) {
+        if (first) {
+            sources.insert(sources.begin(), source);
+        } else {
+            sources.push_back(source);
+        }
+    }
+}
+
+const std::string Manifest::projectPath(const std::string& projectName)
+{
+    auto project = _projects.find(projectName);
+    if (project == _projects.end())
+        return "";
+    if (project->second.sources.size() == 0)
+        return "";
+    return project->second.sources[0];
+}
+
+const bool Manifest::empty(void)
+{
+    for (const auto& configuration : _configurations) {
+        if (configuration.second.architectures.size() != 0)
+            return false;
+    }
+    return true;
+}
+
+const std::string Manifest::dylibOrderFile() const { return _dylibOrderFile; };
+void Manifest::setDylibOrderFile(const std::string& dylibOrderFile) { _dylibOrderFile = dylibOrderFile; };
+
+const std::string Manifest::dirtyDataOrderFile() const { return _dirtyDataOrderFile; };
+void Manifest::setDirtyDataOrderFile(const std::string& dirtyDataOrderFile) { _dirtyDataOrderFile = dirtyDataOrderFile; };
+
+const std::string Manifest::metabomFile() const { return _metabomFile; };
+void Manifest::setMetabomFile(const std::string& metabomFile) { _metabomFile = metabomFile; };
+
+const Platform Manifest::platform() const { return _platform; };
+void Manifest::setPlatform(const Platform platform) { _platform = platform; };
+
+const std::string& Manifest::build() const { return _build; };
+void Manifest::setBuild(const std::string& build) { _build = build; };
+const uint32_t                             Manifest::version() const { return _manifestVersion; };
+void Manifest::setVersion(const uint32_t manifestVersion) { _manifestVersion = manifestVersion; };
+
+BuildQueueEntry Manifest::makeQueueEntry(const std::string& outputPath, const std::set<std::string>& configs, const std::string& arch, bool optimizeStubs, const std::string& prefix, bool verbose)
+{
+    dyld3::BuildQueueEntry retval;
+
+    DyldSharedCache::CreateOptions options;
+    options.archName = arch;
+    options.platform = platform();
+    options.excludeLocalSymbols = true;
+    options.optimizeStubs = optimizeStubs;
+    options.optimizeObjC = true;
+    options.codeSigningDigestMode = (platform() == dyld3::Platform::watchOS) ?
+                                    DyldSharedCache::Agile : DyldSharedCache::SHA256only;
+    options.dylibsRemovedDuringMastering = true;
+    options.inodesAreSameAsRuntime = false;
+    options.cacheSupportsASLR = true;
+    options.forSimulator = false;
+    options.verbose = verbose;
+    options.evictLeafDylibsOnOverflow = false;
+    options.loggingPrefix = prefix;
+    options.pathPrefixes = { "" };
+    options.dylibOrdering = loadOrderFile(_dylibOrderFile);
+    options.dirtyDataSegmentOrdering = loadOrderFile(_dirtyDataOrderFile);
+
+    dyld3::BuildQueueEntry queueEntry;
+    retval.configNames = configs;
+    retval.options = options;
+    retval.outputPath = outputPath;
+    retval.dylibsForCache = dylibsForCache(*configs.begin(), arch);
+    retval.otherDylibsAndBundles = otherDylibsAndBundles(*configs.begin(), arch);
+    retval.mainExecutables = mainExecutables(*configs.begin(), arch);
+
+    return retval;
+}
+
+bool Manifest::loadParser(const void* p, size_t size, uint64_t sliceOffset, const std::string& runtimePath, const std::string& buildPath, const std::set<std::string>& architectures)
+{
+    const mach_header* mh = reinterpret_cast<const mach_header*>(p);
+    if (!MachOParser::isValidMachO(_diags, "", _platform, p, size, runtimePath.c_str(), false)) {
+        return false;
+    }
+
+    auto parser = MachOParser(mh);
+    if (_diags.hasError()) {
+        // Clear the error and punt
+        _diags.verbose("MachoParser error: %s\n", _diags.errorMessage().c_str());
+        _diags.clearError();
+        return false;
+    }
+
+    auto uuid = parser.uuid();
+    auto archName = parser.archName();
+
+    if (parser.fileType() == MH_DYLIB && architectures.count(parser.archName()) != 0) {
+        std::string installName = parser.installName();
+        auto index = std::make_pair(installName, parser.archName());
+        auto i = _installNameMap.find(index);
+
+        if ( installName == "/System/Library/Caches/com.apple.xpc/sdk.dylib"
+            || installName == "/System/Library/Caches/com.apple.xpcd/xpcd_cache.dylib" ) {
+            // HACK to deal with device specific dylibs. These must not be inseted into the installNameMap
+            _uuidMap.insert(std::make_pair(uuid, UUIDInfo(mh, size, sliceOffset, uuid, parser.archName(), runtimePath, buildPath, installName)));
+        } else if (i == _installNameMap.end()) {
+            _installNameMap.insert(std::make_pair(index, uuid));
+            _uuidMap.insert(std::make_pair(uuid, UUIDInfo(mh, size, sliceOffset, uuid, parser.archName(), runtimePath, buildPath, installName)));
+            if (installName[0] != '@' && installName != runtimePath) {
+                _diags.warning("Dylib located at '%s' has  installname '%s'", runtimePath.c_str(), installName.c_str());
+            }
+        } else {
+            auto info = infoForUUID(i->second);
+            _diags.warning("Multiple dylibs claim installname '%s' ('%s' and '%s')", installName.c_str(), runtimePath.c_str(), info.runtimePath.c_str());
+
+            // This is the "Good" one, overwrite
+            if (runtimePath == installName) {
+                _uuidMap.erase(uuid);
+                _uuidMap.insert(std::make_pair(uuid, UUIDInfo(mh, size, sliceOffset, uuid, parser.archName(), runtimePath, buildPath, installName)));
+            }
+        }
+    } else {
+        _uuidMap.insert(std::make_pair(uuid, UUIDInfo(mh, size, sliceOffset, uuid, parser.archName(), runtimePath, buildPath, "")));
+    }
+    return true;
+}
+
+//FIXME: assert we have not errored first
+bool Manifest::loadParsers(const std::string& buildPath, const std::string& runtimePath, const std::set<std::string>& architectures)
+{
+    __block bool retval = false;
+    const void*  p = (uint8_t*)(-1);
+    struct stat  stat_buf;
+
+    std::tie(p, stat_buf) = fileCache.cacheLoad(_diags, buildPath);
+
+    if (p == (uint8_t*)(-1)) {
+        return false;
+    }
+
+    if (FatUtil::isFatFile(p)) {
+        FatUtil::forEachSlice(_diags, p, stat_buf.st_size, ^(uint32_t sliceCpuType, uint32_t sliceCpuSubType, const void* sliceStart, size_t sliceSize, bool& stop) {
+            if (loadParser(sliceStart, sliceSize, (uintptr_t)sliceStart-(uintptr_t)p, runtimePath, buildPath, architectures))
+                retval = true;
+        });
+    } else {
+        return loadParser(p, stat_buf.st_size, 0, runtimePath, buildPath, architectures);
+    }
+    return retval;
+}
+
+const Manifest::UUIDInfo& Manifest::infoForUUID(const UUID& uuid) const {
+    auto i = _uuidMap.find(uuid);
+    assert(i != _uuidMap.end());
+    return i->second;
+}
+
+const Manifest::UUIDInfo Manifest::infoForInstallNameAndarch(const std::string& installName, const std::string arch) const  {
+    UUIDInfo retval;
+    auto uuidI = _installNameMap.find(std::make_pair(installName, arch));
+    if (uuidI == _installNameMap.end())
+        return UUIDInfo();
+
+    auto i = _uuidMap.find(uuidI->second);
+    if (i == _uuidMap.end())
+    return UUIDInfo();
+    return i->second;
+}
+
+MachOParser Manifest::parserForUUID(const UUID& uuid) const {
+    return MachOParser(infoForUUID(uuid).mh);
+}
+
+const std::string Manifest::buildPathForUUID(const UUID& uuid) {
+    return infoForUUID(uuid).buildPath;
+}
+
+const std::string Manifest::runtimePathForUUID(const UUID& uuid) {
+    return infoForUUID(uuid).runtimePath;
+}
+    
+Manifest::Manifest(Diagnostics& D, const std::string& path)  : Manifest(D, path, std::set<std::string>())
+{
+}
+
+Manifest::Manifest(Diagnostics& D, const std::string& path, const std::set<std::string>& overlays) :
+    _diags(D)
+{
+    NSMutableDictionary* manifestDict = [NSMutableDictionary dictionaryWithContentsOfFile:cppToObjStr(path)];
+    NSString*            platStr = manifestDict[@"platform"];
+    std::set<std::string> architectures;
+
+    if (platStr == nullptr)
+        platStr = @"ios";
+    std::string platformString = [platStr UTF8String];
+    setMetabomFile([manifestDict[@"metabomFile"] UTF8String]);
+
+    if (platformString == "ios") {
+        setPlatform(dyld3::Platform::iOS);
+    } else if ( (platformString == "tvos") || (platformString == "atv") ) {
+        setPlatform(dyld3::Platform::tvOS);
+    } else if ( (platformString == "watchos") || (platformString == "watch") ) {
+        setPlatform(dyld3::Platform::watchOS);
+    } else if ( (platformString == "bridgeos") || (platformString == "bridge") ) {
+        setPlatform(dyld3::Platform::bridgeOS);
+    } else if ( (platformString == "macos") || (platformString == "osx") ) {
+        setPlatform(dyld3::Platform::macOS);
+    } else {
+        //Fixme should we error?
+        setPlatform(dyld3::Platform::iOS);
+    }
+
+    for (NSString* project in manifestDict[@"projects"]) {
+        for (NSString* source in manifestDict[@"projects"][project]) {
+            addProjectSource([project UTF8String], [source UTF8String]);
+        }
+    }
+
+    for (NSString* configuration in manifestDict[@"configurations"]) {
+        std::string configStr = [configuration UTF8String];
+        std::string configTag = [manifestDict[@"configurations"][configuration][@"metabomTag"] UTF8String];
+
+        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].metabomTags.insert(configTag);
+        _configurations[configStr].platformName =
+            [manifestDict[@"configurations"][configuration][@"platformName"] UTF8String];
+
+        if (endsWith(configStr, "InternalOS")) {
+            _configurations[configStr].disposition = "internal";
+            _configurations[configStr].device = configStr.substr(0, configStr.length()-strlen("InternalOS"));
+        } else if (endsWith(configStr, "VendorOS")) {
+            _configurations[configStr].disposition = "internal";
+            _configurations[configStr].device = configStr.substr(0, configStr.length()-strlen("VendorOS"));
+        } else if (endsWith(configStr, "VendorUIOS")) {
+            _configurations[configStr].disposition = "internal";
+            _configurations[configStr].device = configStr.substr(0, configStr.length()-strlen("VendorUIOS"));
+        } else if (endsWith(configStr, "CarrierOS")) {
+            _configurations[configStr].disposition = "internal";
+            _configurations[configStr].device = configStr.substr(0, configStr.length()-strlen("CarrierOS"));
+        } else if (endsWith(configStr, "FactoryOS")) {
+            _configurations[configStr].disposition = "internal";
+            _configurations[configStr].device = configStr.substr(0, configStr.length()-strlen("FactoryOS"));
+        } else if (endsWith(configStr, "DesenseOS")) {
+            _configurations[configStr].disposition = "internal";
+            _configurations[configStr].device = configStr.substr(0, configStr.length()-strlen("DesenseOS"));
+        } else if (endsWith(configStr, "MinosOS")) {
+            _configurations[configStr].disposition = "minos";
+            _configurations[configStr].device = configStr.substr(0, configStr.length()-strlen("MinosOS"));
+        } else if (endsWith(configStr, "DemoOS")) {
+            _configurations[configStr].disposition = "demo";
+            _configurations[configStr].device = configStr.substr(0, configStr.length()-strlen("DemoOS"));
+        } else if (endsWith(configStr, "MinosOS")) {
+            _configurations[configStr].disposition = "minos";
+            _configurations[configStr].device = configStr.substr(0, configStr.length()-strlen("MinosOS"));
+        } else if (endsWith(configStr, "DeveloperOS")) {
+            _configurations[configStr].disposition = "user";
+            _configurations[configStr].device = configStr.substr(0, configStr.length()-strlen("DeveloperOS"));
+        } else if (endsWith(configStr, "OS")) {
+            _configurations[configStr].disposition = "user";
+            _configurations[configStr].device = configStr.substr(0, configStr.length()-strlen("OS"));
+        }
+
+        for (NSString* architecutre in manifestDict[@"configurations"][configuration][@"architectures"]) {
+            //HACK until B&I stops mastering armv7s
+            if ([architecutre isEqual:@"armv7s"]) break;
+            _configurations[configStr].architectures[[architecutre UTF8String]] = Architecture();
+            architectures.insert([architecutre UTF8String]);
+        }
+    }
+
+    setVersion([manifestDict[@"manifest-version"] unsignedIntValue]);
+    setBuild([manifestDict[@"build"] UTF8String]);
+    if (manifestDict[@"dylibOrderFile"]) {
+        setDylibOrderFile([manifestDict[@"dylibOrderFile"] UTF8String]);
+    }
+    if (manifestDict[@"dirtyDataOrderFile"]) {
+        setDirtyDataOrderFile([manifestDict[@"dirtyDataOrderFile"] UTF8String]);
+    }
+
+    auto    metabom = MBMetabomOpen(metabomFile().c_str(), false);
+    auto    metabomEnumerator = MBIteratorNewWithPath(metabom, ".", "");
+    MBEntry entry;
+
+    // FIXME error handling (NULL metabom)
+
+    //First we iterate through the bom and build our objects
+
+    while ((entry = MBIteratorNext(metabomEnumerator))) {
+        BOMFSObject  fsObject = MBEntryGetFSObject(entry);
+        BOMFSObjType entryType = BOMFSObjectType(fsObject);
+        std::string  entryPath = BOMFSObjectPathName(fsObject);
+        if (entryPath[0] == '.') {
+            entryPath.erase(0, 1);
+        }
+
+        // Skip artifacts that happen to be in the build chain
+        if ( startsWith(entryPath, "/Applications/Xcode.app") ) {
+            continue;
+        }
+
+        // Skip variants we can't deal with
+        if ( endsWith(entryPath, "_profile.dylib") || endsWith(entryPath, "_debug.dylib") || endsWith(entryPath, "_profile") || endsWith(entryPath, "_debug") || endsWith(entryPath, "/CoreADI") ) {
+            continue;
+        }
+
+        // Skip images that are only used in InternalOS variants
+        if ( startsWith(entryPath, "/AppleInternal/") || startsWith(entryPath, "/usr/local/") || startsWith(entryPath, "/Developer/")) {
+            continue;
+        }
+        
+        // Skip genCache generated dylibs
+        if ( endsWith(entryPath, "/System/Library/Caches/com.apple.xpc/sdk.dylib") || endsWith(entryPath, "/System/Library/Caches/com.apple.xpcd/xpcd_cache.dylib")) {
+            continue;
+        }
+
+        MBTag tag;
+        auto  tagCount = MBEntryGetNumberOfProjectTags(entry);
+        if (entryType == BOMFileType && BOMFSObjectIsBinaryObject(fsObject) && MBEntryGetNumberOfProjectTags(entry) != 0 && tagCount != 0) {
+            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<std::string> tagStrs;
+                std::map<std::string, MBTag> 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 + "'";
+                    }
+                    _diags.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);
+            tagCount = MBEntryGetNumberOfPackageTags(entry);
+            MBTag* tags = (MBTag*)malloc(sizeof(MBTag) * tagCount);
+            MBEntryGetPackageTags(entry, tags);
+            std::set<std::string> tagStrs;
+
+            for (auto i = 0; i < tagCount; ++i) {
+                tagStrs.insert(MBMetabomGetPackageForTag(metabom, tags[i]));
+            }
+
+            _metabomTagMap.insert(std::make_pair(entryPath, tagStrs));
+            bool foundParser = false;
+            for (const auto& overlay : overlays) {
+                if (loadParsers(overlay + "/" + entryPath, entryPath, architectures)) {
+                    foundParser = true;
+                    break;
+                }
+            }
+
+            if (!foundParser) {
+                (void)loadParsers(projectPath(projectName) + "/" + entryPath, entryPath, architectures);
+            }
+        }
+    }
+
+    MBIteratorFree(metabomEnumerator);
+    MBMetabomFree(metabom);
+}
+
+void Manifest::insert(std::vector<DyldSharedCache::MappedMachO>& mappedMachOs, const CacheImageInfo& imageInfo) {
+    auto info = infoForUUID(imageInfo.uuid);
+    auto runtimePath = info.runtimePath;
+    mappedMachOs.emplace_back(runtimePath, info.mh, info.size, false, false, info.sliceFileOffset, 0, 0);
+}
+
+std::vector<DyldSharedCache::MappedMachO> Manifest::dylibsForCache(const std::string& configuration, const std::string& architecture)
+{
+    std::vector<DyldSharedCache::MappedMachO> retval;
+    const auto&                               dylibs = _configurations[configuration].architectures[architecture].results.dylibs;
+    for (const auto& dylib : dylibs) {
+        if (dylib.second.included) {
+            insert(retval, dylib.second);
+        }
+    }
+    return retval;
+}
+
+std::vector<DyldSharedCache::MappedMachO> Manifest::otherDylibsAndBundles(const std::string& configuration, const std::string& architecture)
+{
+    std::vector<DyldSharedCache::MappedMachO> retval;
+    const auto&                               dylibs = _configurations[configuration].architectures[architecture].results.dylibs;
+    for (const auto& dylib : dylibs) {
+        if (!dylib.second.included) {
+            insert(retval, dylib.second);
+        }
+    }
+
+    const auto& bundles = _configurations[configuration].architectures[architecture].results.bundles;
+    for (const auto& bundle : bundles) {
+        insert(retval, bundle.second);
+    }
+
+    return retval;
+}
+
+std::vector<DyldSharedCache::MappedMachO> Manifest::mainExecutables(const std::string& configuration, const std::string& architecture)
+{
+    std::vector<DyldSharedCache::MappedMachO> retval;
+    const auto&                               executables = _configurations[configuration].architectures[architecture].results.executables;
+    for (const auto& executable : executables) {
+        insert(retval, executable.second);
+    }
+
+    return retval;
+}
+
+bool Manifest::filterForConfig(const std::string& configName)
+{
+    for (const auto configuration : _configurations) {
+        if (configName == configuration.first) {
+            std::map<std::string, Configuration> filteredConfigs;
+            filteredConfigs[configName] = configuration.second;
+
+            _configurations = filteredConfigs;
+
+            for (auto& arch : configuration.second.architectures) {
+                arch.second.results = Manifest::Results();
+            }
+            return true;
+        }
+    }
+    return false;
+}
+
+void Manifest::dedupeDispositions(void) {
+    // Since this is all hacky and inference based for now only do it for iOS until XBS
+    // is reved to give us real info. All the other platforms are way smaller anyway.
+    if (_platform != Platform::iOS)
+        return;
+
+    std::map<std::pair<std::string, std::string>, std::set<std::string>> dispositionSets;
+
+    for (const auto& configuration : _configurations) {
+        dispositionSets[std::make_pair(configuration.second.device, configuration.second.disposition)].insert(configuration.first);
+    }
+
+    for (const auto& dSet : dispositionSets) {
+        for (const auto &c1 : dSet.second) {
+            for (const auto &c2 : dSet.second) {
+                _configurations[c1].metabomTags.insert(_configurations[c2].metabomTag);
+            }
+        }
+    }
+}
+
+void Manifest::calculateClosure()
+{
+    auto closureSemaphore = dispatch_semaphore_create(32);
+    auto closureGroup = dispatch_group_create();
+    auto closureQueue = dispatch_queue_create("com.apple.dyld.cache.closure", dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_CONCURRENT, QOS_CLASS_USER_INITIATED, 0));
+
+    dedupeDispositions();
+    for (auto& config : _configurations) {
+        for (auto& arch : config.second.architectures) {
+            dispatch_semaphore_wait(closureSemaphore, DISPATCH_TIME_FOREVER);
+            dispatch_group_async(closureGroup, closureQueue, [&] {
+                calculateClosure(config.first, arch.first);
+                dispatch_semaphore_signal(closureSemaphore);
+            });
+        }
+    }
+
+    dispatch_group_wait(closureGroup, DISPATCH_TIME_FOREVER);
+}
+
+void Manifest::remove(const std::string& config, const std::string& arch)
+{
+    if (_configurations.count(config))
+        _configurations[config].architectures.erase(arch);
+}
+
+void Manifest::removeDylib(MachOParser parser, const std::string& reason, const std::string& configuration,
+    const std::string& architecture, std::unordered_set<UUID>& processedIdentifiers)
+{
+#if 0
+    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(parser->uuid()) == 0) {
+        archManifest.results.dylibs[parser->uuid()].uuid = parser->uuid();
+        archManifest.results.dylibs[parser->uuid()].installname = parser->installName();
+        processedIdentifiers.insert(parser->uuid());
+    }
+    archManifest.results.exclude(MachOProxy::forIdentifier(parser->uuid(), architecture), reason);
+
+    processedIdentifiers.insert(parser->uuid());
+
+    for (const auto& dependent : proxy->dependentIdentifiers) {
+        auto dependentProxy = MachOProxy::forIdentifier(dependent, architecture);
+        auto dependentResultIter = archManifest.results.dylibs.find(dependentProxy->identifier);
+        if ( dependentProxy &&
+             ( dependentResultIter == archManifest.results.dylibs.end() || dependentResultIter->second.included == true ) ) {
+            removeDylib(dependentProxy, "Missing dependency: " + proxy->installName, configuration, architecture,
+                processedIdentifiers);
+        }
+    }
+#endif
+}
+
+const std::string Manifest::removeLargestLeafDylib(const std::set<std::string>& configurations, const std::string& architecture)
+{
+    // Find the leaf nodes
+    __block std::map<std::string, uint64_t> dependentCounts;
+    for (const auto& dylib : _configurations[*configurations.begin()].architectures[architecture].results.dylibs) {
+        if (!dylib.second.included)
+            continue;
+        std::string installName;
+        auto info = infoForUUID(dylib.first);
+        auto parser = MachOParser(info.mh);
+        dependentCounts[parser.installName()] = 0;
+    }
+
+    for (const auto& dylib : _configurations[*configurations.begin()].architectures[architecture].results.dylibs) {
+        if (!dylib.second.included)
+            continue;
+        auto info = infoForUUID(dylib.first);
+        auto parser = MachOParser(info.mh);
+        parser.forEachDependentDylib(^(const char *loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool &stop) {
+            if (!isWeak) {
+                dependentCounts[loadPath]++;
+            }
+        });
+    }
+
+    // Figure out which leaf is largest
+    UUIDInfo largestLeaf;
+
+    for (const auto& dependentCount : dependentCounts) {
+        if (dependentCount.second == 0) {
+            auto info = infoForInstallNameAndarch(dependentCount.first, architecture);
+            assert(info.mh != nullptr);
+            if (info.size > largestLeaf.size) {
+                largestLeaf = info;
+            }
+        }
+    }
+
+    if (largestLeaf.mh == nullptr) {
+        _diags.error("Fatal overflow, could not evict more dylibs");
+        return "";
+    }
+
+    // Remove it ferom all configs
+    for (const auto& config : configurations) {
+        configuration(config).architecture(architecture).results.exclude(*this, largestLeaf.uuid, "Cache Overflow");
+    }
+
+    return largestLeaf.installName;
+}
+
+void Manifest::calculateClosure(const std::string& configuration, const std::string& architecture)
+{
+    __block auto&   configManifest = _configurations[configuration];
+    __block auto&   archManifest = _configurations[configuration].architectures[architecture];
+    __block std::set<UUID> newUuids;
+    std::set<UUID>         processedUuids;
+    std::set<UUID>         cachedUUIDs;
+
+    // Seed anchors
+    for (auto& uuidInfo : _uuidMap) {
+        auto info = uuidInfo.second;
+        if (info.arch != architecture) {
+            continue;
+        }
+
+        auto i = _metabomTagMap.find(info.runtimePath);
+        assert(i != _metabomTagMap.end());
+        auto tags = i->second;
+        if (!is_disjoint(tags, configManifest.metabomTags)) {
+            newUuids.insert(info.uuid);
+
+        }
+    }
+
+    // Pull in all dependencies
+    while (!newUuids.empty()) {
+        std::set<UUID> uuidsToProcess = newUuids;
+        newUuids.clear();
+
+        for (const auto& uuid : uuidsToProcess) {
+            if (processedUuids.count(uuid) > 0) {
+                continue;
+            }
+            processedUuids.insert(uuid);
+
+            auto parser = parserForUUID(uuid);
+            auto runtimePath = runtimePathForUUID(uuid);
+            assert(parser.header() != 0);
+
+            parser.forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool& stop) {
+                auto i = _installNameMap.find(std::make_pair(loadPath, architecture));
+                if (i != _installNameMap.end())
+                newUuids.insert(i->second);
+            });
+
+            if (parser.fileType() == MH_DYLIB) {
+                // Add the dylib to the results
+                if (archManifest.results.dylibs.count(uuid) == 0 ) {
+                    archManifest.results.dylibs[uuid].uuid = uuid;
+                    archManifest.results.dylibs[uuid].installname = parser.installName();
+                }
+
+                // HACK to insert device specific dylib closures into all caches
+                if ( parser.installName() == std::string("/System/Library/Caches/com.apple.xpc/sdk.dylib")
+                    || parser.installName() == std::string("/System/Library/Caches/com.apple.xpcd/xpcd_cache.dylib") ) {
+                    archManifest.results.exclude(&parser, "Device specific dylib");
+                    continue;
+                }
+
+                std::set<std::string> reasons;
+                if (parser.canBePlacedInDyldCache(runtimePath, reasons)) {
+                    auto i = _metabomTagMap.find(runtimePath);
+                    assert(i != _metabomTagMap.end());
+                    auto restrictions = _metabomRestrictedTagMap.find(configuration);
+                    if (restrictions != _metabomRestrictedTagMap.end() && !is_disjoint(restrictions->second, i->second)) {
+                        archManifest.results.exclude(&parser, "Dylib '" + runtimePath + "' removed due to explict restriction");
+                    }
+
+                    // It can be placed in the cache, grab its dependents and queue them for inclusion
+                    cachedUUIDs.insert(parser.uuid());
+                } else {
+                    // It can't be placed in the cache, print out the reasons why
+                    std::string reasonString = "Rejected from cached dylibs: " + runtimePath + " " + architecture + " (\"";
+                    for (auto i = reasons.begin(); i != reasons.end(); ++i) {
+                        reasonString += *i;
+                        if (i != --reasons.end()) {
+                            reasonString += "\", \"";
+                        }
+                    }
+                    reasonString += "\")";
+                    archManifest.results.exclude(&parser, reasonString);
+                }
+            } else if (parser.fileType() == MH_BUNDLE) {
+                if (archManifest.results.bundles.count(uuid) == 0) {
+                    archManifest.results.bundles[uuid].uuid = uuid;
+                }
+            } else if (parser.fileType() == MH_EXECUTE) {
+                //HACK exclude all launchd and installd variants until we can do something about xpcd_cache.dylib and friends
+                if (runtimePath == "/sbin/launchd"
+                    || runtimePath == "/usr/local/sbin/launchd.debug"
+                    || runtimePath == "/usr/local/sbin/launchd.development"
+                    || runtimePath == "/usr/libexec/installd") {
+                    continue;
+                }
+                if (archManifest.results.executables.count(uuid) == 0) {
+                    archManifest.results.executables[uuid].uuid = uuid;
+                }
+            }
+        }
+    }
+
+    __block std::set<UUID>         removedUUIDs;
+    __block bool                   doAgain = true;
+
+    //Trim out dylibs that are missing dependencies
+    while ( doAgain ) {
+        doAgain = false;
+        for (const auto& uuid : cachedUUIDs) {
+            __block std::set<std::string> badDependencies;
+            __block auto parser = parserForUUID(uuid);
+            parser.forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool& stop) {
+                if (isWeak)
+                    return;
+
+                auto i = _installNameMap.find(std::make_pair(loadPath, architecture));
+                if (i == _installNameMap.end() || removedUUIDs.count(i->second)) {
+                    removedUUIDs.insert(uuid);
+                    badDependencies.insert(loadPath);
+                    doAgain = true;
+                }
+
+                if (badDependencies.size()) {
+                    std::string reasonString = "Rejected from cached dylibs: " + std::string(parser.installName()) + " " + architecture + " (\"";
+                    for (auto i = badDependencies.begin(); i != badDependencies.end(); ++i) {
+                        reasonString += *i;
+                        if (i != --badDependencies.end()) {
+                            reasonString += "\", \"";
+                        }
+                    }
+                    reasonString += "\")";
+                    archManifest.results.exclude(&parser, reasonString);
+                }
+            });
+        }
+
+        for (const auto& removedUUID : removedUUIDs) {
+            cachedUUIDs.erase(removedUUID);
+        }
+    }
+
+    //Trim out excluded leaf dylibs
+    __block std::set<std::string> linkedDylibs;
+
+    for(const auto& uuid : cachedUUIDs) {
+        auto parser = parserForUUID(uuid);
+        parser.forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool& stop) {
+            linkedDylibs.insert(loadPath);
+        });
+    }
+
+    for(const auto& uuid : cachedUUIDs) {
+        auto info = infoForUUID(uuid);
+        auto i = _metabomTagMap.find(info.runtimePath);
+        assert(i != _metabomTagMap.end());
+        auto exclusions = _metabomExcludeTagMap.find(configuration);
+        if (exclusions == _metabomExcludeTagMap.end() || is_disjoint(exclusions->second, i->second))
+            continue;
+
+        if (linkedDylibs.count(info.installName) != 0)
+            continue;
+
+        archManifest.results.exclude(*this, info.uuid, "Dylib '" + info.runtimePath + "' excluded leaf node");
+    }
+}
+
+void Manifest::writeJSON(const std::string& path) {
+    NSMutableDictionary* jsonDict = [[NSMutableDictionary alloc] init];
+    for (auto& configuration : _configurations) {
+        jsonDict[cppToObjStr(configuration.first)] = [[NSMutableDictionary alloc] init];
+
+        for (auto& arch : configuration.second.architectures) {
+            NSMutableOrderedSet* includedDylibsSet = [[NSMutableOrderedSet alloc] init];
+            NSMutableOrderedSet* executablesSet = [[NSMutableOrderedSet alloc] init];
+            NSMutableOrderedSet* otherSet = [[NSMutableOrderedSet alloc] init];
+            for (auto& dylib : arch.second.results.dylibs) {
+                NSString *runtimePath = cppToObjStr(runtimePathForUUID(dylib.second.uuid));
+                if (dylib.second.included) {
+                    [includedDylibsSet addObject:runtimePath];
+                } else {
+                    [otherSet addObject:runtimePath];
+                }
+            }
+
+            for (auto& executable : arch.second.results.executables) {
+                NSString *runtimePath = cppToObjStr(runtimePathForUUID(executable.second.uuid));
+                [executablesSet addObject:runtimePath];
+            }
+
+            for (auto& bundle : arch.second.results.bundles) {
+                NSString *runtimePath = cppToObjStr(runtimePathForUUID(bundle.second.uuid));
+                [otherSet addObject:runtimePath];
+            }
+
+            [includedDylibsSet sortUsingComparator:^NSComparisonResult(id  _Nonnull obj1, id  _Nonnull obj2) {
+                return [obj1 compare:obj2];
+            }];
+
+            [executablesSet sortUsingComparator:^NSComparisonResult(id  _Nonnull obj1, id  _Nonnull obj2) {
+                return [obj1 compare:obj2];
+            }];
+
+            [otherSet sortUsingComparator:^NSComparisonResult(id  _Nonnull obj1, id  _Nonnull obj2) {
+                return [obj1 compare:obj2];
+            }];
+
+            jsonDict[cppToObjStr(configuration.first)][cppToObjStr(arch.first)] = @{ @"cachedDylibs" : [includedDylibsSet array], @"mainExecutables" : [executablesSet array], @"other" : [otherSet array]};;
+        }
+    }
+
+    NSError* error = nil;
+    NSData *jsonData = [NSJSONSerialization dataWithJSONObject:jsonDict options:0x0 error:&error];
+    (void)[jsonData writeToFile:cppToObjStr(path) atomically:YES];
+}
+
+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"] = @(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) {
+                    dylibDict[@"included"] = @YES;
+                } else {
+                    dylibDict[@"included"] = @NO;
+                    dylibDict[@"exclusionInfo"] = cppToObjStr(dylib.second.exclusionInfo);
+                }
+                dylibsDict[cppToObjStr(dylib.second.installname)] = dylibDict;
+            }
+
+            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;
+    }
+
+    switch (platform()) {
+    case Platform::iOS:
+        cacheDict[@"platform"] = @"ios";
+        break;
+    case Platform::tvOS:
+        cacheDict[@"platform"] = @"tvos";
+        break;
+    case Platform::watchOS:
+        cacheDict[@"platform"] = @"watchos";
+        break;
+    case Platform::bridgeOS:
+        cacheDict[@"platform"] = @"bridgeos";
+        break;
+    case Platform::macOS:
+        cacheDict[@"platform"] = @"macos";
+        break;
+    case Platform::unknown:
+        cacheDict[@"platform"] = @"unknown";
+        break;
+    }
+
+    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/dyld3/shared-cache/ObjC1Abstraction.hpp b/dyld3/shared-cache/ObjC1Abstraction.hpp
new file mode 100644 (file)
index 0000000..93bf751
--- /dev/null
@@ -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 <typename P>
+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 <typename P>
+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 <typename P>
+struct objc_method_list {
+    enum { OBJC_FIXED_UP = 1771 };
+    uint32_t obsolete;      // struct objc_method_list *
+    uint32_t method_count;  // int
+    struct objc_method<P> 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 <typename P>
+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<P> *getIsa(ContentAccessor* cache) const INLINE { return (struct objc_class<P> *)cache->contentForVMAddr(P::E::get32(isa)); }
+    struct objc_method_list<P> *getMethodList(ContentAccessor* cache) const INLINE { return (struct objc_method_list<P> *)cache->contentForVMAddr(P::E::get32(methodList)); }
+};
+
+template <typename P>
+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<P> *getInstanceMethods(ContentAccessor* cache) const INLINE { return (struct objc_method_list<P> *)cache->contentForVMAddr(P::E::get32(instance_methods)); }
+    struct objc_method_list<P> *getClassMethods(ContentAccessor* cache) const INLINE { return (struct objc_method_list<P> *)cache->contentForVMAddr(P::E::get32(class_methods)); }
+};
+
+template <typename P>
+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<P> *getClass(ContentAccessor* cache, int index) const INLINE { return (struct objc_class<P> *)cache->contentForVMAddr(P::E::get32(defs[index])); }
+    struct objc_category<P> *getCategory(ContentAccessor* cache, int index) const INLINE { return (struct objc_category<P> *)cache->contentForVMAddr(P::E::get32(defs[getClassCount() + index])); }
+};
+
+template <typename P>
+struct objc_module {
+    uint32_t version;  // unsigned long
+    uint32_t size;     // unsigned long
+    uint32_t name;     // char*
+    uint32_t symtab;   // Symtab
+    
+    struct objc_symtab<P> *getSymtab(ContentAccessor* cache) const INLINE { return (struct objc_symtab<P> *)cache->contentForVMAddr(P::E::get32(symtab)); }
+};
+
+template <typename P>
+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 <typename P>
+struct objc_method_description_list {
+    uint32_t count;  // int
+    struct objc_method_description<P> list[0];
+    
+    uint32_t getCount() const INLINE { return P::E::get32(count); }
+};
+
+template <typename P>
+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<P> *getInstanceMethodDescriptions(ContentAccessor* cache) const INLINE { return (struct objc_method_description_list<P> *)cache->contentForVMAddr(P::E::get32(instance_methods)); }
+    struct objc_method_description_list<P> *getClassMethodDescriptions(ContentAccessor* cache) const INLINE { return (struct objc_method_description_list<P> *)cache->contentForVMAddr(P::E::get32(class_methods)); }
+};
+
+
+template <typename P, typename V>
+class LegacySelectorUpdater {
+    typedef typename P::uint_t pint_t;
+
+    static void visitMethodList(objc_method_list<P> *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<P> *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<P>* header, V& visitor)
+    {
+        ArraySection<P, objc_module<P> >
+            modules(cache, header, "__OBJC", "__module_info");
+        for (uint64_t m = 0; m < modules.count(); m++) {
+            objc_symtab<P> *symtab = modules.get(m).getSymtab(cache);
+            if (!symtab) continue;
+
+            // Method lists in classes
+            for (int c = 0; c < symtab->getClassCount(); c++) {
+                objc_class<P> *cls = symtab->getClass(cache, c);
+                objc_class<P> *isa = cls->getIsa(cache);
+                objc_method_list<P> *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<P> *cat = symtab->getCategory(cache, c);
+                objc_method_list<P> *mlist;
+                if ((mlist = cat->getInstanceMethods(cache))) {
+                    visitMethodList(mlist, visitor);
+                }
+                if ((mlist = cat->getClassMethods(cache))) {
+                    visitMethodList(mlist, visitor);
+                }
+            }
+        }
+
+        // Method description lists from protocols        
+        ArraySection<P, objc_protocol<P>>
+            protocols(cache, header, "__OBJC", "__protocol");
+        for (uint64_t p = 0; p < protocols.count(); p++) {
+            objc_protocol<P>& protocol = protocols.get(p);
+            objc_method_description_list<P> *mlist;
+            if ((mlist = protocol.getInstanceMethodDescriptions(cache))) {
+                visitMethodDescriptionList(mlist, visitor);
+            }
+            if ((mlist = protocol.getClassMethodDescriptions(cache))) {
+                visitMethodDescriptionList(mlist, visitor);
+            }
+        }
+
+        // Message refs
+        PointerSection<P, const char *> 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/dyld3/shared-cache/ObjC2Abstraction.hpp b/dyld3/shared-cache/ObjC2Abstraction.hpp
new file mode 100644 (file)
index 0000000..59aa455
--- /dev/null
@@ -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 <iterator>
+#include <deque>
+#include <set>
+
+// iterate an entsize-based list
+// typedef entsize_iterator<P, type_t<P>, type_list_t<P> > type_iterator;
+template <typename P, typename T, typename Tlist>
+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<P,T,Tlist>& operator += (ptrdiff_t count) {
+        current = (T*)((uint8_t *)current + count*entsize);
+        index += count;
+        return *this;
+    }
+    const entsize_iterator<P,T,Tlist>& operator -= (ptrdiff_t count) {
+        current = (T*)((uint8_t *)current - count*entsize);
+        index -= count;
+        return *this;
+    }
+    const entsize_iterator<P,T,Tlist> operator + (ptrdiff_t count) const {
+        return entsize_iterator(*this) += count;
+    }
+    const entsize_iterator<P,T,Tlist> operator - (ptrdiff_t count) const {
+        return entsize_iterator(*this) -= count;
+    }
+    
+    entsize_iterator<P,T,Tlist>& operator ++ () { *this += 1; return *this; }
+    entsize_iterator<P,T,Tlist>& operator -- () { *this -= 1; return *this; }
+    entsize_iterator<P,T,Tlist> operator ++ (int) { 
+        entsize_iterator<P,T,Tlist> result(*this); *this += 1; return result; 
+    }
+    entsize_iterator<P,T,Tlist> operator -- (int) { 
+        entsize_iterator<P,T,Tlist> result(*this); *this -= 1; return result; 
+    }
+    
+    ptrdiff_t operator - (const entsize_iterator<P,T,Tlist>& 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<P,T,Tlist>& rhs) {
+        return this->current == rhs.current;
+    }
+    bool operator != (const entsize_iterator<P,T,Tlist>& rhs) {
+        return this->current != rhs.current;
+    }
+    
+    bool operator < (const entsize_iterator<P,T,Tlist>& rhs) {
+        return this->current < rhs.current;
+    }        
+    bool operator > (const entsize_iterator<P,T,Tlist>& rhs) {
+        return this->current > rhs.current;
+    }
+
+    
+    static void overwrite(entsize_iterator<P,T,Tlist>& dst, const Tlist* srcList)
+    {
+        entsize_iterator<P,T,Tlist> src;
+        uint32_t ee = srcList->getEntsize();
+        for (src = srcList->begin(); src != srcList->end(); ++src) {
+            memcpy(&*dst, &*src, ee);
+            ++dst;
+        }
+    }
+};
+
+template <typename P> 
+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<P>* mh)
+        : data(0) {
+    }
+};
+
+template <typename P>
+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<P>* 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<P>* 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<P>* rwmh = const_cast<macho_header<P>*>(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 <typename P>
+class objc_method_list_t;  // forward reference
+
+
+template <typename P>
+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<P>;
+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<P>&, 
+                                    const objc_method_t<P>&, bool>
+    {
+        bool operator() (const objc_method_t<P>& lhs, 
+                         const objc_method_t<P>& rhs)
+        {
+            return lhs.getName() < rhs.getName();
+        }
+    };
+};
+
+template <typename P>
+class objc_method_list_t {
+    uint32_t entsize;
+    uint32_t count;
+    objc_method_t<P> first;
+
+    void* operator new (size_t, void* buf) { return buf; }
+
+public:
+
+    typedef entsize_iterator<P, objc_method_t<P>, objc_method_list_t<P> > 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<P>& get(uint32_t i) const { return *(objc_method_t<P> *)((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<P>)) { 
+        return sizeof(objc_method_list_t<P>) - sizeof(objc_method_t<P>) + 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<void*>& pointersToRemove) {
+        for(method_iterator it = begin(); it != end(); ++it) {
+            objc_method_t<P>& entry = *it;
+            pointersToRemove.insert(&(entry.name));
+            pointersToRemove.insert(&(entry.types));
+            pointersToRemove.insert(&(entry.imp));
+        }
+    }
+    
+    static void addPointers(uint8_t* methodList, std::vector<void*>& pointersToAdd) {
+        objc_method_list_t<P>* mlist = (objc_method_list_t<P>*)methodList;
+        for(method_iterator it = mlist->begin(); it != mlist->end(); ++it) {
+            objc_method_t<P>& entry = *it;
+            pointersToAdd.push_back(&(entry.name));
+            pointersToAdd.push_back(&(entry.types));
+            pointersToAdd.push_back(&(entry.imp));
+        }
+    }
+
+    static objc_method_list_t<P>* newMethodList(size_t newCount, uint32_t newEntsize) {
+        void *buf = ::calloc(byteSizeForCount(newCount, newEntsize), 1);
+        return new (buf) objc_method_list_t<P>(newCount, newEntsize);
+    }
+
+    void operator delete(void * p) { 
+        ::free(p); 
+    }
+
+    objc_method_list_t(uint32_t newCount, 
+                       uint32_t newEntsize = sizeof(objc_method_t<P>))
+        : entsize(newEntsize), count(newCount) 
+    { }
+
+private:
+    // use newMethodList instead
+    void* operator new (size_t);
+};
+
+
+template <typename P>
+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<<a;
+    }
+};
+
+template <typename P>
+class objc_ivar_list_t {
+    typedef typename P::uint_t pint_t;
+    uint32_t entsize;
+    uint32_t count;
+    objc_ivar_t<P> first;
+
+    void* operator new (size_t, void* buf) { return buf; }
+
+public:
+
+    typedef entsize_iterator<P, objc_ivar_t<P>, objc_ivar_list_t<P> > ivar_iterator;
+
+    uint32_t getCount() const { return P::E::get32(count); }
+
+    uint32_t getEntsize() const { return P::E::get32(entsize); }
+
+    objc_ivar_t<P>& get(pint_t i) const { return *(objc_ivar_t<P> *)((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<P>)) { 
+        return sizeof(objc_ivar_list_t<P>) - sizeof(objc_ivar_t<P>) + 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<P>* newIvarList(size_t newCount, uint32_t newEntsize) {
+        void *buf = ::calloc(byteSizeForCount(newCount, newEntsize), 1);
+        return new (buf) objc_ivar_list_t<P>(newCount, newEntsize);
+    }
+
+    void operator delete(void * p) { 
+        ::free(p); 
+    }
+
+    objc_ivar_list_t(uint32_t newCount, 
+                         uint32_t newEntsize = sizeof(objc_ivar_t<P>))
+        : entsize(newEntsize), count(newCount) 
+    { }
+private:
+    // use newIvarList instead
+    void* operator new (size_t);
+};
+
+
+template <typename P> class objc_property_list_t; // forward 
+
+template <typename P>
+class objc_property_t {
+    typedef typename P::uint_t pint_t;
+    pint_t name;
+    pint_t attributes;
+    friend class objc_property_list_t<P>;
+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 <typename P>
+class objc_property_list_t {
+    uint32_t entsize;
+    uint32_t count;
+    objc_property_t<P> first;
+
+    void* operator new (size_t, void* buf) { return buf; }
+
+public:
+
+    typedef entsize_iterator<P, objc_property_t<P>, objc_property_list_t<P> > property_iterator;
+
+    uint32_t getCount() const { return P::E::get32(count); }
+
+    uint32_t getEntsize() const { return P::E::get32(entsize); }
+
+    objc_property_t<P>& get(uint32_t i) const { return *(objc_property_t<P> *)((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<P>)) { 
+        return sizeof(objc_property_list_t<P>) - sizeof(objc_property_t<P>) + 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<void*>& pointersToRemove) {
+        for(property_iterator it = begin(); it != end(); ++it) {
+            objc_property_t<P>& entry = *it;
+            pointersToRemove.insert(&(entry.name));
+            pointersToRemove.insert(&(entry.attributes));
+        }
+    }
+
+    static void addPointers(uint8_t* propertyList, std::vector<void*>& pointersToAdd) {
+        objc_property_list_t<P>* plist = (objc_property_list_t<P>*)propertyList;
+        for(property_iterator it = plist->begin(); it != plist->end(); ++it) {
+            objc_property_t<P>& entry = *it;
+            pointersToAdd.push_back(&(entry.name));
+            pointersToAdd.push_back(&(entry.attributes));
+        }
+    }
+
+     static objc_property_list_t<P>* newPropertyList(size_t newCount, uint32_t newEntsize) {
+        void *buf = ::calloc(byteSizeForCount(newCount, newEntsize), 1);
+        return new (buf) objc_property_list_t<P>(newCount, newEntsize);
+    }
+
+    void operator delete(void * p) { 
+        ::free(p); 
+    }
+
+    objc_property_list_t(uint32_t newCount, 
+                         uint32_t newEntsize = sizeof(objc_property_t<P>))
+        : entsize(newEntsize), count(newCount) 
+    { }
+private:
+    // use newPropertyList instead
+    void* operator new (size_t);
+};
+
+
+template <typename A> class objc_protocol_list_t;  // forward reference
+
+template <typename P>
+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<P> *getProtocols(ContentAccessor* cache) const { return (objc_protocol_list_t<P> *)cache->contentForVMAddr(P::getP(protocols)); }
+
+    objc_method_list_t<P> *getInstanceMethods(ContentAccessor* cache) const { return (objc_method_list_t<P> *)cache->contentForVMAddr(P::getP(instanceMethods)); }
+
+    objc_method_list_t<P> *getClassMethods(ContentAccessor* cache) const { return (objc_method_list_t<P> *)cache->contentForVMAddr(P::getP(classMethods)); }
+
+    objc_method_list_t<P> *getOptionalInstanceMethods(ContentAccessor* cache) const { return (objc_method_list_t<P> *)cache->contentForVMAddr(P::getP(optionalInstanceMethods)); }
+
+    objc_method_list_t<P> *getOptionalClassMethods(ContentAccessor* cache) const { return (objc_method_list_t<P> *)cache->contentForVMAddr(P::getP(optionalClassMethods)); }
+
+    objc_property_list_t<P> *getInstanceProperties(ContentAccessor* cache) const { return (objc_property_list_t<P> *)cache->contentForVMAddr(P::getP(instanceProperties)); }
+
+    pint_t *getExtendedMethodTypes(ContentAccessor* cache) const {
+        if (getSize() < offsetof(objc_protocol_t<P>, 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<P>, demangledName) + sizeof(demangledName)) {
+            return NULL;
+        }
+        return (const char *)cache->contentForVMAddr(P::getP(demangledName));
+    }
+
+    void setDemangledName(ContentAccessor* cache, const char *newName, Diagnostics& diag) {
+        if (sizeof(*this) < offsetof(objc_protocol_t<P>, demangledName) + sizeof(demangledName))
+            diag.error("objc protocol has the wrong size");
+        else
+            P::setP(demangledName, cache->vmAddrForContent((void*)newName));
+    }
+
+    void addPointers(std::vector<void*>& 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 <typename P>
+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<P>* get(ContentAccessor* cache, pint_t i) {
+        return (objc_protocol_t<P>*)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<P>* 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<P>) + c*sizeof(pint_t);
+    }
+
+    void getPointers(std::set<void*>& pointersToRemove) {
+        for(int i=0 ; i < count; ++i) {
+            pointersToRemove.insert(&list[i]);
+        }
+    }
+
+     static void addPointers(uint8_t* protocolList, std::vector<void*>& pointersToAdd) {
+        objc_protocol_list_t<P>* plist = (objc_protocol_list_t<P>*)protocolList;
+        for(int i=0 ; i < plist->count; ++i) {
+            pointersToAdd.push_back(&plist->list[i]);
+        }
+    }
+
+    static objc_protocol_list_t<P>* newProtocolList(pint_t newCount) {
+        void *buf = ::calloc(byteSizeForCount(newCount), 1);
+        return new (buf) objc_protocol_list_t<P>(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 <typename P>
+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<P> *getMethodList(ContentAccessor* cache) const { return (objc_method_list_t<P> *)cache->contentForVMAddr(P::getP(baseMethods)); }
+
+    objc_protocol_list_t<P> *getProtocolList(ContentAccessor* cache) const { return (objc_protocol_list_t<P> *)cache->contentForVMAddr(P::getP(baseProtocols)); }
+
+    objc_ivar_list_t<P> *getIvarList(ContentAccessor* cache) const { return (objc_ivar_list_t<P> *)cache->contentForVMAddr(P::getP(ivars)); }
+    
+    objc_property_list_t<P> *getPropertyList(ContentAccessor* cache) const { return (objc_property_list_t<P> *)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<P>* mlist) {
+        P::setP(baseMethods, cache->vmAddrForContent(mlist));
+    }
+
+    void setProtocolList(ContentAccessor* cache, objc_protocol_list_t<P>* protolist) {
+        P::setP(baseProtocols, cache->vmAddrForContent(protolist));
+    }
+    void setPropertyList(ContentAccessor* cache, objc_property_list_t<P>* proplist) {
+        P::setP(baseProperties, cache->vmAddrForContent(proplist));
+    }
+    
+    void addMethodListPointer(std::vector<void*>& pointersToAdd) {
+        pointersToAdd.push_back(&this->baseMethods);
+    }
+    
+    void addPropertyListPointer(std::vector<void*>& pointersToAdd) {
+        pointersToAdd.push_back(&this->baseProperties);
+    }
+    
+    void addProtocolListPointer(std::vector<void*>& pointersToAdd) {
+        pointersToAdd.push_back(&this->baseProtocols);
+    }
+};
+
+template <typename P>
+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<P> *getIsa(ContentAccessor* cache) const { return (objc_class_t<P> *)cache->contentForVMAddr(P::getP(isa)); }
+
+    objc_class_t<P> *getSuperclass(ContentAccessor* cache) const { return (objc_class_t<P> *)cache->contentForVMAddr(P::getP(superclass)); }
+    
+    // Low bit marks Swift classes.
+    objc_class_data_t<P> *getData(ContentAccessor* cache) const { return (objc_class_data_t<P> *)cache->contentForVMAddr(P::getP(data & ~0x1LL)); }
+
+    objc_method_list_t<P> *getMethodList(ContentAccessor* cache) const {
+        objc_class_data_t<P>* d = getData(cache);
+        return d->getMethodList(cache);
+    }
+
+    objc_protocol_list_t<P> *getProtocolList(ContentAccessor* cache) const { return getData(cache)->getProtocolList(cache); }
+
+    objc_property_list_t<P> *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<P>* mlist) {
+        getData(cache)->setMethodList(cache, mlist);
+    }
+
+    void setProtocolList(ContentAccessor* cache, objc_protocol_list_t<P>* protolist) {
+        getData(cache)->setProtocolList(cache, protolist);
+    }
+
+    void setPropertyList(ContentAccessor* cache, objc_property_list_t<P>* proplist) {
+        getData(cache)->setPropertyList(cache, proplist);
+    }
+    
+    void addMethodListPointer(ContentAccessor* cache, std::vector<void*>& pointersToAdd) {
+        getData(cache)->addMethodListPointer(pointersToAdd);
+    }
+    
+    void addPropertyListPointer(ContentAccessor* cache, std::vector<void*>& pointersToAdd) {
+        getData(cache)->addPropertyListPointer(pointersToAdd);
+    }
+    
+    void addProtocolListPointer(ContentAccessor* cache, std::vector<void*>& pointersToAdd) {
+        getData(cache)->addProtocolListPointer(pointersToAdd);
+    }
+    
+};
+
+
+
+template <typename P>
+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<P> *getClass(ContentAccessor* cache) const { return (objc_class_t<P> *)cache->contentForVMAddr(P::getP(cls)); }
+
+    objc_method_list_t<P> *getInstanceMethods(ContentAccessor* cache) const { return (objc_method_list_t<P> *)cache->contentForVMAddr(P::getP(instanceMethods)); }
+
+    objc_method_list_t<P> *getClassMethods(ContentAccessor* cache) const { return (objc_method_list_t<P> *)cache->contentForVMAddr(P::getP(classMethods)); }
+
+    objc_protocol_list_t<P> *getProtocols(ContentAccessor* cache) const { return (objc_protocol_list_t<P> *)cache->contentForVMAddr(P::getP(protocols)); }
+    objc_property_list_t<P> *getInstanceProperties(ContentAccessor* cache) const { return (objc_property_list_t<P> *)cache->contentForVMAddr(P::getP(instanceProperties)); }
+
+    void getPointers(std::set<void*>& pointersToRemove) {
+        pointersToRemove.insert(&name);
+        pointersToRemove.insert(&cls);
+        pointersToRemove.insert(&instanceMethods);
+        pointersToRemove.insert(&classMethods);
+        pointersToRemove.insert(&protocols);
+        pointersToRemove.insert(&instanceProperties);
+    }
+
+
+};
+
+template <typename P>
+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 <typename P, typename V>
+class IvarWalker {
+    typedef typename P::uint_t pint_t;
+    V& ivarVisitor;
+public:
+    
+    IvarWalker(V& visitor) : ivarVisitor(visitor) { }
+    
+    void walk(ContentAccessor* cache, const macho_header<P>* header, objc_class_t<P> *cls)
+    {
+        objc_class_data_t<P> *data = cls->getData(cache);
+        objc_ivar_list_t<P> *ivars = data->getIvarList(cache);
+        if (ivars) {
+            for (pint_t i = 0; i < ivars->getCount(); i++) {
+                objc_ivar_t<P>& 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<P>* header, objc_class_t<P> *cls)
+    {
+        walk(cache, header, cls);
+    }
+};
+
+// Call visitor.visitClass() on every class.
+template <typename P, typename V>
+class ClassWalker {
+    typedef typename P::uint_t pint_t;
+    V& _visitor;
+public:
+    
+    ClassWalker(V& visitor) : _visitor(visitor) { }
+    
+    void walk(ContentAccessor* cache, const macho_header<P>* header)
+    {   
+        PointerSection<P, objc_class_t<P>*> classList(cache, header, "__DATA", "__objc_classlist");
+        
+        for (pint_t i = 0; i < classList.count(); i++) {
+            objc_class_t<P>* 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 <typename P, typename V>
+class ProtocolWalker {
+    typedef typename P::uint_t pint_t;
+    V& _protocolVisitor;
+public:
+    
+    ProtocolWalker(V& visitor) : _protocolVisitor(visitor) { }
+    
+    void walk(ContentAccessor* cache, const macho_header<P>* header)
+    {   
+        PointerSection<P, objc_protocol_t<P> *>
+            protocols(cache, header, "__DATA", "__objc_protolist");
+        
+        for (pint_t i = 0; i < protocols.count(); i++) {
+            objc_protocol_t<P> *proto = protocols.get(i);
+            _protocolVisitor.visitProtocol(cache, header, proto);
+        }
+    }
+};
+
+// Call visitor.visitProtocolReference() on every protocol.
+template <typename P, typename V>
+class ProtocolReferenceWalker {
+    typedef typename P::uint_t pint_t;
+    V& _visitor;
+
+    void visitProtocolList(ContentAccessor* cache,
+                           objc_protocol_list_t<P>* 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<P, ProtocolReferenceWalker<P, V>>;
+
+    void visitClass(ContentAccessor* cache, const macho_header<P>*,
+                    objc_class_t<P>* 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<P>* header)
+    {
+        // @protocol expressions
+        PointerSection<P, objc_protocol_t<P> *>
+            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<P, ProtocolReferenceWalker<P, V>> classes(*this);
+        classes.walk(cache, header);
+
+        // protocol lists in protocols
+        // __objc_protolists itself is NOT updated
+        PointerSection<P, objc_protocol_t<P> *>
+            protocols(cache, header, "__DATA", "__objc_protolist");
+        for (pint_t i = 0; i < protocols.count(); i++) {
+            objc_protocol_t<P>* 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 <typename P, typename V>
+class MethodListWalker {
+
+    typedef typename P::uint_t pint_t;
+
+    V& mVisitor;
+
+public: 
+    
+    MethodListWalker(V& visitor) : mVisitor(visitor) { }
+
+    void walk(ContentAccessor* cache, const macho_header<P>* header)
+    {   
+        // Method lists in classes
+        PointerSection<P, objc_class_t<P> *> 
+            classes(cache, header, "__DATA", "__objc_classlist");
+            
+        for (pint_t i = 0; i < classes.count(); i++) {
+            objc_class_t<P> *cls = classes.get(i);
+            objc_method_list_t<P> *mlist;
+            if ((mlist = cls->getMethodList(cache))) {
+                mVisitor.visitMethodList(mlist);
+            }
+            if ((mlist = cls->getIsa(cache)->getMethodList(cache))) {
+                mVisitor.visitMethodList(mlist);
+            }
+        }
+        
+        // Method lists from categories
+        PointerSection<P, objc_category_t<P> *> 
+            cats(cache, header, "__DATA", "__objc_catlist");
+        for (pint_t i = 0; i < cats.count(); i++) {
+            objc_category_t<P> *cat = cats.get(i);
+            objc_method_list_t<P> *mlist;
+            if ((mlist = cat->getInstanceMethods(cache))) {
+                mVisitor.visitMethodList(mlist);
+            }
+            if ((mlist = cat->getClassMethods(cache))) {
+                mVisitor.visitMethodList(mlist);
+            }
+        }
+
+        // Method description lists from protocols
+        PointerSection<P, objc_protocol_t<P> *>
+            protocols(cache, header, "__DATA", "__objc_protolist");
+        for (pint_t i = 0; i < protocols.count(); i++) {
+            objc_protocol_t<P> *proto = protocols.get(i);
+            objc_method_list_t<P> *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 <typename P, typename V>
+class SelectorOptimizer {
+
+    typedef typename P::uint_t pint_t;
+
+    V& mVisitor;
+
+    friend class MethodListWalker<P, SelectorOptimizer<P,V> >;
+    void visitMethodList(objc_method_list_t<P> *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<P> *mlist, pint_t *types)
+    {
+        visitMethodList(mlist);
+    }
+
+public:
+
+    SelectorOptimizer(V& visitor) : mVisitor(visitor) { }
+
+    void optimize(ContentAccessor* cache, const macho_header<P>* header)
+    {
+        // method lists in classes, categories, and protocols
+        MethodListWalker<P, SelectorOptimizer<P,V> > mw(*this);
+        mw.walk(cache, header);
+        
+        // @selector references
+        PointerSection<P, const char *> 
+            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<P, objc_message_ref_t<P> > 
+            msgrefs(cache, header, "__DATA", "__objc_msgrefs");
+        for (pint_t i = 0; i < msgrefs.count(); i++) {
+            objc_message_ref_t<P>& 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 <typename P>
+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<P>* /*unused, may be NULL*/, objc_class_t<P> *cls, objc_ivar_t<P> *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<P>* /*unused, may be NULL*/, objc_class_t<P> *cls)
+    {
+        objc_class_t<P> *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<P> *data = cls->getData(cache);
+            objc_class_data_t<P> *super_data = super->getData(cache);
+            int32_t diff = super_data->getInstanceSize() - data->getInstanceStart();
+            if (diff > 0) {
+                IvarWalker<P, IvarOffsetOptimizer<P> > 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<P>* header)
+    {
+        // The slide code cannot fix up GC layout strings so skip modules that support or require GC
+        const macho_section<P> *imageInfoSection = header->getSection("__DATA", "__objc_imageinfo");
+        if (imageInfoSection) {
+            objc_image_info<P> *info = (objc_image_info<P> *)cache->contentForVMAddr(imageInfoSection->addr());
+            if (!info->supportsGCFlagSet() && !info->requiresGCFlagSet()) {
+                ClassWalker<P, IvarOffsetOptimizer<P> > classVisitor(*this);
+                classVisitor.walk(cache, header);
+            } else {
+                //fprintf(stderr, "GC support present - skipped module\n");
+            }
+        }
+    }
+};
+
+
+// Detect classes that have missing weak-import superclasses.
+template <typename P>
+class WeakClassDetector {
+    bool noMissing;
+
+    friend class ClassWalker<P, WeakClassDetector<P>>;
+    void visitClass(ContentAccessor* cache, const macho_header<P>*, 
+                    objc_class_t<P>* 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. 
+            cache->diagnostics().warning("Superclass of class '%s' is weak-import and missing.",
+                    cls->getName(cache));
+            noMissing = false;
+        }
+    }
+
+public:
+    bool noMissingWeakSuperclasses(ContentAccessor* cache, 
+                                   std::vector<const macho_header<P>*> dylibs)
+    {
+        noMissing = true;
+        ClassWalker<P, WeakClassDetector<P>> classes(*this);
+        for (auto mh : dylibs) {
+            classes.walk(cache, mh);
+        }
+        return noMissing;
+    }
+};
+
+
+// Sort methods in place by selector.
+template <typename P>
+class MethodListSorter {
+
+    typedef typename P::uint_t pint_t;
+
+    uint32_t _optimized;
+
+    friend class MethodListWalker<P, MethodListSorter<P> >;
+    void visitMethodList(objc_method_list_t<P> *mlist)
+    {
+        typename objc_method_t<P>::SortBySELAddress sorter;
+        std::stable_sort(mlist->begin(), mlist->end(), sorter);
+        mlist->setFixedUp();
+        _optimized++;
+    }
+
+    void visitProtocolMethodList(objc_method_list_t<P> *mlist, pint_t *typelist)
+    {
+        typename objc_method_t<P>::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<P>& mi = mlist->get(i);
+                objc_method_t<P>& 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<P>* header)
+    {
+        MethodListWalker<P, MethodListSorter<P> > mw(*this);
+        mw.walk(cache, header);
+    }
+};
+
+
+template <typename P, typename InfoT>
+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<P>* mh, std::vector<void*>& pointersInData) {
+        InfoT* hi = new(&_hInfos[_count++]) InfoT(cache, mh);
+        (void)hi;
+    }
+
+    InfoT* hinfoForHeader(ContentAccessor* cache, const macho_header<P>* 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/dyld3/shared-cache/OptimizerBranches.cpp b/dyld3/shared-cache/OptimizerBranches.cpp
new file mode 100644 (file)
index 0000000..332e045
--- /dev/null
@@ -0,0 +1,1466 @@
+/* -*- 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 <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <CommonCrypto/CommonDigest.h>
+
+#include <string>
+#include <unordered_map>
+#include <unordered_set>
+
+#include "StringUtils.h"
+#include "Trie.hpp"
+#include "MachOFileAbstraction.hpp"
+#include "MachOParser.h"
+#include "Diagnostics.h"
+#include "DyldSharedCache.h"
+#include "CacheBuilder.h"
+
+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",
+    // <rdar://problem/22050956> always use stubs for C++ symbols that can be overridden
+    "__ZdaPv",
+    "__ZdlPv",
+    "__Znam",
+    "__Znwm",
+
+    nullptr
+};
+
+
+static uint64_t branchPoolTextSize(const std::string& archName)
+{
+ if ( startsWith(archName, "arm64") )
+    return 0x0000C000;  // 48KB
+  else
+    return 0;
+}
+
+static uint64_t branchPoolLinkEditSize(const std::string& archName)
+{
+    if ( startsWith(archName, "arm64") )
+        return 0x00100000;  // 1MB
+    else
+        return 0;
+}
+
+
+template <typename P>
+class BranchPoolDylib {
+public:
+                            BranchPoolDylib(DyldSharedCache* cache, uint64_t startAddr,
+                                            uint64_t textRegionStartAddr, uint64_t poolLinkEditStartAddr, uint64_t poolLinkEditStartOffset, Diagnostics& diags);
+
+    uint64_t                addr() { return _startAddr; }
+    uint64_t                getForwardBranch(uint64_t finalTargetAddr, const char* name, std::vector<BranchPoolDylib<P>*>& branchIslandPools);
+    uint64_t                getBackBranch(uint64_t finalTargetAddr, const char* name, std::vector<BranchPoolDylib<P>*>& branchIslandPools);
+    void                    finalizeLoadCommands();
+    void                    printStats();
+
+private:
+    Diagnostics&            _diagnostics;
+    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;
+
+    DyldSharedCache*                            _cacheBuffer;
+    uint64_t                                    _startAddr;
+    std::unordered_map<uint64_t, uint32_t>      _targetToIslandIndex;
+    std::unordered_map<uint32_t, const char*>   _islandIndexToName;
+    macho_symtab_command<P>*                    _symbolTableCmd;
+    macho_dysymtab_command<P>*                  _dynamicSymbolTableCmd;
+    macho_uuid_command<P>*                      _uuidCmd;
+    uint32_t                                    _maxStubs;
+    uint32_t                                    _nextIndex;
+    uint32_t                                    _firstStubOffset;
+    uint32_t*                                   _stubInstructions;
+    macho_nlist<P>*                             _symbolTable;
+    char*                                       _nextString;
+    char*                                       _stringPoolStart;
+    char*                                       _stringPoolEnd;
+};
+
+template <typename P>
+BranchPoolDylib<P>::BranchPoolDylib(DyldSharedCache* cache, uint64_t poolStartAddr,
+                                     uint64_t textRegionStartAddr, uint64_t poolLinkEditStartAddr, uint64_t poolLinkEditStartOffset, Diagnostics& diags)
+    : _cacheBuffer(cache), _startAddr(poolStartAddr), _nextIndex(0), _firstStubOffset(0x280), _diagnostics(diags)
+{
+    std::string archName = cache->archName();
+    bool is64 = (sizeof(typename P::uint_t) == 8);
+
+    const uint64_t textSegSize = branchPoolTextSize(archName);
+    const uint64_t linkEditSegSize = branchPoolLinkEditSize(archName);
+    const unsigned stubCount = (unsigned)((textSegSize - _firstStubOffset)/4);
+    const uint32_t linkeditOffsetSymbolTable = 0;
+    const uint32_t linkeditOffsetIndirectSymbolTable = stubCount*sizeof(macho_nlist<P>);
+    const uint32_t linkeditOffsetSymbolPoolOffset = linkeditOffsetIndirectSymbolTable + stubCount*sizeof(uint32_t);
+    _maxStubs = stubCount;
+
+    // write mach_header and load commands for pseudo dylib
+    macho_header<P>* mh = (macho_header<P>*)((uint8_t*)cache + poolStartAddr - textRegionStartAddr);
+    mh->set_magic(is64 ? MH_MAGIC_64 : MH_MAGIC);
+    mh->set_cputype(dyld3::MachOParser::cpuTypeFromArchName(archName));
+    mh->set_cpusubtype(dyld3::MachOParser::cpuSubtypeFromArchName(archName));
+    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<P>* cmd = (macho_load_command<P>*)((uint8_t*)mh + sizeof(macho_header<P>));
+    macho_segment_command<P>* textSegCmd = (macho_segment_command<P>*)cmd;
+    textSegCmd->set_cmd(is64 ? LC_SEGMENT_64 : LC_SEGMENT);
+    textSegCmd->set_cmdsize(sizeof(macho_segment_command<P>)*2+sizeof(macho_section<P>));
+    textSegCmd->set_segname("__TEXT");
+    textSegCmd->set_vmaddr(poolStartAddr);
+    textSegCmd->set_vmsize(textSegSize);
+    textSegCmd->set_fileoff(poolStartAddr - textRegionStartAddr);
+    textSegCmd->set_filesize(branchPoolTextSize(archName));
+    textSegCmd->set_maxprot(PROT_READ|PROT_EXEC);
+    textSegCmd->set_initprot(PROT_READ|PROT_EXEC);
+    textSegCmd->set_nsects(1);
+    textSegCmd->set_flags(0);
+    macho_section<P>* stubSection = (macho_section<P>*)((uint8_t*)textSegCmd + sizeof(macho_segment_command<P>));
+    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<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
+    macho_segment_command<P>* linkEditSegCmd = (macho_segment_command<P>*)cmd;
+    linkEditSegCmd->set_cmd(is64 ? LC_SEGMENT_64 :  LC_SEGMENT);
+    linkEditSegCmd->set_cmdsize(sizeof(macho_segment_command<P>));
+    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<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
+    macho_dylib_command<P>* installNameCmd = (macho_dylib_command<P>*)cmd;
+    installNameCmd->set_cmd(LC_ID_DYLIB);
+    installNameCmd->set_cmdsize(sizeof(macho_dylib_command<P>) + 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<P>), "dyld_shared_cache_branch_islands");
+    // LC_SYMTAB
+    cmd = (macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
+    _symbolTableCmd = (macho_symtab_command<P>*)cmd;
+    _symbolTableCmd->set_cmd(LC_SYMTAB);
+    _symbolTableCmd->set_cmdsize(sizeof(macho_symtab_command<P>));
+    _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<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
+    _dynamicSymbolTableCmd = (macho_dysymtab_command<P>*)cmd;
+    _dynamicSymbolTableCmd->set_cmd(LC_DYSYMTAB);
+    _dynamicSymbolTableCmd->set_cmdsize(sizeof(macho_dysymtab_command<P>));
+    _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<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
+    // LC_UUID
+    _uuidCmd = (macho_uuid_command<P>*)cmd;
+    _uuidCmd->set_cmd(LC_UUID);
+    _uuidCmd->set_cmdsize(sizeof(macho_uuid_command<P>));
+    cmd = (macho_load_command<P>*)(((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*)cache + poolLinkEditStartOffset;
+    // write symbol table
+    _symbolTable = (macho_nlist<P>*)(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], "<unused>");
+    _nextString = &_stringPoolStart[10];
+}
+
+
+template <typename P>
+void BranchPoolDylib<P>::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 ) {
+        _diagnostics.verbose("branch islands in image at 0x%0llX:\n", _startAddr);
+        for (uint32_t i=0; i < _nextIndex; ++i) {
+            _diagnostics.verbose("   0x%llX  %s\n", indexToAddr(i), _islandIndexToName[i]);
+        }
+    }
+}
+
+template <typename P>
+uint64_t BranchPoolDylib<P>::getForwardBranch(uint64_t finalTargetAddr, const char* name, std::vector<BranchPoolDylib<P>*>& 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<P>* nextPool = nullptr;
+        for (size_t i=0; i < branchIslandPools.size()-1; ++i) {
+            if ( branchIslandPools[i] == this ) {
+                nextPool = branchIslandPools[i+1];
+                break;
+            }
+        }
+
+        if (nextPool == nullptr) {
+            _diagnostics.warning("BranchPoolDylib<P>::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 <typename P>
+uint64_t BranchPoolDylib<P>::getBackBranch(uint64_t finalTargetAddr, const char* name, std::vector<BranchPoolDylib<P>*>& 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<P>* nextPool = nullptr;
+        for (long i=branchIslandPools.size()-1; i > 0; --i) {
+            if ( branchIslandPools[i] == this ) {
+                nextPool = branchIslandPools[i-1];
+                break;
+            }
+        }
+
+        if (nextPool == nullptr) {
+            _diagnostics.warning("BranchPoolDylib<P>::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 <typename P>
+void BranchPoolDylib<P>::printStats()
+{
+    _diagnostics.verbose("  island pool at 0x%0llX has %u stubs and stringPool size=%lu\n", _startAddr, _nextIndex, _nextString - _stringPoolStart);
+}
+
+
+
+template <typename P>
+class StubOptimizer {
+public:
+                            StubOptimizer(void* cacheBuffer, macho_header<P>* mh, Diagnostics& diags);
+    void                    buildStubMap(const std::unordered_set<std::string>& neverStubEliminate);
+    void                    optimizeStubs(std::unordered_map<uint64_t,std::vector<uint64_t>>& targetToBranchIslands);
+    void                    bypassStubs(std::unordered_map<uint64_t,std::vector<uint64_t>>& targetToBranchIslands);
+    void                    optimizeCallSites(std::vector<BranchPoolDylib<P>*>& 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:
+    Diagnostics _diagnostics;
+    typedef std::function<bool(uint8_t callSiteKind, uint64_t callSiteAddr, uint64_t stubAddr, uint32_t& instruction)> CallSiteHandler;
+    typedef typename P::uint_t pint_t;
+    typedef typename P::E E;
+
+    void                    forEachCallSiteToAStub(CallSiteHandler);
+    void                    optimizeArm64CallSites(std::vector<BranchPoolDylib<P>*>& 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<pint_t, AddressAndName> StubVMAddrToTarget;
+
+    static const int64_t b128MegLimit = 0x07FFFFFF;
+    static const int64_t b16MegLimit  = 0x00FFFFFF;
+
+
+    macho_header<P>*                        _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<P>*          _symTabCmd           = nullptr;
+    const macho_dysymtab_command<P>*        _dynSymTabCmd        = nullptr;
+    const macho_dyld_info_command<P>*       _dyldInfo            = nullptr;
+    macho_linkedit_data_command<P>*         _splitSegInfoCmd     = nullptr;
+    const macho_section<P>*                 _textSection         = nullptr;
+    const macho_section<P>*                 _stubSection         = nullptr;
+    uint32_t                                _textSectionIndex    = 0;
+    uint32_t                                _stubSectionIndex    = 0;
+    pint_t                                  _textSegStartAddr    = 0;
+    uint32_t                                _textSegCacheOffset  = 0;
+    std::vector<macho_segment_command<P>*>  _segCmds;
+    std::unordered_map<pint_t, pint_t>      _stubAddrToLPAddr;
+    std::unordered_map<pint_t, pint_t>      _lpAddrToTargetAddr;
+     std::unordered_map<pint_t, const char*> _targetAddrToName;
+};
+
+template <typename P>
+StubOptimizer<P>::StubOptimizer(void* cacheBuffer, macho_header<P>* mh, Diagnostics& diags)
+: _mh(mh), _cacheBuffer(cacheBuffer), _diagnostics(diags)
+{
+    _linkeditBias = (uint8_t*)cacheBuffer;
+    const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)mh + sizeof(macho_header<P>));
+    const uint32_t cmd_count = mh->ncmds();
+    macho_segment_command<P>* segCmd;
+    uint32_t sectionIndex = 0;
+    const macho_load_command<P>* cmd = cmds;
+    for (uint32_t i = 0; i < cmd_count; ++i) {
+        switch (cmd->cmd()) {
+            case LC_ID_DYLIB:
+                _installName = ((macho_dylib_command<P>*)cmd)->name();
+                break;
+            case LC_SYMTAB:
+                _symTabCmd = (macho_symtab_command<P>*)cmd;
+                break;
+            case LC_DYSYMTAB:
+                _dynSymTabCmd = (macho_dysymtab_command<P>*)cmd;
+                break;
+            case LC_SEGMENT_SPLIT_INFO:
+                _splitSegInfoCmd = (macho_linkedit_data_command<P>*)cmd;
+                break;
+            case LC_DYLD_INFO:
+            case LC_DYLD_INFO_ONLY:
+                _dyldInfo = (macho_dyld_info_command<P>*)cmd;
+                break;
+            case macho_segment_command<P>::CMD:
+                segCmd =( macho_segment_command<P>*)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<P>* const sectionsStart = (macho_section<P>*)((char*)segCmd + sizeof(macho_segment_command<P>));
+                    const macho_section<P>* const sectionsEnd = &sectionsStart[segCmd->nsects()];
+                    for (const macho_section<P>* 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<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
+    }
+}
+
+
+
+template <typename P>
+uint32_t StubOptimizer<P>::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 ) {
+        _diagnostics.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 ) {
+        _diagnostics.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 ) {
+        _diagnostics.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 <typename P>
+uint64_t StubOptimizer<P>::lazyPointerAddrFromArm64Stub(const uint8_t* stubInstructions, uint64_t stubVMAddr)
+{
+    uint32_t stubInstr1 = E::get32(*(uint32_t*)stubInstructions);
+    if ( (stubInstr1 & 0x9F00001F) != 0x90000010 ) {
+        _diagnostics.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 ) {
+        _diagnostics.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 <typename P>
+void StubOptimizer<P>::buildStubMap(const std::unordered_set<std::string>& neverStubEliminate)
+{
+    // find all stubs and lazy pointers
+    const macho_nlist<P>* symbolTable = (const macho_nlist<P>*)(((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<P>* const cmds = (macho_load_command<P>*)((uint8_t*)_mh + sizeof(macho_header<P>));
+    const uint32_t cmd_count = _mh->ncmds();
+    const macho_load_command<P>* cmd = cmds;
+    for (uint32_t i = 0; i < cmd_count; ++i) {
+        if ( cmd->cmd() == macho_segment_command<P>::CMD ) {
+            macho_segment_command<P>* seg = (macho_segment_command<P>*)cmd;
+            macho_section<P>* const sectionsStart = (macho_section<P>*)((char*)seg + sizeof(macho_segment_command<P>));
+            macho_section<P>* const sectionsEnd = &sectionsStart[seg->nsects()];
+            for(macho_section<P>* 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() ) {
+                                    _diagnostics.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<P>* sym = &symbolTable[symbolIndex];
+                                uint32_t stringOffset = sym->n_strx();
+                                if ( stringOffset > _symTabCmd->strsize() ) {
+                                    _diagnostics.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* 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]);
+                                lpVMAddr = (pint_t)sect->addr() + j * sizeof(pint_t);
+                                if ( symbolIndex >= _symTabCmd->nsyms() ) {
+                                    _diagnostics.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<P>* sym = &symbolTable[symbolIndex];
+                                uint32_t stringOffset = sym->n_strx();
+                                if ( stringOffset > _symTabCmd->strsize() ) {
+                                    _diagnostics.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) ) {
+                                    _diagnostics.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;
+                        }
+                    }
+                }
+            }
+        }
+        cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
+    }
+}
+
+
+template <typename P>
+void StubOptimizer<P>::forEachCallSiteToAStub(CallSiteHandler handler)
+{
+    if (_diagnostics.hasError())
+        return;
+    const uint8_t* infoStart = &_linkeditBias[_splitSegInfoCmd->dataoff()];
+    const uint8_t* infoEnd = &infoStart[_splitSegInfoCmd->datasize()];
+    if ( *infoStart++ != DYLD_CACHE_ADJ_V2_FORMAT ) {
+        _diagnostics.error("malformed split seg info in %s", _installName);
+        return;
+    }
+
+    uint8_t* textSectionContent = (uint8_t*)_cacheBuffer + _textSegCacheOffset + _textSection->addr() -_textSegStartAddr;
+
+    // Whole         :== <count> FromToSection+
+    // FromToSection :== <from-sect-index> <to-sect-index> <count> ToOffset+
+    // ToOffset         :== <to-sect-offset-delta> <count> FromOffset+
+    // FromOffset     :== <kind> <count> <from-sect-offset-delta>
+    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) {
+                    _diagnostics.error("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 <typename P>
+int32_t StubOptimizer<P>::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 <typename P>
+uint32_t StubOptimizer<P>::setDisplacementInThumbBranch(uint32_t instruction,  uint32_t instrAddr,
+                                                        int32_t displacement, bool targetIsThumb) {
+    if ( (displacement > 16777214) || (displacement < (-16777216)) ) {
+        _diagnostics.error("thumb branch out of range at 0x%0X in %s", instrAddr, _installName);
+        return 0;
+    }
+    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 ) {
+            _diagnostics.error("no pc-rel thumb branch instruction that switches to arm mode at 0x%0X in %s", instrAddr, _installName);
+            return 0;
+        }
+    }
+    else {
+        _diagnostics.error("not b/bl/blx at 0x%0X in %s", instrAddr, _installName);
+        return 0;
+    }
+    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 <typename P>
+void StubOptimizer<P>::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 ){
+                _diagnostics.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 ) {
+                _diagnostics.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);
+                if (_diagnostics.hasError())
+                    return false;
+                _branchesDirectCount++;
+                return true;
+            }
+        }
+        else if ( kind == DYLD_CACHE_ADJ_V2_ARM_BR24 ) {
+            // too few of these to be worth trying to optimize
+        }
+
+        return false;
+    });
+    if (_diagnostics.hasError())
+        return;
+}
+
+
+template <typename P>
+void StubOptimizer<P>::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 <typename P>
+void StubOptimizer<P>::optimizeArm64CallSites(std::vector<BranchPoolDylib<P>*>& 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 ) {
+            _diagnostics.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<P>* pool : branchIslandPools ) {
+                if ( (pool->addr() > callSiteAddr) && (pool->addr() < finalTargetAddr) ) {
+                    uint64_t brIslandAddr = pool->getForwardBranch(finalTargetAddr, targetName, branchIslandPools);
+                    if ( brIslandAddr == 0 ) {
+                        // branch island pool full
+                        _diagnostics.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<P>* 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
+                        _diagnostics.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;
+    });
+    if (_diagnostics.hasError())
+        return;
+}
+
+
+template <typename P>
+void StubOptimizer<P>::optimizeCallSites(std::vector<BranchPoolDylib<P>*>& branchIslandPools)
+{
+    if ( _textSection == NULL )
+        return;
+    if ( _stubSection == NULL )
+        return;
+
+
+    switch ( _mh->cputype() ) {
+        case CPU_TYPE_ARM64:
+            optimizeArm64CallSites(branchIslandPools);
+             if ( verbose ) {
+                _diagnostics.verbose("%5u branches in __text, %5u changed to direct branches, %5u changed to use islands for %s\n",
+                            _branchesCount, _branchesDirectCount, _branchesIslandCount, _installName);
+            }
+           break;
+        case CPU_TYPE_ARM:
+            optimizeArmCallSites();
+            optimizeArmStubs();
+            if ( verbose ) {
+                _diagnostics.verbose("%3u of %3u stubs optimized. %5u branches in __text, %5u changed to direct branches for %s\n",
+                            _stubOptimizedCount, _stubCount, _branchesCount, _branchesDirectCount, _installName);
+            }
+            break;
+    }
+}
+
+template <typename P>
+void bypassStubs(DyldSharedCache* cache, const std::string& archName, const std::vector<uint64_t>& branchPoolStartAddrs,
+                const char* const neverStubEliminateDylibs[], Diagnostics& diags)
+{
+    diags.verbose("Stub elimination optimization:\n");
+
+    // construct a StubOptimizer for each image
+    __block std::vector<StubOptimizer<P>*> optimizers;
+    cache->forEachImage(^(const mach_header* mh, const char* installName) {
+        optimizers.push_back(new StubOptimizer<P>((void*)cache, (macho_header<P>*)mh, diags));
+    });
+
+    // construct a BranchPoolDylib for each pool
+    std::vector<BranchPoolDylib<P>*> pools;
+
+    if ( startsWith(archName, "arm64") ) {
+        // Find hole at end of linkedit region for branch pool linkedits
+        __block uint64_t textRegionStartAddr = 0;
+        __block uint64_t linkEditRegionStartAddr = 0;
+        __block uint64_t linkEditRegionEndAddr = 0;
+        __block uint64_t linkEditRegionStartCacheOffset = 0;
+        cache->forEachRegion(^(const 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*)cache;
+            }
+        });
+        __block uint64_t lastLinkEditRegionUsedOffset = 0;
+        cache->forEachImage(^(const mach_header* mh, const char* installName) {
+            dyld3::MachOParser parser(mh);
+            parser.forEachSegment(^(const char* segName, uint32_t fileOffset, uint32_t fileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, bool& stop) {
+                if ( strcmp(segName, "__LINKEDIT") == 0 ) {
+                    if ( fileOffset >= lastLinkEditRegionUsedOffset )
+                        lastLinkEditRegionUsedOffset = fileOffset + vmSize;
+                }
+            });
+        });
+        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<P>(cache, poolAddr, textRegionStartAddr, poolLinkEditStartAddr, poolLinkEditStartOffset, diags));
+                poolLinkEditStartAddr += poolSize;
+                poolLinkEditStartOffset += poolSize;
+            }
+        }
+    }
+
+    // build set of functions to never stub-eliminate because tools may need to override them
+    std::unordered_set<std::string> neverStubEliminate;
+    for (const char** p=sNeverStubEliminateSymbols; *p != nullptr; ++p) {
+        neverStubEliminate.insert(*p);
+    }
+    for (const char* const* d=neverStubEliminateDylibs; *d != nullptr; ++d) {
+        for (StubOptimizer<P>* 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<ExportInfoTrie::Entry> exports;
+                if ( !ExportInfoTrie::parseTrie(exportsStart, exportsEnd, exports) ) {
+                    diags.error("malformed exports trie in %s", *d);
+                    return;
+                }
+                for(const ExportInfoTrie::Entry& entry : exports) {
+                    neverStubEliminate.insert(entry.name);
+               }
+            }
+        }
+    }
+
+    // build maps of stubs-to-lp and lp-to-target
+    for (StubOptimizer<P>* op : optimizers)
+        op->buildStubMap(neverStubEliminate);
+
+    // optimize call sites to by-pass stubs or jump through island
+    for (StubOptimizer<P>* op : optimizers)
+        op->optimizeCallSites(pools);
+
+   // final fix ups in branch pools
+    for (BranchPoolDylib<P>* pool : pools) {
+        pool->finalizeLoadCommands();
+        pool->printStats();
+    }
+
+    // write total optimization info
+    uint32_t callSiteCount = 0;
+    uint32_t callSiteDirectOptCount = 0;
+    uint32_t callSiteOneHopOptCount = 0;
+    for (StubOptimizer<P>* op : optimizers) {
+        callSiteCount           += op->_branchesCount;
+        callSiteDirectOptCount  += op->_branchesDirectCount;
+        callSiteOneHopOptCount  += op->_branchesIslandCount;
+    }
+    diags.verbose("  cache contains %u call sites of which %u were direct bound and %u were bound through islands\n", callSiteCount, callSiteDirectOptCount, callSiteOneHopOptCount);
+
+    // clean up
+    for (StubOptimizer<P>* op : optimizers)
+        delete op;
+   for (BranchPoolDylib<P>* p : pools)
+        delete p;
+
+}
+
+void bypassStubs(DyldSharedCache* cache, const std::vector<uint64_t>& branchPoolStartAddrs, const char* const neverStubEliminateDylibs[], Diagnostics& diags)
+{
+    std::string archName = cache->archName();
+    if ( startsWith(archName, "arm64") )
+        bypassStubs<Pointer64<LittleEndian>>(cache, archName, branchPoolStartAddrs, neverStubEliminateDylibs, diags);
+    else if ( archName == "armv7k" )
+        bypassStubs<Pointer32<LittleEndian>>(cache, archName, branchPoolStartAddrs, neverStubEliminateDylibs, diags);
+    // no stub optimization done for other arches
+}
+
+
+/*
+template <typename P>
+void StubOptimizer<P>::optimizeStubs(std::unordered_map<uint64_t,std::vector<uint64_t>>& 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 <typename P>
+void StubOptimizer<P>::bypassStubs(std::unordered_map<uint64_t,std::vector<uint64_t>>& 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<uint64_t>& 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/dyld3/shared-cache/OptimizerLinkedit.cpp b/dyld3/shared-cache/OptimizerLinkedit.cpp
new file mode 100644 (file)
index 0000000..84e2a65
--- /dev/null
@@ -0,0 +1,1180 @@
+/* -*- 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 <dirent.h>
+#include <sys/errno.h>
+#include <sys/fcntl.h>
+#include <mach-o/loader.h>
+#include <mach-o/fat.h>
+#include <assert.h>
+
+#include <fstream>
+#include <string>
+#include <algorithm>
+#include <unordered_map>
+#include <unordered_set>
+
+#include "MachOFileAbstraction.hpp"
+#include "Trie.hpp"
+#include "DyldSharedCache.h"
+#include "CacheBuilder.h"
+
+
+#define ALIGN_AS_TYPE(value, type) \
+        ((value + alignof(type) - 1) & (-alignof(type)))
+
+namespace {
+
+template <typename P>
+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<P>* symbolTable) {
+        // make sorted list of strings
+        std::vector<std::string> 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;
+    }
+
+    size_t size() {
+        size_t size = 1;
+        for (auto& entry : _map) {
+            size += (entry.first.size() + 1);
+        }
+        return size;
+    }
+
+
+private:
+    std::unordered_map<std::string, std::vector<uint32_t>> _map;
+};
+
+
+
+
+struct LocalSymbolInfo
+{
+    uint32_t    dylibOffset;
+    uint32_t    nlistStartIndex;
+    uint32_t    nlistCount;
+};
+
+
+template <typename P>
+class LinkeditOptimizer {
+public:
+                    LinkeditOptimizer(void* cacheBuffer, macho_header<P>* mh, Diagnostics& diag);
+
+    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<P>& stringPool, uint32_t& offset, uint32_t& symbolIndex);
+    void            copyImportedSymbols(uint8_t* newLinkEditContent, SortedStringPool<P>& stringPool, uint32_t& offset, uint32_t& symbolIndex);
+    void            copyLocalSymbols(uint8_t* newLinkEditContent, SortedStringPool<P>& stringPool, uint32_t& offset, uint32_t& symbolIndex,
+                                     bool redact, std::vector<LocalSymbolInfo>& localSymbolInfos,
+                                     std::vector<macho_nlist<P>>& unmappedLocalSymbols, SortedStringPool<P>& 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<P>*                        machHeader() { return _mh; }
+    const std::vector<const char*>          getDownwardDependents() { return _downDependentPaths; }
+    const std::vector<const char*>          getAllDependents() { return _allDependentPaths; }
+    const std::vector<const char*>          getReExportPaths() { return _reExportPaths; }
+    const std::vector<uint64_t>             initializerAddresses() { return _initializerAddresses; }
+    const std::vector<macho_section<P>*>    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<macho_segment_command<P>*>&  segCmds() { return _segCmds; }
+
+
+private:
+
+    typedef typename P::uint_t pint_t;
+    typedef typename P::E E;
+
+    macho_header<P>*                        _mh;
+    void*                                   _cacheBuffer;
+    Diagnostics&                            _diagnostics;
+    uint32_t                                _linkeditSize        = 0;
+    uint32_t                                _linkeditCacheOffset = 0;
+    uint64_t                                _linkeditAddr        = 0;
+    const uint8_t*                          _linkeditBias       = nullptr;
+    const char*                             _installName        = nullptr;
+    macho_symtab_command<P>*                _symTabCmd          = nullptr;
+    macho_dysymtab_command<P>*              _dynSymTabCmd       = nullptr;
+    macho_dyld_info_command<P>*             _dyldInfo           = nullptr;
+    macho_linkedit_data_command<P>*         _functionStartsCmd  = nullptr;
+    macho_linkedit_data_command<P>*         _dataInCodeCmd      = nullptr;
+    std::vector<macho_segment_command<P>*>  _segCmds;
+    std::unordered_map<uint32_t,uint32_t>   _oldToNewSymbolIndexes;
+    std::vector<const char*>                _reExportPaths;
+    std::vector<const char*>                _downDependentPaths;
+    std::vector<const char*>                _allDependentPaths;
+    std::vector<uint64_t>                   _initializerAddresses;
+    std::vector<macho_section<P>*>          _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 <typename P>
+class AcceleratorTables {
+public:
+                AcceleratorTables(DyldSharedCache* cache, uint64_t linkeditStartAddr, Diagnostics& diag, const std::vector<LinkeditOptimizer<P>*>& optimizers);
+
+    uint32_t    totalSize() const;
+    void        copyTo(uint8_t* buffer);
+
+private:
+    typedef typename P::E  E;
+
+    struct NodeChain;
+
+    struct DepNode {
+        std::vector<DepNode*>   _dependents;
+        unsigned                _depth;
+        const char*             _installName;
+
+                                DepNode() : _depth(0), _installName(nullptr) { }
+        void                    computeDepth();
+        static void             verifyUnreachable(DepNode* target, NodeChain& chain, Diagnostics& diag, std::unordered_set<DepNode*>& visitedNodes, const std::vector<DepNode*>& from);
+    };
+
+    struct NodeChain {
+        NodeChain*   prev;
+        DepNode*     node;
+    };
+
+    std::unordered_map<macho_header<P>*, DepNode>                   _depDAG;
+    std::vector<dyld_cache_image_info_extra>                        _extraInfo;
+    std::vector<uint8_t>                                            _trieBytes;
+    std::vector<uint16_t>                                           _reExportArray;
+    std::vector<uint16_t>                                           _dependencyArray;
+    std::vector<uint16_t>                                           _bottomUpArray;
+    std::vector<dyld_cache_accelerator_initializer>                 _initializers;
+    std::vector<dyld_cache_accelerator_dof>                         _dofSections;
+    std::vector<dyld_cache_range_entry>                             _rangeTable;
+    std::unordered_map<macho_header<P>*, uint32_t>                  _machHeaderToImageIndex;
+    std::unordered_map<std::string, macho_header<P>*>               _dylibPathToMachHeader;
+    std::unordered_map<macho_header<P>*, LinkeditOptimizer<P>*>     _machHeaderToOptimizer;
+    dyld_cache_accelerator_info                                     _acceleratorInfoHeader;
+};
+
+
+template <typename P>
+void AcceleratorTables<P>::AcceleratorTables::DepNode::verifyUnreachable(AcceleratorTables<P>::DepNode* target, struct AcceleratorTables<P>::NodeChain& chain, Diagnostics& diag,
+                                                                        std::unordered_set<DepNode*>& visitedNodes, const std::vector<AcceleratorTables<P>::DepNode*>& from) {
+    for (DepNode* node : from) {
+        bool foundCycle = (node == target);
+        for (NodeChain* c = &chain; c->prev != nullptr; c = c->prev) {
+            if ( c->node == target ) {
+                foundCycle = true;
+                break;
+            }
+        }
+        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;
+            }
+            diag.warning("%s", msg.c_str());
+            return;
+        }
+
+        if ( visitedNodes.count(node) )
+            continue;
+        visitedNodes.insert(node);
+        NodeChain nextChain;
+        nextChain.prev = &chain;
+        nextChain.node = node;
+        verifyUnreachable(target, nextChain, diag, visitedNodes, node->_dependents);
+    }
+}
+
+const uint16_t kBranchIslandDylibIndex = 0x7FFF;
+
+template <typename P>
+AcceleratorTables<P>::AcceleratorTables(DyldSharedCache* cache, uint64_t linkeditStartAddr, Diagnostics& diag, const std::vector<LinkeditOptimizer<P>*>& optimizers)
+{
+    // build table mapping tables to map between mach_header, index, and optimizer
+    for ( LinkeditOptimizer<P>* op : optimizers ) {
+        _machHeaderToOptimizer[op->machHeader()] = op;
+    }
+    const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((uint8_t*)cache + cache->header.mappingOffset);
+    uint64_t cacheStartAddress = mappings[0].address;
+    const dyld_cache_image_info* images = (dyld_cache_image_info*)((uint8_t*)cache + cache->header.imagesOffset);
+    for (unsigned i=0; i < cache->header.imagesCount; ++i) {
+        uint64_t segCacheFileOffset = images[i].address - cacheStartAddress;
+        macho_header<P>* mhMapped = (macho_header<P>*)((uint8_t*)cache+segCacheFileOffset);
+        const char* path = (char*)cache + 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;
+    }
+
+
+    // build DAG of image dependencies
+    for (LinkeditOptimizer<P>* op : optimizers) {
+        _depDAG[op->machHeader()]._installName = op->installName();
+    }
+    for (LinkeditOptimizer<P>* op : optimizers) {
+        DepNode& node = _depDAG[op->machHeader()];
+        for (const char* depPath : op->getDownwardDependents()) {
+            macho_header<P>* 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<DepNode*> visitedNodes;
+        DepNode::verifyUnreachable(node, chain, diag, 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<macho_header<P>*> sortedMachHeaders;
+    sortedMachHeaders.reserve(optimizers.size());
+    for (LinkeditOptimizer<P>* 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<P>* lmh, macho_header<P>* 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
+    dyld_cache_image_info_extra emptyExtra;
+    emptyExtra.exportsTrieAddr              = 0;
+    emptyExtra.weakBindingsAddr             = 0;
+    emptyExtra.exportsTrieSize              = 0;
+    emptyExtra.weakBindingsSize             = 0;
+    emptyExtra.dependentsStartArrayIndex    = 0;
+    emptyExtra.reExportsStartArrayIndex     = 0;
+    _extraInfo.insert(_extraInfo.begin(), sortedMachHeaders.size(), emptyExtra);
+
+    //for ( macho_header<P>* 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<P>* mh : sortedMachHeaders) {
+        LinkeditOptimizer<P>* op = _machHeaderToOptimizer[mh];
+        unsigned index = _machHeaderToImageIndex[mh];
+        auto depPaths = op->getAllDependents();
+        if ( depPaths.empty() ) {
+            _extraInfo[index].dependentsStartArrayIndex = 0;
+        }
+        else {
+            _extraInfo[index].dependentsStartArrayIndex = (uint32_t)_dependencyArray.size();
+            auto downPaths = op->getDownwardDependents();
+            for (const char* depPath : depPaths) {
+                macho_header<P>* 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<P>* mh : sortedMachHeaders) {
+        LinkeditOptimizer<P>* op = _machHeaderToOptimizer[mh];
+        unsigned index = _machHeaderToImageIndex[mh];
+        auto reExPaths = op->getReExportPaths();
+        if ( reExPaths.empty() ) {
+            _extraInfo[index].reExportsStartArrayIndex = 0;
+        }
+        else {
+            _extraInfo[index].reExportsStartArrayIndex = (uint32_t)_reExportArray.size();
+            for (const char* reExPath : reExPaths) {
+                macho_header<P>* 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<P>* mh : sortedMachHeaders) {
+        LinkeditOptimizer<P>* 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());
+            dyld_cache_accelerator_initializer entry;
+            entry.functionOffset = (uint32_t)(initializer-cacheStartAddress);
+            entry.imageIndex = _machHeaderToImageIndex[mh];
+            _initializers.push_back(entry);
+        }
+    }
+
+    // build ordered list of DOF sections
+    for (macho_header<P>* mh : sortedMachHeaders) {
+        LinkeditOptimizer<P>* op = _machHeaderToOptimizer[mh];
+        assert(op != NULL);
+        unsigned imageIndex = _machHeaderToImageIndex[mh];
+        for (auto& sect : op->dofSections()) {
+            //fprintf(stderr, "0x%08llX %s\n", initializer, op->installName());
+            dyld_cache_accelerator_dof entry;
+            entry.sectionAddress = sect->addr();
+            entry.sectionSize    = (uint32_t)sect->size();
+            entry.imageIndex     = imageIndex;
+            _dofSections.push_back(entry);
+        }
+    }
+
+    // register exports trie and weak binding info in each dylib with image extra info
+    for (macho_header<P>* mh : sortedMachHeaders) {
+        LinkeditOptimizer<P>* op = _machHeaderToOptimizer[mh];
+        unsigned index = _machHeaderToImageIndex[mh];
+        _extraInfo[index].exportsTrieAddr  = op->exportsTrieLinkEditOffset() + linkeditStartAddr;
+        _extraInfo[index].exportsTrieSize  = op->exportsTrieLinkEditSize();
+        _extraInfo[index].weakBindingsAddr = op->weakBindingLinkEditOffset() + linkeditStartAddr;
+        _extraInfo[index].weakBindingsSize = op->weakBindingLinkEditSize();
+    }
+
+    // record location of __DATA/__dyld section in libdyld.dylib
+    macho_header<P>* libdyldMH = _dylibPathToMachHeader["/usr/lib/system/libdyld.dylib"];
+    LinkeditOptimizer<P>* libdyldOp = _machHeaderToOptimizer[libdyldMH];
+    uint64_t dyldSectionAddr = libdyldOp->dyldSectionAddress();
+
+    // build range table for fast address->image lookups
+    for (macho_header<P>* mh : sortedMachHeaders) {
+        LinkeditOptimizer<P>* op = _machHeaderToOptimizer[mh];
+        unsigned imageIndex = _machHeaderToImageIndex[mh];
+        for (const macho_segment_command<P>* segCmd : op->segCmds()) {
+            if ( strcmp(segCmd->segname(), "__LINKEDIT") == 0 )
+                continue;
+            dyld_cache_range_entry entry;
+            entry.startAddress = segCmd->vmaddr();
+            entry.size         = (uint32_t)segCmd->vmsize();
+            entry.imageIndex   = imageIndex;
+            _rangeTable.push_back(entry);
+        }
+    }
+    std::sort(_rangeTable.begin(), _rangeTable.end(),
+              [&](const dyld_cache_range_entry& lRange, const dyld_cache_range_entry& rRange) -> bool {
+                return (lRange.startAddress < rRange.startAddress);
+              });
+
+    // build trie that maps install names to image index
+    std::vector<DylibIndexTrie::Entry> 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
+    _acceleratorInfoHeader.version              = 1;
+    _acceleratorInfoHeader.imageExtrasCount     = (uint32_t)_extraInfo.size();
+    _acceleratorInfoHeader.imagesExtrasOffset   = ALIGN_AS_TYPE(sizeof(dyld_cache_accelerator_info), dyld_cache_image_info_extra);
+    _acceleratorInfoHeader.bottomUpListOffset   = _acceleratorInfoHeader.imagesExtrasOffset + _acceleratorInfoHeader.imageExtrasCount*sizeof(dyld_cache_image_info_extra);
+    _acceleratorInfoHeader.dylibTrieOffset      = _acceleratorInfoHeader.bottomUpListOffset + _acceleratorInfoHeader.imageExtrasCount*sizeof(uint16_t);
+    _acceleratorInfoHeader.dylibTrieSize        = (uint32_t)_trieBytes.size();
+    _acceleratorInfoHeader.initializersOffset   = ALIGN_AS_TYPE(_acceleratorInfoHeader.dylibTrieOffset + _acceleratorInfoHeader.dylibTrieSize, dyld_cache_accelerator_initializer);
+    _acceleratorInfoHeader.initializersCount    = (uint32_t)_initializers.size();
+    _acceleratorInfoHeader.dofSectionsOffset    = ALIGN_AS_TYPE(_acceleratorInfoHeader.initializersOffset + _acceleratorInfoHeader.initializersCount*sizeof(dyld_cache_accelerator_initializer), dyld_cache_accelerator_initializer);
+    _acceleratorInfoHeader.dofSectionsCount     = (uint32_t)_dofSections.size();
+    _acceleratorInfoHeader.reExportListOffset   = ALIGN_AS_TYPE(_acceleratorInfoHeader.dofSectionsOffset + _acceleratorInfoHeader.dofSectionsCount*sizeof(dyld_cache_accelerator_dof), dyld_cache_accelerator_dof);
+    _acceleratorInfoHeader.reExportCount        = (uint32_t)_reExportArray.size();
+    _acceleratorInfoHeader.depListOffset        = ALIGN_AS_TYPE(_acceleratorInfoHeader.reExportListOffset + _acceleratorInfoHeader.reExportCount*sizeof(uint16_t), uint16_t);
+    _acceleratorInfoHeader.depListCount         = (uint32_t)_dependencyArray.size();
+    _acceleratorInfoHeader.rangeTableOffset     = ALIGN_AS_TYPE(_acceleratorInfoHeader.depListOffset + _acceleratorInfoHeader.depListCount*sizeof(uint16_t), dyld_cache_range_entry);
+    _acceleratorInfoHeader.rangeTableCount      = (uint32_t)_rangeTable.size();
+    _acceleratorInfoHeader.dyldSectionAddr      = dyldSectionAddr;
+}
+
+
+template <typename P>
+void AcceleratorTables<P>::DepNode::computeDepth()
+{
+    if ( _depth != 0 )
+        return;
+    _depth = 1;
+    for (DepNode* node : _dependents) {
+        node->computeDepth();
+        if ( node->_depth >= _depth )
+            _depth = node->_depth + 1;
+    }
+}
+
+template <typename P>
+uint32_t AcceleratorTables<P>::totalSize() const
+{
+    return (uint32_t)align(_acceleratorInfoHeader.rangeTableOffset + _acceleratorInfoHeader.rangeTableCount*sizeof(dyld_cache_range_entry), 14);
+}
+
+template <typename P>
+void AcceleratorTables<P>::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 <typename P>
+LinkeditOptimizer<P>::LinkeditOptimizer(void* cacheBuffer, macho_header<P>* mh, Diagnostics& diag)
+: _mh(mh), _cacheBuffer(cacheBuffer), _diagnostics(diag)
+{
+    _linkeditBias = (uint8_t*)cacheBuffer;
+    const unsigned origLoadCommandsSize = mh->sizeofcmds();
+    unsigned bytesRemaining = origLoadCommandsSize;
+    unsigned removedCount = 0;
+    const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)mh + sizeof(macho_header<P>));
+    const uint32_t cmdCount = mh->ncmds();
+    const macho_load_command<P>* cmd = cmds;
+    const macho_dylib_command<P>* dylibCmd;
+    const macho_routines_command<P>* routinesCmd;
+    macho_segment_command<P>* segCmd;
+    for (uint32_t i = 0; i < cmdCount; ++i) {
+        bool remove = false;
+        switch (cmd->cmd()) {
+            case LC_ID_DYLIB:
+                _installName = ((macho_dylib_command<P>*)cmd)->name();
+                break;
+            case LC_SYMTAB:
+                _symTabCmd = (macho_symtab_command<P>*)cmd;
+                break;
+            case LC_DYSYMTAB:
+                _dynSymTabCmd = (macho_dysymtab_command<P>*)cmd;
+                break;
+            case LC_DYLD_INFO:
+            case LC_DYLD_INFO_ONLY:
+                _dyldInfo = (macho_dyld_info_command<P>*)cmd;
+                _exportInfoSize = _dyldInfo->export_size();
+                break;
+            case LC_FUNCTION_STARTS:
+                _functionStartsCmd = (macho_linkedit_data_command<P>*)cmd;
+                break;
+            case LC_DATA_IN_CODE:
+                _dataInCodeCmd = (macho_linkedit_data_command<P>*)cmd;
+                break;
+            case LC_ROUTINES:
+            case LC_ROUTINES_64:
+                routinesCmd = (macho_routines_command<P>*)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<P>*)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<P>::CMD:
+                segCmd = (macho_segment_command<P>*)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<P>* const sectionsStart = (macho_section<P>*)((uint8_t*)segCmd + sizeof(macho_segment_command<P>));
+                    macho_section<P>* const sectionsEnd = &sectionsStart[segCmd->nsects()];
+                    for (macho_section<P>* 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<P>* nextCmd = (macho_load_command<P>*)(((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 <typename P>
+void LinkeditOptimizer<P>::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<P>* 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: <rdar://problem/20357466> 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<P>));
+    _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 <typename P>
+void LinkeditOptimizer<P>::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 <typename P>
+void LinkeditOptimizer<P>::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 <typename P>
+void LinkeditOptimizer<P>::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 <typename P>
+void LinkeditOptimizer<P>::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 <typename P>
+void LinkeditOptimizer<P>::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 <typename P>
+void LinkeditOptimizer<P>::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 <typename P>
+void LinkeditOptimizer<P>::copyLocalSymbols(uint8_t* newLinkEditContent, SortedStringPool<P>& stringPool, uint32_t& offset, uint32_t& symbolIndex,
+                                            bool redact, std::vector<LocalSymbolInfo>& localSymbolInfos,
+                                            std::vector<macho_nlist<P>>& unmappedLocalSymbols, SortedStringPool<P>& 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<P>* const symbolTable = (macho_nlist<P>*)(&_linkeditBias[_symTabCmd->symoff()]);
+    const macho_nlist<P>* const firstExport = &symbolTable[_dynSymTabCmd->ilocalsym()];
+    const macho_nlist<P>* const lastExport  = &symbolTable[_dynSymTabCmd->ilocalsym()+_dynSymTabCmd->nlocalsym()];
+    for (const macho_nlist<P>* 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<P>* newSymbolEntry = (macho_nlist<P>*)&newLinkEditContent[offset];
+        *newSymbolEntry = *entry;
+        if ( redact ) {
+            // if removing local symbols, change __text symbols to "<redacted>" so backtraces don't have bogus names
+            if ( entry->n_sect() == 1 ) {
+                stringPool.add(symbolIndex, "<redacted>");
+                ++symbolIndex;
+                offset += sizeof(macho_nlist<P>);
+            }
+            // 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<P>);
+       }
+    }
+    _newLocalSymbolCount = symbolIndex - _newLocalSymbolsStartIndex;
+    localInfo.nlistCount = (uint32_t)unmappedLocalSymbols.size() - localInfo.nlistStartIndex;
+    localSymbolInfos.push_back(localInfo);
+}
+
+
+template <typename P>
+void LinkeditOptimizer<P>::copyExportedSymbols(uint8_t* newLinkEditContent, SortedStringPool<P>& stringPool, uint32_t& offset, uint32_t& symbolIndex)
+{
+    _newExportedSymbolsStartIndex = symbolIndex;
+    const char* strings = (char*)&_linkeditBias[_symTabCmd->stroff()];
+    const macho_nlist<P>* const symbolTable = (macho_nlist<P>*)(&_linkeditBias[_symTabCmd->symoff()]);
+    const macho_nlist<P>* const firstExport = &symbolTable[_dynSymTabCmd->iextdefsym()];
+    const macho_nlist<P>* const lastExport  = &symbolTable[_dynSymTabCmd->iextdefsym()+_dynSymTabCmd->nextdefsym()];
+    uint32_t oldSymbolIndex = _dynSymTabCmd->iextdefsym();
+    for (const macho_nlist<P>* 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<P>* newSymbolEntry = (macho_nlist<P>*)&newLinkEditContent[offset];
+        *newSymbolEntry = *entry;
+        newSymbolEntry->set_n_strx(0);
+        stringPool.add(symbolIndex, name);
+        _oldToNewSymbolIndexes[oldSymbolIndex] = symbolIndex - _newLocalSymbolsStartIndex;
+        ++symbolIndex;
+        offset += sizeof(macho_nlist<P>);
+    }
+    _newExportedSymbolCount = symbolIndex - _newExportedSymbolsStartIndex;
+}
+
+template <typename P>
+void LinkeditOptimizer<P>::copyImportedSymbols(uint8_t* newLinkEditContent, SortedStringPool<P>& stringPool, uint32_t& offset, uint32_t& symbolIndex)
+{
+    _newImportedSymbolsStartIndex = symbolIndex;
+    const char* strings = (char*)&_linkeditBias[_symTabCmd->stroff()];
+    const macho_nlist<P>* const symbolTable = (macho_nlist<P>*)(&_linkeditBias[_symTabCmd->symoff()]);
+    const macho_nlist<P>* const firstImport = &symbolTable[_dynSymTabCmd->iundefsym()];
+    const macho_nlist<P>* const lastImport  = &symbolTable[_dynSymTabCmd->iundefsym()+_dynSymTabCmd->nundefsym()];
+    uint32_t oldSymbolIndex = _dynSymTabCmd->iundefsym();
+    for (const macho_nlist<P>* entry = firstImport; entry < lastImport; ++entry, ++oldSymbolIndex) {
+        if ( (entry->n_type() & N_TYPE) != N_UNDF)
+            continue;
+        const char* name = &strings[entry->n_strx()];
+        macho_nlist<P>* newSymbolEntry = (macho_nlist<P>*)&newLinkEditContent[offset];
+        *newSymbolEntry = *entry;
+        newSymbolEntry->set_n_strx(0);
+        stringPool.add(symbolIndex, name);
+        _oldToNewSymbolIndexes[oldSymbolIndex] = symbolIndex - _newLocalSymbolsStartIndex;
+        ++symbolIndex;
+        offset += sizeof(macho_nlist<P>);
+    }
+    _newImportedSymbolCount = symbolIndex - _newImportedSymbolsStartIndex;
+}
+
+template <typename P>
+void LinkeditOptimizer<P>::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 <typename P>
+uint64_t mergeLinkedits(DyldSharedCache* cache, bool dontMapLocalSymbols, bool addAcceleratorTables, std::vector<LinkeditOptimizer<P>*>& optimizers, Diagnostics& diagnostics, dyld_cache_local_symbols_info** localsInfo)
+{
+    // allocate space for new linkedit data
+    uint32_t linkeditStartOffset = 0xFFFFFFFF;
+    uint32_t linkeditEndOffset = 0;
+    uint64_t linkeditStartAddr = 0;
+    for (LinkeditOptimizer<P>* 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<P> stringPool;
+    uint32_t offset = 0;
+
+    diagnostics.verbose("Merged LINKEDIT:\n");
+
+    // copy weak binding info
+    uint32_t startWeakBindInfosOffset = offset;
+    for (LinkeditOptimizer<P>* op : optimizers) {
+        op->copyWeakBindingInfo(newLinkEdit, offset);
+    }
+    diagnostics.verbose("  weak bindings size:      %5uKB\n", (uint32_t)(offset-startWeakBindInfosOffset)/1024);
+
+    // copy export info
+    uint32_t startExportInfosOffset = offset;
+    for (LinkeditOptimizer<P>* op : optimizers) {
+        op->copyExportInfo(newLinkEdit, offset);
+    }
+    diagnostics.verbose("  exports info size:       %5uKB\n", (uint32_t)(offset-startExportInfosOffset)/1024);
+
+    // in theory, an optimized cache can drop the binding info
+    if ( true ) {
+        // copy binding info
+        uint32_t startBindingsInfosOffset = offset;
+        for (LinkeditOptimizer<P>* op : optimizers) {
+            op->copyBindingInfo(newLinkEdit, offset);
+        }
+        diagnostics.verbose("  bindings size:           %5uKB\n", (uint32_t)(offset-startBindingsInfosOffset)/1024);
+
+       // copy lazy binding info
+        uint32_t startLazyBindingsInfosOffset = offset;
+        for (LinkeditOptimizer<P>* op : optimizers) {
+            op->copyLazyBindingInfo(newLinkEdit, offset);
+        }
+        diagnostics.verbose("  lazy bindings size:      %5uKB\n", (offset-startLazyBindingsInfosOffset)/1024);
+    }
+
+    // copy symbol table entries
+    std::vector<macho_nlist<P>> unmappedLocalSymbols;
+    if ( dontMapLocalSymbols )
+        unmappedLocalSymbols.reserve(0x01000000);
+    std::vector<LocalSymbolInfo> localSymbolInfos;
+        localSymbolInfos.reserve(optimizers.size());
+    SortedStringPool<P> localSymbolsStringPool;
+    uint32_t symbolIndex = 0;
+    const uint32_t sharedSymbolTableStartOffset = offset;
+    uint32_t sharedSymbolTableExportsCount = 0;
+    uint32_t sharedSymbolTableImportsCount = 0;
+    for (LinkeditOptimizer<P>* 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<P>* op : optimizers) {
+        op->copyFunctionStarts(newLinkEdit, offset);
+    }
+    diagnostics.verbose("  function starts size:    %5uKB\n", (offset-startFunctionStartsOffset)/1024);
+
+    // copy data-in-code info
+    uint32_t startDataInCodeOffset = offset;
+    for (LinkeditOptimizer<P>* op : optimizers) {
+        op->copyDataInCode(newLinkEdit, offset);
+    }
+    diagnostics.verbose("  data in code size:       %5uKB\n", (offset-startDataInCodeOffset)/1024);
+
+    // copy indirect symbol tables
+    for (LinkeditOptimizer<P>* 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<P>*)&newLinkEdit[sharedSymbolTableStartOffset]);
+    offset += sharedSymbolStringsSize;
+    uint32_t newLinkeditUnalignedSize = offset;
+    uint64_t newLinkeditEnd = align(linkeditStartOffset+newLinkeditUnalignedSize, 14);
+    diagnostics.verbose("  symbol table size:       %5uKB (%d exports, %d imports)\n", (sharedSymbolTableEndOffset-sharedSymbolTableStartOffset)/1024, sharedSymbolTableExportsCount, sharedSymbolTableImportsCount);
+    diagnostics.verbose("  symbol string pool size: %5uKB\n", sharedSymbolStringsSize/1024);
+
+    // overwrite mapped LINKEDIT area in cache with new merged LINKEDIT content
+    diagnostics.verbose("LINKEDITS optimized from %uMB to %uMB\n", (uint32_t)totalUnoptLinkeditsSize/(1024*1024), (uint32_t)newLinkeditUnalignedSize/(1024*1024));
+    ::memcpy((char*)cache + linkeditStartOffset, newLinkEdit, newLinkeditUnalignedSize);
+    ::bzero((char*)cache + linkeditStartOffset+newLinkeditUnalignedSize, totalUnoptLinkeditsSize-newLinkeditUnalignedSize);
+    ::free(newLinkEdit);
+
+    // If making cache for customers, add extra accelerator tables for dyld
+    if ( addAcceleratorTables ) {
+        AcceleratorTables<P> tables(cache, linkeditStartAddr, diagnostics, optimizers);
+        uint32_t tablesSize = tables.totalSize();
+        if ( tablesSize < (totalUnoptLinkeditsSize-newLinkeditUnalignedSize) ) {
+            tables.copyTo((uint8_t*)cache+newLinkeditEnd);
+            newLinkeditEnd += tablesSize;
+            uint64_t accelInfoAddr = align(linkeditStartAddr + newLinkeditUnalignedSize, 14);
+            cache->header.accelerateInfoAddr = accelInfoAddr;
+            cache->header.accelerateInfoSize = tablesSize;
+            diagnostics.verbose("Accelerator tables %uMB\n", (uint32_t)tablesSize/(1024*1024));
+       }
+        else {
+            diagnostics.warning("not enough room to add dyld accelerator tables");
+        }
+    }
+
+    // update mapping to reduce linkedit size
+    dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((char*)cache + cache->header.mappingOffset);
+    mappings[2].size = newLinkeditEnd - mappings[2].fileOffset;
+
+    // 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;
+        const uint32_t entriesOffset = sizeof(dyld_cache_local_symbols_info);
+        const uint32_t entriesCount  = (uint32_t)localSymbolInfos.size();
+        const uint32_t nlistOffset   = (uint32_t)align(entriesOffset + entriesCount * sizeof(dyld_cache_local_symbols_info), 4); // 16-byte align start
+        const uint32_t nlistCount    = (uint32_t)unmappedLocalSymbols.size();
+        const uint32_t stringsSize   = (uint32_t)localSymbolsStringPool.size();
+        const uint32_t stringsOffset = nlistOffset + nlistCount * sizeof(macho_nlist<P>);
+        // allocate buffer for local symbols
+        const size_t localsBufferSize = align(stringsOffset + stringsSize, 14);
+        dyld_cache_local_symbols_info* infoHeader = (dyld_cache_local_symbols_info*)malloc(localsBufferSize);
+        // fill in header info
+        infoHeader->nlistOffset       = nlistOffset;
+        infoHeader->nlistCount        = nlistCount;
+        infoHeader->stringsOffset     = stringsOffset;
+        infoHeader->stringsSize       = stringsSize;
+        infoHeader->entriesOffset     = entriesOffset;
+        infoHeader->entriesCount      = entriesCount;
+        // copy info for each dylib
+        dyld_cache_local_symbols_entry* entries = (dyld_cache_local_symbols_entry*)(((uint8_t*)infoHeader)+entriesOffset);
+        for (int i=0; i < entriesCount; ++i) {
+            entries[i].dylibOffset        = localSymbolInfos[i].dylibOffset;
+            entries[i].nlistStartIndex    = localSymbolInfos[i].nlistStartIndex;
+            entries[i].nlistCount         = localSymbolInfos[i].nlistCount;
+        }
+        // copy nlists
+        macho_nlist<P>* newLocalsSymbolTable = (macho_nlist<P>*)(((uint8_t*)infoHeader)+nlistOffset);
+        ::memcpy(newLocalsSymbolTable, &unmappedLocalSymbols[0], nlistCount*sizeof(macho_nlist<P>));
+        // copy string pool
+        localSymbolsStringPool.copyPoolAndUpdateOffsets(((char*)infoHeader)+stringsOffset, newLocalsSymbolTable);
+        // return buffer of local symbols, caller to free() it
+        *localsInfo = infoHeader;
+    }
+
+    // update all load commands to new merged layout
+    for (LinkeditOptimizer<P>* op : optimizers) {
+        op->updateLoadCommands(linkeditStartOffset, linkeditStartAddr, newLinkeditEnd-linkeditStartOffset,
+                               sharedSymbolTableStartOffset, sharedSymbolTableCount,
+                               sharedSymbolStringsOffset, sharedSymbolStringsSize);
+    }
+
+    return newFileSize;
+}
+
+} // anonymous namespace
+
+template <typename P>
+uint64_t optimizeLinkedit(DyldSharedCache* cache, bool dontMapLocalSymbols, bool addAcceleratorTables, const std::vector<uint64_t>& branchPoolOffsets, Diagnostics& diag, dyld_cache_local_symbols_info** localsInfo)
+{
+    // construct a LinkeditOptimizer for each image
+    __block std::vector<LinkeditOptimizer<P>*> optimizers;
+    cache->forEachImage(^(const mach_header* mh, const char*) {
+        optimizers.push_back(new LinkeditOptimizer<P>(cache, (macho_header<P>*)mh, diag));
+    });
+#if 0
+    // add optimizer for each branch pool
+    for (uint64_t poolOffset : branchPoolOffsets) {
+        macho_header<P>* mh =  (macho_header<P>*)((char*)cache + poolOffset);
+        optimizers.push_back(new LinkeditOptimizer<P>(cache, mh, diag));
+    }
+#endif
+    // merge linkedit info
+    uint64_t newFileSize = mergeLinkedits(cache, dontMapLocalSymbols, addAcceleratorTables, optimizers, diag, localsInfo);
+
+    // delete optimizers
+    for (LinkeditOptimizer<P>* op : optimizers)
+        delete op;
+
+    return newFileSize;
+}
+
+uint64_t optimizeLinkedit(DyldSharedCache* cache, bool is64, bool dontMapLocalSymbols, bool addAcceleratorTables, const std::vector<uint64_t>& branchPoolOffsets, Diagnostics& diag, dyld_cache_local_symbols_info** localsInfo)
+{
+    if ( is64) {
+        return optimizeLinkedit<Pointer64<LittleEndian>>(cache, dontMapLocalSymbols, addAcceleratorTables, branchPoolOffsets, diag, localsInfo);
+    }
+    else {
+        return optimizeLinkedit<Pointer32<LittleEndian>>(cache, dontMapLocalSymbols, addAcceleratorTables, branchPoolOffsets, diag, localsInfo);
+    }
+}
+
+
+
diff --git a/dyld3/shared-cache/OptimizerObjC.cpp b/dyld3/shared-cache/OptimizerObjC.cpp
new file mode 100644 (file)
index 0000000..71fe9dc
--- /dev/null
@@ -0,0 +1,820 @@
+/* -*- 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 <dirent.h>
+#include <sys/errno.h>
+#include <sys/fcntl.h>
+#include <mach-o/loader.h>
+#include <mach-o/fat.h>
+#include <assert.h>
+
+#include "DyldSharedCache.h"
+#include "Diagnostics.h"
+#include "CacheBuilder.h"
+#include "FileAbstraction.hpp"
+#include "MachOFileAbstraction.hpp"
+
+
+// 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(const DyldSharedCache* cache, Diagnostics& diag)
+        : _diagnostics(diag)
+    {
+        __block int index = 0;
+        cache->forEachRegion(^(const void* content, uint64_t vmAddr, uint64_t size, uint32_t permissions) {
+            _regions[index++] = { (uint8_t*)content, (uint8_t*)content+size, vmAddr, vmAddr+size };
+        });
+    }
+
+    void* contentForVMAddr(uint64_t vmaddr) {
+        for (const Info& info : _regions) {
+            if ( (info.startAddr <= vmaddr) && (vmaddr < info.endAddr) )
+                return (void*)(info.contentStart + vmaddr - info.startAddr);
+        }
+        if ( vmaddr != 0 )
+            _diagnostics.error("invalid vmaddr 0x%0llX in ObjC data", vmaddr);
+        return nullptr;
+    }
+
+    uint64_t vmAddrForContent(const void* content) {
+        for (const Info& info : _regions) {
+            if ( (info.contentStart <= content) && (content < info.contentEnd) )
+                return info.startAddr + ((uint8_t*)content - (uint8_t*)info.contentStart);
+        }
+        _diagnostics.error("invalid content pointer %p in ObjC data", content);
+        return 0;
+    }
+
+    Diagnostics& diagnostics() { return _diagnostics; }
+
+private:
+    struct Info { uint8_t* contentStart; uint8_t* contentEnd; uint64_t startAddr; uint64_t endAddr; };
+    Diagnostics&    _diagnostics;
+    Info            _regions[3];
+};
+
+
+// Access a section containing a list of pointers
+template <typename P, typename T>
+class PointerSection 
+{
+    typedef typename P::uint_t   pint_t;
+public:
+    PointerSection(ContentAccessor* cache, const macho_header<P>* 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 ) {
+            _cache->diagnostics().error("index out of range in section %s", _section->sectname());
+            return 0;
+        }
+        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 ) {
+            _cache->diagnostics().error("index out of range in section %s", _section->sectname());
+            return;
+        }
+        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<macho_section<P>*>(_section)->set_size(_count * sizeof(pint_t));
+    }
+
+private:
+    ContentAccessor* const         _cache;
+    const macho_section<P>* const  _section;
+    pint_t* const                  _base;
+    pint_t const                   _count;
+};
+
+
+// Access a section containing an array of structures
+template <typename P, typename T>
+class ArraySection 
+{
+public:
+    ArraySection(ContentAccessor* cache, const macho_header<P>* 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) {
+            _cache->diagnostics().error("index out of range in section %s", _section->sectname());
+        }
+        return _base[index];
+    }
+
+private:
+    ContentAccessor* const         _cache;
+    const macho_section<P>* 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 <typename P>
+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 <typename P>
+class ClassListBuilder
+{
+private:
+    objc_opt::string_map    _classNames;
+    objc_opt::class_map     _classes;
+    size_t                  _count = 0;
+    HeaderInfoOptimizer<P, objc_header_info_ro_t<P>>& _hInfos;
+
+public:
+
+    ClassListBuilder(HeaderInfoOptimizer<P, objc_header_info_ro_t<P>>& hinfos) : _hInfos(hinfos) { }
+
+    void visitClass(ContentAccessor* cache,
+                    const macho_header<P>* header,
+                    objc_class_t<P>* 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<uint64_t, uint64_t>(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 <typename P>
+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;
+    Diagnostics&            _diagnostics;
+
+    friend class ProtocolReferenceWalker<P, ProtocolOptimizer<P>>;
+
+    pint_t visitProtocolReference(ContentAccessor* cache, pint_t oldValue)
+    {
+        objc_protocol_t<P>* proto = (objc_protocol_t<P>*)
+            cache->contentForVMAddr(oldValue);
+        pint_t newValue = (pint_t)_protocols[proto->getName(cache)];
+        if (oldValue != newValue) _protocolReferenceCount++;
+        return newValue;
+    }
+
+public:
+
+    ProtocolOptimizer(Diagnostics& diag)
+        : _protocolCount(0), _protocolReferenceCount(0), _diagnostics(diag) {
+    }
+
+    void addProtocols(ContentAccessor* cache, const macho_header<P>* header)
+    {
+        PointerSection<P, objc_protocol_t<P> *>
+            protocols(cache, header, "__DATA", "__objc_protolist");
+        
+        for (pint_t i = 0; i < protocols.count(); i++) {
+            objc_protocol_t<P> *proto = protocols.get(i);
+
+            const char *name = proto->getName(cache);
+            if (_protocolNames.count(name) == 0) {
+                if (proto->getSize() > sizeof(objc_protocol_t<P>)) {
+                    _diagnostics.error("objc protocol is too big");
+                    return;
+                }
+
+                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<void*>& 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<P>);
+        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<P>* oldProto = (objc_protocol_t<P>*)
+                cache->contentForVMAddr(iter->second);
+
+            // Create a new protocol object.
+            objc_protocol_t<P>* proto = (objc_protocol_t<P>*)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<P>));
+                // 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, _diagnostics);
+            }
+            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<P>* header)
+    {
+        ProtocolReferenceWalker<P, ProtocolOptimizer<P>> 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 <typename P>
+void optimizeObjC(DyldSharedCache* cache, bool forProduction, std::vector<void*>& pointersForASLR, Diagnostics& diag)
+{
+    typedef typename P::E           E;
+    typedef typename P::uint_t      pint_t;
+
+    diag.verbose("Optimizing objc metadata:\n");
+    diag.verbose("  cache type is %s\n", forProduction ? "production" : "development");
+
+    ContentAccessor cacheAccessor(cache, diag);
+
+    size_t headerSize = P::round_up(sizeof(objc_opt::objc_opt_t));
+    if (headerSize != sizeof(objc_opt::objc_opt_t)) {
+        diag.warning("libobjc's optimization structure size is wrong (metadata not optimized)");
+    }
+
+    //
+    // Find libobjc's empty sections and build list of images with objc metadata
+    //
+    __block const macho_section<P> *optROSection = nullptr;
+    __block const macho_section<P> *optRWSection = nullptr;
+    __block const macho_section<P> *optPointerListSection = nullptr;
+    __block std::vector<const macho_header<P>*> objcDylibs;
+    cache->forEachImage(^(const mach_header* machHeader, const char* installName) {
+        const macho_header<P>* mh = (const macho_header<P>*)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 ) {
+        diag.warning("libobjc's read-only section missing (metadata not optimized)");
+        return;
+    }
+    if ( optRWSection == nullptr ) {
+        diag.warning("libobjc's read/write section missing (metadata not optimized)");
+        return;
+    }
+    if ( optPointerListSection == nullptr ) {
+        diag.warning("libobjc's pointer list section missing (metadata not optimized)");
+        return;
+    }
+
+    uint8_t* optROData = (uint8_t*)cacheAccessor.contentForVMAddr(optROSection->addr());
+    if ( optROData == nullptr ) {
+        diag.warning("libobjc's read-only section has bad content");
+        return;
+    }
+    size_t optRORemaining = optROSection->size();
+    uint8_t* optRWData = (uint8_t*)cacheAccessor.contentForVMAddr(optRWSection->addr());
+    size_t optRWRemaining = optRWSection->size();
+    if (optRORemaining < headerSize) {
+        diag.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) {
+        diag.warning("libobjc's read-only section version is unrecognized (metadata not optimized)");
+        return;
+    }
+
+    if (optPointerListSection->size() < sizeof(objc_opt::objc_opt_pointerlist_tt<pint_t>)) {
+        diag.warning("libobjc's pointer list section is too small (metadata not optimized)");
+        return;
+    }
+    const objc_opt::objc_opt_pointerlist_tt<pint_t> *optPointerList = (const objc_opt::objc_opt_pointerlist_tt<pint_t> *)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<const macho_header<P>*> addressSortedDylibs = objcDylibs;
+    std::sort(addressSortedDylibs.begin(), addressSortedDylibs.end(), [](const macho_header<P>* lmh, const macho_header<P>* 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<P, objc_header_info_ro_t<P>> hinfoROOptimizer;
+    const char* err = hinfoROOptimizer.init((uint32_t)objcDylibs.size(), optROData, optRORemaining);
+    if (err) {
+        diag.warning("%s", err);
+        return;
+    }
+    else {
+        for (const macho_header<P>* 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<P, objc_header_info_rw_t<P>> hinfoRWOptimizer;
+    err = hinfoRWOptimizer.init((uint32_t)objcDylibs.size(), optRWData, optRWRemaining);
+    if (err) {
+        diag.warning("%s", err);
+        return;
+    }
+    else {
+        for (const macho_header<P>* 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<P> uniq(&cacheAccessor);
+    std::vector<const macho_header<P>*> sizeSortedDylibs = objcDylibs;
+    std::sort(sizeSortedDylibs.begin(), sizeSortedDylibs.end(),  [](const macho_header<P>* lmh, const macho_header<P>* rmh) -> bool {
+        const macho_section<P>* lSection = lmh->getSection("__TEXT", "__objc_methname");
+        const macho_section<P>* rSection = rmh->getSection("__TEXT", "__objc_methname");
+        uint64_t lSelectorSize = (lSection ? lSection->size() : 0);
+        uint64_t rSelectorSize = (rSection ? rSection->size() : 0);
+        return lSelectorSize > rSelectorSize;
+    });
+
+    SelectorOptimizer<P, ObjCSelectorUniquer<P> > selOptimizer(uniq);
+    for (const macho_header<P>* mh : sizeSortedDylibs) {
+        LegacySelectorUpdater<P, ObjCSelectorUniquer<P>>::update(&cacheAccessor, mh, uniq);
+        selOptimizer.optimize(&cacheAccessor, mh);
+    }
+
+    diag.verbose("  uniqued  %6lu selectors\n", uniq.strings().size());
+    diag.verbose("  updated  %6lu selector references\n", 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) {
+        diag.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;
+
+    diag.verbose("  selector table occupancy %u/%u (%u%%)\n",
+                    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<P> 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) {
+            diag.error("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<P> classes(hinfoROOptimizer);
+    ClassWalker<P, ClassListBuilder<P>> classWalker(classes);
+    for (const macho_header<P>* mh : sizeSortedDylibs) {
+        classWalker.walk(&cacheAccessor, mh);
+    }
+
+    diag.verbose("  recorded % 6ld classes\n", 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) {
+        diag.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;
+
+    diag.verbose("  found    % 6ld duplicate classes\n",
+               duplicateCount);
+    diag.verbose("  class table occupancy %u/%u (%u%%)\n",
+               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<P> methodSorter;
+    for (const macho_header<P>* mh : sizeSortedDylibs) {
+        methodSorter.optimize(&cacheAccessor, mh);
+    }
+
+    diag.verbose("  sorted   % 6ld method lists\n", 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<P> protocolOptimizer(diag);
+    for (const macho_header<P>* mh : sizeSortedDylibs) {
+        protocolOptimizer.addProtocols(&cacheAccessor, mh);
+    }
+
+    diag.verbose("  uniqued  % 6ld protocols\n",
+               protocolOptimizer.protocolCount());
+
+    pint_t protocolClassVMAddr = (pint_t)P::getP(optPointerList->protocolClass);
+    err = protocolOptimizer.writeProtocols(&cacheAccessor,
+                                           optRWData, optRWRemaining,
+                                           optROData, optRORemaining,
+                                           pointersForASLR, protocolClassVMAddr);
+    if (err) {
+        diag.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) {
+        diag.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;
+
+    diag.verbose("  protocol table occupancy %u/%u (%u%%)\n",
+               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<P>* mh : sizeSortedDylibs) {
+        protocolOptimizer.updateReferences(&cacheAccessor, mh);
+    }
+
+    diag.verbose("  updated  % 6ld protocol references\n", protocolOptimizer.protocolReferenceCount());
+
+
+    //
+    // Repair ivar offsets.
+    //
+    // This is SAFE: the runtime always validates ivar offsets at runtime.
+    IvarOffsetOptimizer<P> ivarOffsetOptimizer;
+    for (const macho_header<P>* mh : sizeSortedDylibs) {
+        ivarOffsetOptimizer.optimize(&cacheAccessor, mh);
+    }
+    
+    diag.verbose("  updated  % 6ld ivar offsets\n", 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<P>* mh : sizeSortedDylibs) {
+        const macho_section<P>* imageInfoSection = mh->getSection("__DATA", "__objc_imageinfo");
+        if (!imageInfoSection) {
+            imageInfoSection = mh->getSection("__OBJC", "__image_info");
+        }
+        if (imageInfoSection) {
+            objc_image_info<P>* info = (objc_image_info<P>*)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;
+    diag.verbose("  %lu/%llu bytes (%d%%) used in libobjc read-only optimization section\n",
+                  roSize, optROSection->size(), percent(roSize, optROSection->size()));
+    diag.verbose("  %lu/%llu bytes (%d%%) used in libobjc read/write optimization section\n",
+                  rwSize, optRWSection->size(), percent(rwSize, optRWSection->size()));
+    diag.verbose("  wrote objc metadata optimization version %d\n", objc_opt::VERSION);
+}
+
+
+} // anon namespace
+
+void optimizeObjC(DyldSharedCache* cache, bool is64, bool customerCache, std::vector<void*>& pointersForASLR, Diagnostics& diag)
+{
+    if ( is64 )
+        optimizeObjC<Pointer64<LittleEndian>>(cache, customerCache, pointersForASLR, diag);
+    else
+        optimizeObjC<Pointer32<LittleEndian>>(cache, customerCache, pointersForASLR, diag);
+}
+
+
diff --git a/dyld3/shared-cache/StringUtils.h b/dyld3/shared-cache/StringUtils.h
new file mode 100644 (file)
index 0000000..701e5fd
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2017 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 StringUtils_h
+#define StringUtils_h
+
+#include <string>
+
+inline bool startsWith(const std::string& str, const std::string& prefix)
+{
+    return std::mismatch(prefix.begin(), prefix.end(), str.begin()).first == prefix.end();
+}
+
+inline bool endsWith(const std::string& str, const std::string& suffix)
+{
+    std::size_t index = str.find(suffix, str.size() - suffix.size());
+    return (index != std::string::npos);
+}
+
+inline bool contains(const std::string& str, const std::string& search)
+{
+    std::size_t index = str.find(search);
+    return (index != std::string::npos);
+}
+
+inline char hexDigit(uint8_t value)
+{
+    if ( value < 10 )
+        return '0' + value;
+    else
+        return 'a' + value - 10;
+}
+
+inline void bytesToHex(const uint8_t* bytes, size_t byteCount, char buffer[])
+{
+    char* p = buffer;
+    for (int i=0; i < byteCount; ++i) {
+        *p++ = hexDigit(bytes[i] >> 4);
+        *p++ = hexDigit(bytes[i] & 0x0F);
+    }
+    *p++ =  '\0';
+}
+
+inline uint8_t hexCharToUInt(const char hexByte, uint8_t& value) {
+    if (hexByte >= '0' && hexByte <= '9') {
+        value = hexByte - '0';
+        return true;
+    } else if (hexByte >= 'A' && hexByte <= 'F') {
+        value = hexByte - 'A' + 10;
+        return true;
+    } else if (hexByte >= 'a' && hexByte <= 'f') {
+        value = hexByte - 'a' + 10;
+        return true;
+    }
+
+    return false;
+}
+
+inline uint64_t hexToUInt64(const char* startHexByte, const char** endHexByte) {
+    if (startHexByte == nullptr)
+        return 0;
+    uint64_t retval = 0;
+    if (startHexByte[0] == '0' &&  startHexByte[1] == 'x') {
+        startHexByte +=2;
+    }
+    *endHexByte = startHexByte + 16;
+
+    //FIXME overrun?
+    for (uint32_t i = 0; i < 16; ++i) {
+        uint8_t value;
+        if (!hexCharToUInt(startHexByte[i], value)) {
+            *endHexByte = &startHexByte[i];
+            break;
+        }
+        retval = (retval << 4) + value;
+    }
+    return retval;
+}
+
+inline bool hexToBytes(const char* startHexByte, uint32_t length, uint8_t buffer[]) {
+    if (startHexByte == nullptr)
+        return false;
+    const char *currentHexByte = startHexByte;
+    for (uint32_t i = 0; i < length; ++i) {
+        uint8_t value;
+        if (!hexCharToUInt(currentHexByte[i], value)) {
+            return false;
+        }
+        if (i%2 == 0) {
+            buffer[i/2] = value << 4;
+        } else {
+            buffer[(i-1)/2] |= value;
+        }
+    }
+    return true;
+}
+
+#endif // StringUtils_h
+
diff --git a/dyld3/shared-cache/Trie.hpp b/dyld3/shared-cache/Trie.hpp
new file mode 100644 (file)
index 0000000..e021095
--- /dev/null
@@ -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 <typename V>
+ 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<std::bidirectional_iterator_tag, const Entry>;
+
+ const_iterator begin() const;
+ const_iterator end() const;
+
+ Trie(void);
+ Trie(const uint8_t* start, const uint8_t* end);
+ Trie(const std::vector<Entry>& entries);
+
+ void emit(std::vector<uint8_t>& output);
+ */
+
+
+#ifndef __TRIE__
+#define __TRIE__
+#define TRIE_DEBUG (0)
+
+#include <algorithm>
+#include <vector>
+#include <memory>
+#include <string>
+#include <map>
+#include <iterator>
+
+#include <mach-o/loader.h>
+
+#if __cplusplus <= 201103L
+namespace std {
+       template<typename T, typename... Args>
+       std::unique_ptr<T> make_unique(Args&&... args)
+       {
+               return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
+       }
+}
+#endif
+
+namespace TrieUtils {
+
+static void append_uleb128(uint64_t value, std::vector<uint8_t>& 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<uint8_t>& 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 <typename V>
+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<Entry>& entries) : count(0), nodeCount(1) {
+               // make nodes for all exported symbols
+               for (auto& entry : entries) {
+                       addEntry(entry);
+               }
+       }
+
+       void emit(std::vector<uint8_t>& output) {
+               // create vector of nodes
+               std::vector<Node*> 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<Entry>& output)
+       {
+               // empty trie has no entries
+               if ( start == end )
+                       return false;
+               char cummulativeString[32768];
+               std::vector<EntryWithOffset> 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<std::string,std::unique_ptr<Node> > 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<uint8_t>& 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<Node> nodeC;
+                                       std::swap(nodeC, entry.second);
+                                       currentNode->fChildren.erase(entry.first);
+
+                                       //Build the new node and insert it
+                                       std::unique_ptr<Node> nodeB = std::make_unique<Node>();
+                                       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<Node>(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<EntryWithOffset>& 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<Node*>& 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() : address(0), flags(0), other(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<uint8_t>& 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<ExportInfo> 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<uint8_t>& 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<DylibIndex> DylibIndexTrie;
+
+
+#endif // __TRIE__
+
+
diff --git a/dyld3/shared-cache/dyld_cache_format.h b/dyld3/shared-cache/dyld_cache_format.h
new file mode 100644 (file)
index 0000000..de63bf9
--- /dev/null
@@ -0,0 +1,279 @@
+/* -*- 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 <stdint.h>
+#include <uuid/uuid.h>
+
+
+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
+    uint64_t    dylibsImageGroupAddr;   // (unslid) address of ImageGroup for dylibs in this cache
+    uint64_t    dylibsImageGroupSize;   // size of ImageGroup for dylibs in this cache
+    uint64_t    otherImageGroupAddr;    // (unslid) address of ImageGroup for other OS dylibs
+    uint64_t    otherImageGroupSize;    // size of oImageGroup for other OS dylibs
+    uint64_t    progClosuresAddr;       // (unslid) address of list of program launch closures
+    uint64_t    progClosuresSize;       // size of list of program launch closures
+    uint64_t    progClosuresTrieAddr;   // (unslid) address of trie of indexes into program launch closures
+    uint64_t    progClosuresTrieSize;   // size of trie of indexes into program launch closures
+    uint32_t    platform;               // platform number (macOS=1, etc)
+    uint32_t    formatVersion        : 8, // launch_cache::binary_format::kFormatVersion
+                dylibsExpectedOnDisk : 1, // dyld should expect the dylib exists on disk and to compare inode/mtime to see if cache is valid
+                simulator            : 1; // for simulator of specified platform
+    uint64_t    sharedRegionStart;      // base load address of cache if not slid
+    uint64_t    sharedRegionSize;       // overall size of region cache can be mapped into
+    uint64_t    maxSlide;               // runtime slide of cache can be between zero and this value
+};
+
+
+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/dyld3/shared-cache/dyld_closure_util.cpp b/dyld3/shared-cache/dyld_closure_util.cpp
new file mode 100644 (file)
index 0000000..db01f5f
--- /dev/null
@@ -0,0 +1,614 @@
+/*
+ * Copyright (c) 2017 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 <stdio.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <dlfcn.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/mman.h>
+#include <sys/syslimits.h>
+#include <mach-o/arch.h>
+#include <mach-o/loader.h>
+#include <mach-o/dyld_priv.h>
+#include <bootstrap.h>
+#include <mach/mach.h>
+#include <dispatch/dispatch.h>
+
+#include <map>
+#include <vector>
+
+#include "LaunchCache.h"
+#include "LaunchCacheWriter.h"
+#include "DyldSharedCache.h"
+#include "FileUtils.h"
+#include "ImageProxy.h"
+#include "StringUtils.h"
+#include "ClosureBuffer.h"
+
+extern "C" {
+    #include "closuredProtocol.h"
+}
+
+static const DyldSharedCache* mapCacheFile(const char* path)
+{
+    struct stat statbuf;
+    if (stat(path, &statbuf)) {
+        fprintf(stderr, "Error: stat failed for dyld shared cache at %s\n", path);
+        return nullptr;
+    }
+        
+    int cache_fd = open(path, O_RDONLY);
+    if (cache_fd < 0) {
+        fprintf(stderr, "Error: failed to open shared cache file at %s\n", path);
+        return nullptr;
+    }
+    
+    void* mapped_cache = mmap(NULL, (size_t)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", path, errno);
+        return nullptr;
+    }
+    close(cache_fd);
+
+    return (DyldSharedCache*)mapped_cache;
+}
+
+struct CachedSections
+{
+    uint32_t            mappedOffsetStart;
+    uint32_t            mappedOffsetEnd;
+    uint64_t            vmAddress;
+    const mach_header*  mh;
+    std::string         segmentName;
+    std::string         sectionName;
+    const char*         dylibPath;
+};
+
+static const CachedSections& find(uint32_t mappedOffset, const std::vector<CachedSections>& sections)
+{
+    for (const CachedSections& entry : sections) {
+        //printf("0x%08X -> 0x%08X\n", entry.mappedOffsetStart, entry.mappedOffsetEnd);
+        if ( (entry.mappedOffsetStart <= mappedOffset) && (mappedOffset < entry.mappedOffsetEnd) )
+            return entry;
+    }
+    assert(0 && "invalid offset");
+}
+
+/*
+static const dyld3::launch_cache::BinaryClosureData*
+callClosureDaemon(const std::string& mainPath, const std::string& cachePath, const std::vector<std::string>& envArgs)
+{
+
+    mach_port_t   serverPort = MACH_PORT_NULL;
+    mach_port_t   bootstrapPort = MACH_PORT_NULL;
+    kern_return_t kr = task_get_bootstrap_port(mach_task_self(), &bootstrapPort);
+    kr = bootstrap_look_up(bootstrapPort, "com.apple.dyld.closured", &serverPort);
+    switch( kr ) {
+        case BOOTSTRAP_SUCCESS :
+            // service currently registered, "a good thing" (tm)
+            break;
+        case BOOTSTRAP_UNKNOWN_SERVICE :
+            // service not currently registered, try again later
+            fprintf(stderr, "bootstrap_look_up(): %s\n", mach_error_string(kr));
+            return nullptr;
+        default:
+            // service not currently registered, try again later
+            fprintf(stderr, "bootstrap_look_up(): %s [%d]\n", mach_error_string(kr), kr);
+            return nullptr;
+    }
+
+    //printf("serverPort=%d, replyPort=%d\n", serverPort, replyPort);
+
+
+
+    bool success;
+    char envBuffer[2048];
+    vm_offset_t reply = 0;
+    uint32_t  replySize = 0;
+    envBuffer[0] = '\0';
+    envBuffer[1] = '\0';
+//    kr = closured_CreateLaunchClosure(serverPort, mainPath.c_str(), cachePath.c_str(), uuid, envBuffer, &success, &reply, &replySize);
+
+    printf("success=%d, buf=%p, bufLen=%d\n", success, (void*)reply, replySize);
+
+    if (!success)
+        return nullptr;
+    return (const dyld3::launch_cache::BinaryClosureData*)reply;
+}
+*/
+
+static void usage()
+{
+    printf("dyld_closure_util program to create of view dyld3 closures\n");
+    printf("  mode:\n");
+    printf("    -create_closure <prog-path>            # create a closure for the specified main executable\n");
+    printf("    -create_image_group <dylib-path>       # create an ImageGroup for the specified dylib/bundle\n");
+    printf("    -list_dyld_cache_closures              # list all closures in the dyld shared cache with size\n");
+    printf("    -list_dyld_cache_other_dylibs          # list all group-1 (non-cached dylibs/bundles)\n");
+    printf("    -print_image_group <closure-path>      # print specified ImageGroup file as JSON\n");
+    printf("    -print_closure_file <closure-path>     # print specified closure file as JSON\n");
+    printf("    -print_dyld_cache_closure <prog-path>  # find closure for specified program in dyld cache and print as JSON\n");
+    printf("    -print_dyld_cache_dylibs               # print group-0 (cached dylibs) as JSON\n");
+    printf("    -print_dyld_cache_other_dylibs         # print group-1 (non-cached dylibs/bundles) as JSON\n");
+    printf("    -print_dyld_cache_other <path>         # print just one group-1 (non-cached dylib/bundle) as JSON\n");
+    printf("    -print_dyld_cache_patch_table          # print locations in shared cache that may need patching\n");
+    printf("  options:\n");
+    printf("    -cache_file <cache-path>               # path to cache file to use (default is current cache)\n");
+    printf("    -build_root <path-prefix>              # when building a closure, the path prefix when runtime volume is not current boot volume\n");
+    printf("    -o <output-file>                       # when building a closure, the file to write the (binary) closure to\n");
+    printf("    -include_all_dylibs_in_dir             # when building a closure, add other mach-o files found in directory\n");
+    printf("    -env <var=value>                       # when building a closure, DYLD_* env vars to assume\n");
+    printf("    -dlopen <path>                         # for use with -create_closure to append ImageGroup if target had called dlopen\n");
+    printf("    -verbose_fixups                        # for use with -print* options to force printing fixups\n");
+}
+
+int main(int argc, const char* argv[])
+{
+    const char*               cacheFilePath = nullptr;
+    const char*               inputMainExecutablePath = nullptr;
+    const char*               inputTopImagePath = nullptr;
+    const char*               outPath = nullptr;
+    const char*               printPath = nullptr;
+    const char*               printGroupPath = nullptr;
+    const char*               printCacheClosure = nullptr;
+    const char*               printCachedDylib = nullptr;
+    const char*               printOtherDylib = nullptr;
+    bool                      listCacheClosures = false;
+    bool                      listOtherDylibs = false;
+    bool                      includeAllDylibs = false;
+    bool                      printClosures = false;
+    bool                      printCachedDylibs = false;
+    bool                      printOtherDylibs = false;
+    bool                      printPatchTable = false;
+    bool                      useClosured = false;
+    bool                      verboseFixups = false;
+    std::vector<std::string>  buildtimePrefixes;
+    std::vector<std::string>  envArgs;
+    std::vector<const char*>  dlopens;
+
+    if ( argc == 1 ) {
+        usage();
+        return 0;
+    }
+
+    for (int i = 1; i < argc; ++i) {
+        const char* arg = argv[i];
+        if ( strcmp(arg, "-cache_file") == 0 ) {
+            cacheFilePath = argv[++i];
+            if ( cacheFilePath == nullptr ) {
+                fprintf(stderr, "-cache_file option requires path to cache file\n");
+                return 1;
+            }
+        }
+        else if ( strcmp(arg, "-create_closure") == 0 ) {
+            inputMainExecutablePath = argv[++i];
+            if ( inputMainExecutablePath == nullptr ) {
+                fprintf(stderr, "-create_closure option requires a path to an executable\n");
+                return 1;
+            }
+        }
+       else if ( strcmp(arg, "-create_image_group") == 0 ) {
+            inputTopImagePath = argv[++i];
+            if ( inputTopImagePath == nullptr ) {
+                fprintf(stderr, "-create_image_group option requires a path to a dylib or bundle\n");
+                return 1;
+            }
+        }
+       else if ( strcmp(arg, "-dlopen") == 0 ) {
+            const char* path = argv[++i];
+            if ( path == nullptr ) {
+                fprintf(stderr, "-dlopen option requires a path to a packed closure list\n");
+                return 1;
+            }
+            dlopens.push_back(path);
+        }
+       else if ( strcmp(arg, "-verbose_fixups") == 0 ) {
+           verboseFixups = true;
+        }
+        else if ( strcmp(arg, "-build_root") == 0 ) {
+            const char* buildRootPath = argv[++i];
+            if ( buildRootPath == nullptr ) {
+                fprintf(stderr, "-build_root option requires a path \n");
+                return 1;
+            }
+            buildtimePrefixes.push_back(buildRootPath);
+        }
+        else if ( strcmp(arg, "-o") == 0 ) {
+            outPath = argv[++i];
+            if ( outPath == nullptr ) {
+                fprintf(stderr, "-o option requires a path \n");
+                return 1;
+            }
+        }
+        else if ( strcmp(arg, "-print_closure_file") == 0 ) {
+            printPath = argv[++i];
+            if ( printPath == nullptr ) {
+                fprintf(stderr, "-print_closure_file option requires a path \n");
+                return 1;
+            }
+        }
+        else if ( strcmp(arg, "-print_image_group") == 0 ) {
+            printGroupPath = argv[++i];
+            if ( printGroupPath == nullptr ) {
+                fprintf(stderr, "-print_image_group option requires a path \n");
+                return 1;
+            }
+        }
+        else if ( strcmp(arg, "-list_dyld_cache_closures") == 0 ) {
+            listCacheClosures = true;
+        }
+        else if ( strcmp(arg, "-list_dyld_cache_other_dylibs") == 0 ) {
+            listOtherDylibs = true;
+        }
+        else if ( strcmp(arg, "-print_dyld_cache_closure") == 0 ) {
+            printCacheClosure = argv[++i];
+            if ( printCacheClosure == nullptr ) {
+                fprintf(stderr, "-print_dyld_cache_closure option requires a path \n");
+                return 1;
+            }
+        }
+        else if ( strcmp(arg, "-print_dyld_cache_closures") == 0 ) {
+            printClosures = true;
+        }
+        else if ( strcmp(arg, "-print_dyld_cache_dylibs") == 0 ) {
+            printCachedDylibs = true;
+        }
+        else if ( strcmp(arg, "-print_dyld_cache_other_dylibs") == 0 ) {
+            printOtherDylibs = true;
+        }
+        else if ( strcmp(arg, "-print_dyld_cache_dylib") == 0 ) {
+            printCachedDylib = argv[++i];
+            if ( printCachedDylib == nullptr ) {
+                fprintf(stderr, "-print_dyld_cache_dylib option requires a path \n");
+                return 1;
+            }
+        }
+        else if ( strcmp(arg, "-print_dyld_cache_other") == 0 ) {
+            printOtherDylib = argv[++i];
+            if ( printOtherDylib == nullptr ) {
+                fprintf(stderr, "-print_dyld_cache_other option requires a path \n");
+                return 1;
+            }
+        }
+        else if ( strcmp(arg, "-print_dyld_cache_patch_table") == 0 ) {
+            printPatchTable = true;
+        }
+        else if ( strcmp(arg, "-include_all_dylibs_in_dir") == 0 ) {
+            includeAllDylibs = true;
+        }
+        else if ( strcmp(arg, "-env") == 0 ) {
+            const char* envArg = argv[++i];
+            if ( (envArg == nullptr) || (strchr(envArg, '=') == nullptr) ) {
+                fprintf(stderr, "-env option requires KEY=VALUE\n");
+                return 1;
+            }
+            envArgs.push_back(envArg);
+        }
+        else if ( strcmp(arg, "-use_closured") == 0 ) {
+            useClosured = true;
+        }
+        else {
+            fprintf(stderr, "unknown option %s\n", arg);
+            return 1;
+        }
+    }
+
+    if ( (inputMainExecutablePath || inputTopImagePath) && printPath ) {
+        fprintf(stderr, "-create_closure and -print_closure_file are mutually exclusive");
+        return 1;
+    }
+
+    const DyldSharedCache* dyldCache = nullptr;
+    bool dyldCacheIsRaw = false;
+    if ( cacheFilePath != nullptr ) {
+        dyldCache = mapCacheFile(cacheFilePath);
+        dyldCacheIsRaw = true;
+    }
+    else {
+#if !defined(__MAC_OS_X_VERSION_MIN_REQUIRED) || (__MAC_OS_X_VERSION_MIN_REQUIRED >= 101300)
+        size_t cacheLength;
+        dyldCache = (DyldSharedCache*)_dyld_get_shared_cache_range(&cacheLength);
+        dyldCacheIsRaw = false;
+#endif
+    }
+    dyld3::ClosureBuffer::CacheIdent cacheIdent;
+    dyldCache->getUUID(cacheIdent.cacheUUID);
+    cacheIdent.cacheAddress     = (unsigned long)dyldCache;
+    cacheIdent.cacheMappedSize  = dyldCache->mappedSize();
+    dyld3::DyldCacheParser cacheParser(dyldCache, dyldCacheIsRaw);
+
+    if ( buildtimePrefixes.empty() )
+        buildtimePrefixes.push_back("");
+
+    std::vector<const dyld3::launch_cache::binary_format::ImageGroup*> existingGroups;
+    const dyld3::launch_cache::BinaryClosureData* mainClosure = nullptr;
+    if ( inputMainExecutablePath != nullptr ) {
+        dyld3::PathOverrides pathStuff(envArgs);
+        STACK_ALLOC_DYNARRAY(const dyld3::launch_cache::binary_format::ImageGroup*, 3+dlopens.size(), theGroups);
+        theGroups[0] = cacheParser.cachedDylibsGroup();
+        theGroups[1] = cacheParser.otherDylibsGroup();
+        dyld3::launch_cache::DynArray<const dyld3::launch_cache::binary_format::ImageGroup*> groupList(2, &theGroups[0]);
+        dyld3::ClosureBuffer clsBuffer(cacheIdent, inputMainExecutablePath, groupList, pathStuff);
+
+        std::string mainPath = inputMainExecutablePath;
+        for (const std::string& prefix : buildtimePrefixes) {
+            if ( startsWith(mainPath, prefix) ) {
+                mainPath = mainPath.substr(prefix.size());
+                if ( mainPath[0] != '/' )
+                    mainPath = "/" + mainPath;
+                break;
+            }
+        }
+        
+        Diagnostics closureDiag;
+        //if ( useClosured )
+        //    mainClosure = closured_makeClosure(closureDiag, clsBuffer);
+       // else
+            mainClosure = dyld3::ImageProxyGroup::makeClosure(closureDiag, clsBuffer, mach_task_self(), buildtimePrefixes);
+        if ( closureDiag.hasError() ) {
+            fprintf(stderr, "dyld_closure_util: %s\n", closureDiag.errorMessage().c_str());
+            return 1;
+        }
+        for (const std::string& warn : closureDiag.warnings() )
+            fprintf(stderr, "dyld_closure_util: warning: %s\n", warn.c_str());
+
+        dyld3::launch_cache::Closure closure(mainClosure);
+        if ( outPath != nullptr ) {
+            safeSave(mainClosure, closure.size(), outPath);
+        }
+        else {
+            dyld3::launch_cache::Closure theClosure(mainClosure);
+            theGroups[2] = theClosure.group().binaryData();
+            if ( !dlopens.empty() )
+                printf("[\n");
+            closure.printAsJSON(dyld3::launch_cache::ImageGroupList(3, &theGroups[0]), true);
+
+            int groupIndex = 3;
+            for (const char* path : dlopens) {
+                printf(",\n");
+                dyld3::launch_cache::DynArray<const dyld3::launch_cache::binary_format::ImageGroup*> groupList2(groupIndex-2, &theGroups[2]);
+                dyld3::ClosureBuffer dlopenBuffer(cacheIdent, path, groupList2, pathStuff);
+                Diagnostics  dlopenDiag;
+                //if ( useClosured )
+                //    theGroups[groupIndex] = closured_makeDlopenGroup(closureDiag, clsBuffer);
+                //else
+                    theGroups[groupIndex] = dyld3::ImageProxyGroup::makeDlopenGroup(dlopenDiag, dlopenBuffer, mach_task_self(), buildtimePrefixes);
+                if ( dlopenDiag.hasError() ) {
+                    fprintf(stderr, "dyld_closure_util: %s\n", dlopenDiag.errorMessage().c_str());
+                    return 1;
+                }
+                for (const std::string& warn : dlopenDiag.warnings() )
+                    fprintf(stderr, "dyld_closure_util: warning: %s\n", warn.c_str());
+                dyld3::launch_cache::ImageGroup dlopenGroup(theGroups[groupIndex]);
+                dlopenGroup.printAsJSON(dyld3::launch_cache::ImageGroupList(groupIndex+1, &theGroups[0]), true);
+                ++groupIndex;
+            }
+            if ( !dlopens.empty() )
+                printf("]\n");
+        }
+
+    }
+#if 0
+    else if ( inputTopImagePath != nullptr ) {
+        std::string imagePath = inputTopImagePath;
+        for (const std::string& prefix : buildtimePrefixes) {
+            if ( startsWith(imagePath, prefix) ) {
+                imagePath = imagePath.substr(prefix.size());
+                if ( imagePath[0] != '/' )
+                    imagePath = "/" + imagePath;
+                break;
+            }
+        }
+
+        Diagnostics igDiag;
+        existingGroups.push_back(dyldCache->cachedDylibsGroup());
+        existingGroups.push_back(dyldCache->otherDylibsGroup());
+        if ( existingClosuresPath != nullptr ) {
+            size_t mappedSize;
+            const void* imageGroups = mapFileReadOnly(existingClosuresPath, mappedSize);
+            if ( imageGroups == nullptr ) {
+                fprintf(stderr, "dyld_closure_util: could not read file %s\n", printPath);
+                return 1;
+            }
+            uint32_t sentGroups = *(uint32_t*)imageGroups;
+            uint16_t lastGroupNum = 2;
+            existingGroups.resize(sentGroups+2);
+            const uint8_t* p   = (uint8_t*)(imageGroups)+4;
+            //const uint8_t* end = (uint8_t*)(imageGroups) + mappedSize;
+            for (uint32_t i=0; i < sentGroups; ++i) {
+                const dyld3::launch_cache::binary_format::ImageGroup* aGroup = (const dyld3::launch_cache::binary_format::ImageGroup*)p;
+                existingGroups[2+i] = aGroup;
+                dyld3::launch_cache::ImageGroup imgrp(aGroup);
+                lastGroupNum = imgrp.groupNum();
+                p += imgrp.size();
+            }
+        }
+        const dyld3::launch_cache::binary_format::ImageGroup* ig = dyld3::ImageProxyGroup::makeDlopenGroup(igDiag, dyldCache, existingGroups.size(), existingGroups, imagePath, envArgs);
+        if ( igDiag.hasError() ) {
+            fprintf(stderr, "dyld_closure_util: %s\n", igDiag.errorMessage().c_str());
+            return 1;
+        }
+
+        dyld3::launch_cache::ImageGroup group(ig);
+        group.printAsJSON(dyldCache, true);
+    }
+#endif
+    else if ( printPath != nullptr ) {
+        size_t mappedSize;
+        const void* buff = mapFileReadOnly(printPath, mappedSize);
+        if ( buff == nullptr ) {
+            fprintf(stderr, "dyld_closure_util: could not read file %s\n", printPath);
+            return 1;
+        }
+        dyld3::launch_cache::Closure theClosure((dyld3::launch_cache::binary_format::Closure*)buff);
+        STACK_ALLOC_DYNARRAY(const dyld3::launch_cache::binary_format::ImageGroup*, 3, theGroups);
+        theGroups[0] = cacheParser.cachedDylibsGroup();
+        theGroups[1] = cacheParser.otherDylibsGroup();
+        theGroups[2] = theClosure.group().binaryData();
+        theClosure.printAsJSON(theGroups, verboseFixups);
+        //closure.printStatistics();
+        munmap((void*)buff, mappedSize);
+    }
+    else if ( printGroupPath != nullptr ) {
+        size_t mappedSize;
+        const void* buff = mapFileReadOnly(printGroupPath, mappedSize);
+        if ( buff == nullptr ) {
+            fprintf(stderr, "dyld_closure_util: could not read file %s\n", printPath);
+            return 1;
+        }
+        dyld3::launch_cache::ImageGroup group((dyld3::launch_cache::binary_format::ImageGroup*)buff);
+//        group.printAsJSON(dyldCache, verboseFixups);
+        munmap((void*)buff, mappedSize);
+    }
+    else if ( listCacheClosures ) {
+        cacheParser.forEachClosure(^(const char* runtimePath, const dyld3::launch_cache::binary_format::Closure* closureBinary) {
+            dyld3::launch_cache::Closure closure(closureBinary);
+            printf("%6lu  %s\n", closure.size(), runtimePath);
+        });
+    }
+    else if ( listOtherDylibs ) {
+        dyld3::launch_cache::ImageGroup dylibGroup(cacheParser.otherDylibsGroup());
+        for (uint32_t i=0; i < dylibGroup.imageCount(); ++i) {
+            dyld3::launch_cache::Image image = dylibGroup.image(i);
+            printf("%s\n", image.path());
+        }
+    }
+    else if ( printCacheClosure ) {
+        const dyld3::launch_cache::BinaryClosureData* cls = cacheParser.findClosure(printCacheClosure);
+        if ( cls != nullptr ) {
+            dyld3::launch_cache::Closure theClosure(cls);
+            STACK_ALLOC_DYNARRAY(const dyld3::launch_cache::binary_format::ImageGroup*, 3, theGroups);
+            theGroups[0] = cacheParser.cachedDylibsGroup();
+            theGroups[1] = cacheParser.otherDylibsGroup();
+            theGroups[2] = theClosure.group().binaryData();
+            theClosure.printAsJSON(theGroups, verboseFixups);
+        }
+        else {
+            fprintf(stderr, "no closure in cache for %s\n", printCacheClosure);
+        }
+    }
+    else if ( printClosures ) {
+        cacheParser.forEachClosure(^(const char* runtimePath, const dyld3::launch_cache::binary_format::Closure* closureBinary) {
+            dyld3::launch_cache::Closure theClosure(closureBinary);
+            STACK_ALLOC_DYNARRAY(const dyld3::launch_cache::binary_format::ImageGroup*, 3, theGroups);
+            theGroups[0] = cacheParser.cachedDylibsGroup();
+            theGroups[1] = cacheParser.otherDylibsGroup();
+            theGroups[2] = theClosure.group().binaryData();
+            theClosure.printAsJSON(theGroups, verboseFixups);
+        });
+    }
+    else if ( printCachedDylibs ) {
+        STACK_ALLOC_DYNARRAY(const dyld3::launch_cache::binary_format::ImageGroup*, 2, theGroups);
+        theGroups[0] = cacheParser.cachedDylibsGroup();
+        theGroups[1] = cacheParser.otherDylibsGroup();
+        dyld3::launch_cache::ImageGroup dylibGroup(theGroups[0]);
+        dylibGroup.printAsJSON(theGroups, verboseFixups);
+    }
+    else if ( printCachedDylib != nullptr ) {
+        STACK_ALLOC_DYNARRAY(const dyld3::launch_cache::binary_format::ImageGroup*, 2, theGroups);
+        theGroups[0] = cacheParser.cachedDylibsGroup();
+        theGroups[1] = cacheParser.otherDylibsGroup();
+        dyld3::launch_cache::ImageGroup dylibGroup(cacheParser.cachedDylibsGroup());
+        uint32_t imageIndex;
+        const dyld3::launch_cache::binary_format::Image* binImage = dylibGroup.findImageByPath(printCachedDylib, imageIndex);
+        if ( binImage != nullptr ) {
+            dyld3::launch_cache::Image image(binImage);
+            image.printAsJSON(theGroups, true);
+        }
+        else {
+            fprintf(stderr, "no such other image found\n");
+        }
+    }
+    else if ( printOtherDylibs ) {
+        STACK_ALLOC_DYNARRAY(const dyld3::launch_cache::binary_format::ImageGroup*, 2, theGroups);
+        theGroups[0] = cacheParser.cachedDylibsGroup();
+        theGroups[1] = cacheParser.otherDylibsGroup();
+        dyld3::launch_cache::ImageGroup dylibGroup(theGroups[1]);
+        dylibGroup.printAsJSON(theGroups, verboseFixups);
+    }
+    else if ( printOtherDylib != nullptr ) {
+        STACK_ALLOC_DYNARRAY(const dyld3::launch_cache::binary_format::ImageGroup*, 2, theGroups);
+        theGroups[0] = cacheParser.cachedDylibsGroup();
+        theGroups[1] = cacheParser.otherDylibsGroup();
+        dyld3::launch_cache::ImageGroup dylibGroup(cacheParser.otherDylibsGroup());
+        uint32_t imageIndex;
+        const dyld3::launch_cache::binary_format::Image* binImage = dylibGroup.findImageByPath(printOtherDylib, imageIndex);
+        if ( binImage != nullptr ) {
+            dyld3::launch_cache::Image image(binImage);
+            image.printAsJSON(theGroups, true);
+        }
+        else {
+            fprintf(stderr, "no such other image found\n");
+        }
+    }
+    else if ( printPatchTable ) {
+        __block uint64_t cacheBaseAddress = 0;
+        dyldCache->forEachRegion(^(const void* content, uint64_t vmAddr, uint64_t size, uint32_t permissions) {
+            if ( cacheBaseAddress == 0 )
+                cacheBaseAddress = vmAddr;
+        });
+        __block std::vector<CachedSections> sections;
+        __block bool hasError = false;
+        dyldCache->forEachImage(^(const mach_header* mh, const char* installName) {
+            dyld3::MachOParser parser(mh, dyldCacheIsRaw);
+            parser.forEachSection(^(const char* segName, const char* sectionName, uint32_t flags, uint64_t addr, const void* content, 
+                                    uint64_t size, uint32_t alignP2, uint32_t reserved1, uint32_t reserved2, bool illegalSectionSize, bool& stop) {
+                if ( illegalSectionSize ) {
+                    fprintf(stderr, "dyld_closure_util: section size extends beyond the end of the segment %s/%s\n", segName, sectionName);
+                    stop = true;
+                    return;
+                }
+                uint32_t offsetStart = (uint32_t)(addr - cacheBaseAddress);
+                uint32_t offsetEnd   = (uint32_t)(offsetStart + size);
+                sections.push_back({offsetStart, offsetEnd, addr, mh, segName, sectionName, installName});
+            });
+        });
+        if (hasError)
+            return 1;
+        dyld3::launch_cache::ImageGroup dylibGroup(cacheParser.cachedDylibsGroup());
+        dylibGroup.forEachDyldCachePatchLocation(cacheParser, ^(uint32_t targetCacheVmOffset, const std::vector<uint32_t>& usesPointersCacheVmOffsets, bool& stop) {
+            const CachedSections& targetSection = find(targetCacheVmOffset, sections);
+            dyld3::MachOParser targetParser(targetSection.mh, dyldCacheIsRaw);
+            const char* symbolName;
+            uint64_t symbolAddress;
+            if ( targetParser.findClosestSymbol(targetSection.vmAddress + targetCacheVmOffset - targetSection.mappedOffsetStart, &symbolName, &symbolAddress) ) {
+                printf("%s:  [cache offset = 0x%08X]\n", symbolName, targetCacheVmOffset);
+            }
+            else {
+                printf("0x%08X from %40s    %10s   %16s  + 0x%06X\n", targetCacheVmOffset, strrchr(targetSection.dylibPath, '/')+1, targetSection.segmentName.c_str(), targetSection.sectionName.c_str(), targetCacheVmOffset - targetSection.mappedOffsetStart);
+            }
+            for (uint32_t offset : usesPointersCacheVmOffsets) {
+                const CachedSections& usedInSection  = find(offset, sections);
+                printf("%40s    %10s   %16s  + 0x%06X\n",  strrchr(usedInSection.dylibPath, '/')+1, usedInSection.segmentName.c_str(), usedInSection.sectionName.c_str(), offset - usedInSection.mappedOffsetStart);
+            }
+        });
+    }
+
+
+    return 0;
+}
diff --git a/dyld3/shared-cache/dyld_shared_cache_builder.mm b/dyld3/shared-cache/dyld_shared_cache_builder.mm
new file mode 100644 (file)
index 0000000..cbedce2
--- /dev/null
@@ -0,0 +1,381 @@
+/* -*- 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 <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <sys/resource.h>
+#include <mach/mach.h>
+#include <mach/mach_time.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <fcntl.h>
+#include <dlfcn.h>
+#include <signal.h>
+#include <errno.h>
+#include <sys/uio.h>
+#include <unistd.h>
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#include <sys/resource.h>
+#include <dirent.h>
+#include <libgen.h>
+#include <pthread.h>
+#include <fts.h>
+
+#include <vector>
+#include <array>
+#include <set>
+#include <map>
+#include <unordered_set>
+#include <algorithm>
+
+#include <spawn.h>
+
+#include <Bom/Bom.h>
+
+#include "Manifest.h"
+#include "Diagnostics.h"
+#include "DyldSharedCache.h"
+#include "BuilderUtils.h"
+#include "FileUtils.h"
+#include "StringUtils.h"
+#include "MachOParser.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;
+
+static const char* tempRootDirTemplate = "/tmp/dyld_shared_cache_builder.XXXXXX";
+static char*       tempRootDir = nullptr;
+
+int runCommandAndWait(Diagnostics& diags, const char* args[])
+{
+    pid_t pid;
+    int   status;
+    int   res = posix_spawn(&pid, args[0], nullptr, nullptr, (char**)args, environ);
+    if (res != 0)
+        diags.error("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;
+        }
+    }
+
+    return res;
+}
+
+void processRoots(Diagnostics& diags, std::set<std::string>& roots)
+{
+    std::set<std::string> processedRoots;
+    struct stat           sb;
+    int                   res = 0;
+    const char*           args[8];
+
+    for (const auto& root : roots) {
+        res = stat(root.c_str(), &sb);
+
+        if (res == 0 && S_ISDIR(sb.st_mode)) {
+            roots.insert(root);
+            return;
+        } else if (endsWith(root, ".cpio") || endsWith(root, ".cpio.gz") || endsWith(root, ".cpgz") || endsWith(root, ".cpio.bz2") || endsWith(root, ".cpbz2") || endsWith(root, ".pax") || endsWith(root, ".pax.gz") || endsWith(root, ".pgz") || endsWith(root, ".pax.bz2") || endsWith(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 (endsWith(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 (endsWith(root, ".tar.gz") || endsWith(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 (endsWith(root, ".tar.bz2")
+            || endsWith(root, ".tbz2")
+            || endsWith(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 (endsWith(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 (endsWith(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 {
+            diags.error("unknown archive type: %s", root.c_str());
+            continue;
+        }
+
+        if (res != runCommandAndWait(diags, args)) {
+            fprintf(stderr, "Could not expand archive %s: %s (%d)", root.c_str(), strerror(res), res);
+            exit(-1);
+        }
+        for (auto& existingRoot : processedRoots) {
+            if (existingRoot == tempRootDir)
+                return;
+        }
+
+        processedRoots.insert(tempRootDir);
+    }
+
+    roots = processedRoots;
+}
+
+bool writeRootList(const std::string& dstRoot, const std::set<std::string>& 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<std::string> 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 {
+        __block Diagnostics   diags;
+        std::set<std::string> roots;
+        std::string           dylibCacheDir;
+        std::string           release;
+        bool                  emitDevCaches = true;
+        bool                  emitElidedDylibs = true;
+        bool                  listConfigs = false;
+        bool                  copyRoots = false;
+        bool                  debug = false;
+        std::string           dstRoot;
+        std::string           configuration;
+        std::string           resultPath;
+
+        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) {
+                    diags = Diagnostics(true);
+                    debug = true;
+                } else if (strcmp(arg, "-list_configs") == 0) {
+                    listConfigs = true;
+                } else if (strcmp(arg, "-root") == 0) {
+                    roots.insert(realPath(argv[++i]));
+                } else if (strcmp(arg, "-copy_roots") == 0) {
+                    copyRoots = true;
+                } else if (strcmp(arg, "-dylib_cache") == 0) {
+                    dylibCacheDir = realPath(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 = realPath(argv[++i]);
+                } else if (strcmp(arg, "-release") == 0) {
+                    release = argv[++i];
+                } else if (strcmp(arg, "-results") == 0) {
+                    resultPath = realPath(argv[++i]);
+                } else {
+                    //usage();
+                    diags.error("unknown option: %s\n", arg);
+                }
+            } else {
+                if (!configuration.empty()) {
+                    diags.error("You may only specify one configuration");
+                }
+                configuration = argv[i];
+            }
+        }
+
+        time_t mytime = time(0);
+        fprintf(stderr, "Started: %s", asctime(localtime(&mytime)));
+        processRoots(diags, roots);
+
+        struct rlimit rl = { OPEN_MAX, OPEN_MAX };
+        (void)setrlimit(RLIMIT_NOFILE, &rl);
+
+        if (dylibCacheDir.empty() && release.empty()) {
+            fprintf(stderr, "you must specify either -dylib_cache or -release");
+            exit(-1);
+        } else if (!dylibCacheDir.empty() && !release.empty()) {
+            fprintf(stderr, "you may not use -dylib_cache and -release at the same time");
+            exit(-1);
+        }
+
+        if ((configuration.empty() || dstRoot.empty()) && !listConfigs) {
+            fprintf(stderr, "Must specify a configuration and a valid -dst_root OR -list_configs\n");
+            exit(-1);
+        }
+
+        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());
+
+        dispatch_async(dispatch_get_main_queue(), ^{
+            auto manifest = dyld3::Manifest(diags, dylibCacheDir + "/Manifest.plist", roots);
+
+            if (manifest.build().empty()) {
+                fprintf(stderr, "No manifest found at '%s/Manifest.plist'\n", dylibCacheDir.c_str());
+                exit(-1);
+            }
+            fprintf(stderr, "Building Caches for %s\n", manifest.build().c_str());
+
+            if (listConfigs) {
+                manifest.forEachConfiguration([](const std::string& configName) {
+                    printf("%s\n", configName.c_str());
+                });
+            }
+
+            if (!manifest.filterForConfig(configuration)) {
+                fprintf(stderr, "No config %s. Please run with -list_configs to see configurations available for this %s.\n",
+                    configuration.c_str(), manifest.build().c_str());
+                exit(-1);
+            }
+            manifest.calculateClosure();
+
+            std::vector<dyld3::BuildQueueEntry> buildQueue;
+
+            bool cacheBuildSuccess = build(diags, manifest, dstRoot, false, debug, false, false);
+
+            if (!cacheBuildSuccess) {
+                exit(-1);
+            }
+
+            writeRootList(dstRoot, roots);
+
+            if (copyRoots) {
+                manifest.forEachConfiguration([&manifest](const std::string& configName) {
+                    for (auto& arch : manifest.configuration(configName).architectures) {
+                        for (auto& dylib : arch.second.results.dylibs) {
+                            if (dylib.second.included) {
+                                dyld3::MachOParser parser = manifest.parserForUUID(dylib.first);
+                                cachePaths.insert(parser.installName());
+                            }
+                        }
+                    }
+                });
+
+                BOMCopier copier = BOMCopierNewWithSys(BomSys_default());
+                BOMCopierSetCopyFileStartedHandler(copier, filteredCopy);
+                for (auto& root : roots) {
+                    BOMCopierCopy(copier, root.c_str(), dstRoot.c_str());
+                }
+                BOMCopierFree(copier);
+            }
+
+            
+
+
+            int err = sync_volume_np(dstRoot.c_str(), SYNC_VOLUME_FULLSYNC | SYNC_VOLUME_WAIT);
+            if (err) {
+                fprintf(stderr, "Volume sync failed errnor=%d (%s)\n", err, strerror(err));
+            }
+
+            // 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.
+
+            if (!resultPath.empty()) {
+                manifest.write(resultPath);
+            }
+
+            const char* args[8];
+            args[0] = (char*)"/bin/rm";
+            args[1] = (char*)"-rf";
+            args[2] = (char*)tempRootDir;
+            args[3] = nullptr;
+            (void)runCommandAndWait(diags, args);
+
+            for (const std::string& warn : diags.warnings()) {
+                fprintf(stderr, "dyld_shared_cache_builder: warning: %s\n", warn.c_str());
+            }
+            exit(0);
+        });
+    }
+
+    dispatch_main();
+
+    return 0;
+}
diff --git a/dyld3/shared-cache/make_ios_dyld_cache.cpp b/dyld3/shared-cache/make_ios_dyld_cache.cpp
new file mode 100644 (file)
index 0000000..588d7d8
--- /dev/null
@@ -0,0 +1,356 @@
+/* -*- 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 <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <mach/mach.h>
+#include <mach/mach_time.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <fcntl.h>
+#include <dlfcn.h>
+#include <signal.h>
+#include <errno.h>
+#include <assert.h>
+#include <sys/uio.h>
+#include <unistd.h>
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#include <sys/resource.h>
+#include <dirent.h>
+#include <rootless.h>
+#include <dscsym.h>
+#include <dispatch/dispatch.h>
+#include <pthread/pthread.h>
+
+#include <algorithm>
+#include <vector>
+#include <unordered_set>
+#include <unordered_set>
+#include <iostream>
+#include <fstream>
+
+#include "MachOParser.h"
+#include "FileUtils.h"
+#include "StringUtils.h"
+#include "DyldSharedCache.h"
+
+
+
+struct MappedMachOsByCategory
+{
+    std::string                                 archName;
+    std::vector<DyldSharedCache::MappedMachO>   dylibsForCache;
+    std::vector<DyldSharedCache::MappedMachO>   otherDylibsAndBundles;
+    std::vector<DyldSharedCache::MappedMachO>   mainExecutables;
+};
+
+static bool verbose = false;
+
+
+static bool addIfMachO(const std::string& buildRootPath, const std::string& runtimePath, const struct stat& statBuf, dyld3::Platform platform, std::vector<MappedMachOsByCategory>& files)
+{
+    // read start of file to determine if it is mach-o or a fat file
+    std::string fullPath = buildRootPath + runtimePath;
+    int fd = ::open(fullPath.c_str(), O_RDONLY);
+    if ( fd < 0 )
+        return false;
+    bool result = false;
+    const void* wholeFile = ::mmap(NULL, (size_t)statBuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+    if ( wholeFile != MAP_FAILED ) {
+        Diagnostics diag;
+        bool usedWholeFile = false;
+        for (MappedMachOsByCategory& file : files) {
+            size_t sliceOffset;
+            size_t sliceLength;
+            bool fatButMissingSlice;
+            const void* slice = MAP_FAILED;
+            if ( dyld3::FatUtil::isFatFileWithSlice(diag, wholeFile, statBuf.st_size, file.archName, sliceOffset, sliceLength, fatButMissingSlice) ) {
+                slice = ::mmap(NULL, sliceLength, PROT_READ, MAP_PRIVATE, fd, sliceOffset);
+                if ( slice != MAP_FAILED ) {
+                    //fprintf(stderr, "mapped slice at %p size=0x%0lX, offset=0x%0lX for %s\n", p, len, offset, fullPath.c_str());
+                    if ( !dyld3::MachOParser::isValidMachO(diag, file.archName, platform, slice, sliceLength, fullPath.c_str(), false) ) {
+                        ::munmap((void*)slice, sliceLength);
+                        slice = MAP_FAILED;
+                    }
+                }
+            }
+            else if ( !fatButMissingSlice && dyld3::MachOParser::isValidMachO(diag, file.archName, platform, wholeFile, statBuf.st_size, fullPath.c_str(), false) ) {
+                slice           = wholeFile;
+                sliceLength     = statBuf.st_size;
+                sliceOffset     = 0;
+                usedWholeFile   = true;
+                //fprintf(stderr, "mapped whole file at %p size=0x%0lX for %s\n", p, len, inputPath.c_str());
+            }
+            if ( slice != MAP_FAILED ) {
+                const mach_header* mh = (mach_header*)slice;
+                dyld3::MachOParser parser(mh);
+                if ( parser.platform() != platform ) {
+                    fprintf(stderr, "skipped wrong platform binary: %s\n", fullPath.c_str());
+                    result = false;
+                }
+                else {
+                    bool sip = true; // assume anything found in the simulator runtime is a platform binary
+                    if ( parser.isDynamicExecutable() ) {
+                        bool issetuid = (statBuf.st_mode & (S_ISUID|S_ISGID));
+                        file.mainExecutables.emplace_back(runtimePath, mh, sliceLength, issetuid, sip, sliceOffset, statBuf.st_mtime, statBuf.st_ino);
+                    }
+                    else {
+                        if ( parser.canBePlacedInDyldCache(runtimePath) ) {
+                            file.dylibsForCache.emplace_back(runtimePath, mh, sliceLength, false, sip, sliceOffset, statBuf.st_mtime, statBuf.st_ino);
+                        }
+                        else {
+                            file.otherDylibsAndBundles.emplace_back(runtimePath, mh, sliceLength, false, sip, sliceOffset, statBuf.st_mtime, statBuf.st_ino);
+                        }
+                    }
+                    result = true;
+                }
+            }
+        }
+        if ( !usedWholeFile )
+            ::munmap((void*)wholeFile, statBuf.st_size);
+    }
+    ::close(fd);
+    return result;
+}
+
+
+static bool parsePathsFile(const std::string& filePath, std::vector<std::string>& paths) {
+    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.push_back(line);
+        }
+        myfile.close();
+        return true;
+    }
+    return false;
+}
+
+
+static void mapAllFiles(const std::string& dylibsRootDir, const std::vector<std::string>& paths, dyld3::Platform platform, std::vector<MappedMachOsByCategory>& files)
+{
+    for (const std::string& runtimePath : paths) {
+        std::string fullPath = dylibsRootDir + runtimePath;
+        struct stat statBuf;
+        if ( (stat(fullPath.c_str(), &statBuf) != 0) || !addIfMachO(dylibsRootDir, runtimePath, statBuf, platform, files) )
+             fprintf(stderr, "could not load: %s\n", fullPath.c_str());
+    }
+}
+
+
+
+inline uint32_t absolutetime_to_milliseconds(uint64_t abstime)
+{
+    return (uint32_t)(abstime/1000/1000);
+}
+
+
+#define TERMINATE_IF_LAST_ARG( s )      \
+    do {                                \
+        if ( i == argc - 1 ) {          \
+            fprintf(stderr, s );        \
+            return 1;                   \
+        }                               \
+    } while ( 0 )
+
+int main(int argc, const char* argv[])
+{
+    std::string                     rootPath;
+    std::string                     dylibListFile;
+    bool                            force = false;
+    std::string                     cacheDir;
+    std::string                     dylibsList;
+    std::unordered_set<std::string> archStrs;
+
+    dyld3::Platform platform = dyld3::Platform::iOS;
+
+    // parse command line options
+    for (int i = 1; i < argc; ++i) {
+        const char* arg = argv[i];
+        if (strcmp(arg, "-debug") == 0) {
+            verbose = true;
+        }
+        else if (strcmp(arg, "-verbose") == 0) {
+            verbose = true;
+        }
+        else if (strcmp(arg, "-tvOS") == 0) {
+            platform = dyld3::Platform::tvOS;
+        }
+        else if (strcmp(arg, "-iOS") == 0) {
+            platform = dyld3::Platform::iOS;
+        }
+        else if (strcmp(arg, "-watchOS") == 0) {
+            platform = dyld3::Platform::watchOS;
+        }
+        else if ( strcmp(arg, "-root") == 0 ) {
+            TERMINATE_IF_LAST_ARG("-root missing path argument\n");
+            rootPath = argv[++i];
+        }
+        else if ( strcmp(arg, "-dylibs_list") == 0 ) {
+            TERMINATE_IF_LAST_ARG("-dylibs_list missing path argument\n");
+            dylibsList = 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 {
+            //usage();
+            fprintf(stderr, "update_dyld_sim_shared_cache: unknown option: %s\n", arg);
+            return 1;
+        }
+    }
+
+    if ( cacheDir.empty() ) {
+        fprintf(stderr, "missing -cache_dir <path> option to specify directory in which to write cache file(s)\n");
+        return 1;
+    }
+
+    if ( rootPath.empty() ) {
+        fprintf(stderr, "missing -runtime_dir <path> option to specify directory which is root of simulator runtime)\n");
+        return 1;
+    }
+    else {
+        // canonicalize rootPath
+        char resolvedPath[PATH_MAX];
+        if ( realpath(rootPath.c_str(), resolvedPath) != NULL ) {
+            rootPath = resolvedPath;
+        }
+        if ( rootPath.back() != '/' )
+            rootPath = rootPath + "/";
+    }
+
+    int err = mkpath_np(cacheDir.c_str(), S_IRWXU | S_IRGRP|S_IXGRP | S_IROTH|S_IXOTH);
+    if ( (err != 0) && (err != EEXIST) ) {
+        fprintf(stderr, "mkpath_np fail: %d", err);
+        return 1;
+    }
+
+    if ( archStrs.empty() ) {
+        switch ( platform ) {
+            case dyld3::Platform::iOS:
+            case dyld3::Platform::tvOS:
+                archStrs.insert("arm64");
+                break;
+            case dyld3::Platform::watchOS:
+                archStrs.insert("armv7k");
+                break;
+             case dyld3::Platform::unknown:
+             case dyld3::Platform::macOS:
+                assert(0 && "macOS not support with this tool");
+                break;
+        }
+    }
+
+    uint64_t t1 = mach_absolute_time();
+
+    // find all mach-o files for requested architectures
+    std::vector<MappedMachOsByCategory> allFileSets;
+    if ( archStrs.count("arm64") )
+        allFileSets.push_back({"arm64"});
+    if ( archStrs.count("armv7k") )
+        allFileSets.push_back({"armv7k"});
+    std::vector<std::string> paths;
+    parsePathsFile(dylibsList, paths);
+    mapAllFiles(rootPath, paths, platform, allFileSets);
+
+    uint64_t t2 = mach_absolute_time();
+
+    fprintf(stderr, "time to scan file system and construct lists of mach-o files: %ums\n", absolutetime_to_milliseconds(t2-t1));
+
+    // build all caches in parallel
+    __block bool cacheBuildFailure = false;
+    dispatch_apply(allFileSets.size(), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(size_t index) {
+        const MappedMachOsByCategory& fileSet = allFileSets[index];
+        const std::string outFile = cacheDir + "/dyld_shared_cache_" + fileSet.archName;
+
+        fprintf(stderr, "make %s cache with %lu dylibs, %lu other dylibs, %lu programs\n", fileSet.archName.c_str(), fileSet.dylibsForCache.size(), fileSet.otherDylibsAndBundles.size(), fileSet.mainExecutables.size());
+
+        // build cache new cache file
+        DyldSharedCache::CreateOptions options;
+        options.archName                     = fileSet.archName;
+        options.platform                     = platform;
+        options.excludeLocalSymbols          = true;
+        options.optimizeStubs                = false;
+        options.optimizeObjC                 = true;
+        options.codeSigningDigestMode = (platform() == dyld3::Platform::watchOS) ?
+                                        DyldSharedCache::Agile : DyldSharedCache::SHA256only;
+        options.dylibsRemovedDuringMastering = true;
+        options.inodesAreSameAsRuntime       = false;
+        options.cacheSupportsASLR            = true;
+        options.forSimulator                 = false;
+        options.verbose                      = verbose;
+        options.evictLeafDylibsOnOverflow    = false;
+        options.pathPrefixes                 = { rootPath };
+        DyldSharedCache::CreateResults results = DyldSharedCache::create(options, fileSet.dylibsForCache, fileSet.otherDylibsAndBundles, fileSet.mainExecutables);
+
+        // print any warnings
+        for (const std::string& warn : results.warnings) {
+            fprintf(stderr, "update_dyld_sim_shared_cache: warning: %s %s\n", fileSet.archName.c_str(), warn.c_str());
+        }
+        if ( !results.errorMessage.empty() ) {
+            // print error (if one)
+            fprintf(stderr, "update_dyld_sim_shared_cache: %s\n", results.errorMessage.c_str());
+            cacheBuildFailure = true;
+        }
+        else {
+            // save new cache file to disk and write new .map file
+            assert(results.cacheContent != nullptr);
+            if ( !safeSave(results.cacheContent, results.cacheLength, outFile) )
+                cacheBuildFailure = true;
+            if ( !cacheBuildFailure ) {
+                std::string mapStr = results.cacheContent->mapFile();
+                std::string outFileMap = cacheDir + "/dyld_shared_cache_" + fileSet.archName + ".map";
+                safeSave(mapStr.c_str(), mapStr.size(), outFileMap);
+            }
+            // free created cache buffer
+            vm_deallocate(mach_task_self(), (vm_address_t)results.cacheContent, results.cacheLength);
+        }
+    });
+
+    // we could unmap all input files, but tool is about to quit
+
+    return (cacheBuildFailure ? 1 : 0);
+}
+
diff --git a/dyld3/shared-cache/multi_dyld_shared_cache_builder.mm b/dyld3/shared-cache/multi_dyld_shared_cache_builder.mm
new file mode 100644 (file)
index 0000000..8d106d1
--- /dev/null
@@ -0,0 +1,282 @@
+/* -*- 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 <Bom/Bom.h>
+#include <dispatch/dispatch.h>
+#include <copyfile.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <sys/resource.h>
+#include <mach/mach.h>
+#include <mach/mach_time.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <fcntl.h>
+#include <dlfcn.h>
+#include <signal.h>
+#include <errno.h>
+#include <sys/uio.h>
+#include <unistd.h>
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#include <sys/resource.h>
+#include <dirent.h>
+#include <libgen.h>
+#include <pthread.h>
+
+#include <vector>
+#include <array>
+#include <set>
+#include <map>
+#include <algorithm>
+
+#include "Manifest.h"
+#include "FileUtils.h"
+#include "BuilderUtils.h"
+
+#define CACHE_BUILDER_COPY_FILE_MODE COPYFILE_ALL
+
+#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.cache-builder.build", DISPATCH_QUEUE_CONCURRENT);
+
+#define kDylibCachePrefix "/AppleInternal/Developer/DylibCaches/"
+
+void createArtifact(Diagnostics& diags, dyld3::Manifest& manifest, const std::string& dylibCachePath, bool includeExecutables)
+{
+    auto copy_state = copyfile_state_alloc();
+    mkpath_np((dylibCachePath + "/Metadata").c_str(), 0755);
+    (void)copyfile(manifest.metabomFile().c_str(), (dylibCachePath + "/Metadata/metabom.bom").c_str(), copy_state, CACHE_BUILDER_COPY_FILE_MODE);
+
+    if (!manifest.dylibOrderFile().empty()) {
+        (void)copyfile(realPath(manifest.dylibOrderFile()).c_str(), (dylibCachePath + "/Metadata/dylibOrderFile.txt").c_str(), copy_state, CACHE_BUILDER_COPY_FILE_MODE);
+    }
+
+    if (!manifest.dirtyDataOrderFile().empty()) {
+        (void)copyfile(realPath(manifest.dirtyDataOrderFile()).c_str(), (dylibCachePath + "/Metadata/dirtyDataOrderFile.txt").c_str(), copy_state, CACHE_BUILDER_COPY_FILE_MODE);
+    }
+
+    std::set<dyld3::UUID> uuids;
+    std::map<std::string, std::string> copy_pairs;
+
+    manifest.forEachConfiguration([&manifest, &uuids](const std::string& config) {
+        manifest.configuration(config).forEachArchitecture([&manifest, &config, &uuids](const std::string& arch) {
+            auto results = manifest.configuration(config).architecture(arch).results;
+            for (const auto& image : results.dylibs) {
+                uuids.insert(image.first);
+            }
+            for (const auto& image : results.bundles) {
+                uuids.insert(image.first);
+            }
+            for (const auto& image : results.executables) {
+                uuids.insert(image.first);
+            }
+        });
+    });
+
+    for (auto& uuid : uuids) {
+        auto buildPath = manifest.buildPathForUUID(uuid);
+        auto installPath = manifest.runtimePathForUUID(uuid);
+        assert(!buildPath.empty() && !installPath.empty());
+        copy_pairs.insert(std::make_pair(installPath, buildPath));
+    }
+
+    for (const auto& copy_pair : copy_pairs) {
+        std::string from = realPath(copy_pair.second);
+        std::string to = dylibCachePath + "/Root/" + copy_pair.first;
+        mkpath_np(dirPath(to).c_str(), 0755);
+        int err = copyfile(from.c_str(), to.c_str(), copy_state, CACHE_BUILDER_COPY_FILE_MODE);
+        diags.verbose("COPYING (%d) %s -> %s\n", err, from.c_str(), to.c_str());
+
+    }
+    copyfile_state_free(copy_state);
+
+    fprintf(stderr, "[Artifact] dylibs copied\n");
+}
+
+void addArtifactPaths(Diagnostics& diags, dyld3::Manifest& manifest)
+{
+    manifest.setDylibOrderFile("./Metadata/dylibOrderFile.txt");
+    manifest.setDirtyDataOrderFile("./Metadata/dirtyDataOrderFile.txt");
+    manifest.setMetabomFile("./Metadata/metabom.bom");
+
+    for (auto& projects : manifest.projects()) {
+        manifest.addProjectSource(projects.first, "./Root", true);
+    }
+}
+
+int main(int argc, const char* argv[])
+{
+    @autoreleasepool {
+        __block Diagnostics diags;
+        bool                verbose = false;
+        std::string         masterDstRoot;
+        std::string         dylibCacheDir;
+        std::string         resultPath;
+        std::string         manifestPath;
+        bool                preflight = false;
+        __block bool        allBuildsSucceeded = true;
+        bool                skipWrites = false;
+        bool                skipBuilds = false;
+        bool                agileChooseSHA256CdHash = false;
+        time_t              mytime = time(0);
+        fprintf(stderr, "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) {
+                    verbose = true;
+                    diags = Diagnostics(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()) {
+                        diags.error("-master_dst_root missing path argument");
+                    }
+                } else if (strcmp(arg, "-results") == 0) {
+                    resultPath = argv[++i];
+                } else if (strcmp(arg, "-plist") == 0) {
+                    manifestPath = argv[++i];
+                } else if (strcmp(arg, "-agile_choose_sha256_cdhash") == 0) {
+                    agileChooseSHA256CdHash = true;
+                } else {
+                    // usage();
+                    diags.error("unknown option: %s", arg);
+                }
+            } else {
+                manifestPath = argv[i];
+            }
+        }
+
+        if (getenv("LGG_SKIP_CACHE_FUN") != nullptr) {
+            skipBuilds = true;
+        }
+
+        if (diags.hasError()) {
+            printf("%s\n", diags.errorMessage().c_str());
+            exit(-1);
+        }
+
+        dispatch_async(dispatch_get_main_queue(), ^{
+            if (manifestPath.empty()) {
+                fprintf(stderr, "mainfest path argument is required\n");
+                exit(-1);
+            }
+
+            (void)chdir(dirname(strdup(manifestPath.c_str())));
+            __block auto manifest = dyld3::Manifest(diags, manifestPath);
+
+            if (manifest.build().empty()) {
+                fprintf(stderr, "No version found in manifest\n");
+                exit(-1);
+            }
+
+            fprintf(stderr, "Building Caches for %s\n", manifest.build().c_str());
+
+            if (masterDstRoot.empty()) {
+                fprintf(stderr, "-master_dst_root required path argument\n");
+                exit(-1);
+            }
+
+            if (manifest.version() < 4) {
+                fprintf(stderr, "must specify valid manifest file\n");
+                exit(-1);
+            }
+
+            struct rlimit rl = { OPEN_MAX, OPEN_MAX };
+            (void)setrlimit(RLIMIT_NOFILE, &rl);
+
+            manifest.calculateClosure();
+
+            if (!skipWrites && !skipBuilds) {
+                (void)mkpath_np((masterDstRoot + "/Boms/").c_str(), 0755);
+                dispatch_group_async(buildGroup(), build_queue, ^{
+                    createArtifact(diags, manifest, masterDstRoot + "/Artifact.dlc/", true);
+                });
+            }
+
+            if (!dylibCacheDir.empty()) {
+                dispatch_group_async(buildGroup(), build_queue, ^{
+                    createArtifact(diags, manifest, dylibCacheDir + "/AppleInternal/Developer/DylibCaches/" + manifest.build() + ".dlc/", false);
+                });
+            }
+
+            if (!skipBuilds) {
+                dispatch_group_async(buildGroup(), build_queue, ^{
+                    makeBoms(manifest, masterDstRoot);
+                });
+                allBuildsSucceeded = build(diags, manifest, masterDstRoot, true, verbose, skipWrites,
+                                           agileChooseSHA256CdHash);
+            }
+
+            manifest.write(resultPath);
+
+            addArtifactPaths(diags, manifest);
+            if (!dylibCacheDir.empty()) {
+                manifest.write(dylibCacheDir + "/AppleInternal/Developer/DylibCaches/" + manifest.build() + ".dlc/Manifest.plist");
+                manifest.writeJSON(dylibCacheDir + "/AppleInternal/Developer/DylibCaches/" + manifest.build() + ".dlc/Manifest.json");
+            }
+
+            if (!skipWrites) {
+                mkpath_np((masterDstRoot + "/Artifact.dlc").c_str(), 0755);
+                auto copy_state = copyfile_state_alloc();
+                (void)copyfile(realPath(manifestPath).c_str(), (masterDstRoot + "/Artifact.dlc/BNIManifest.plist").c_str(), copy_state, COPYFILE_ALL);
+                copyfile_state_free(copy_state);
+                manifest.write(masterDstRoot + "/Artifact.dlc/Manifest.plist");
+                manifest.writeJSON(masterDstRoot + "/Artifact.dlc/Manifest.json");
+            }
+
+            dispatch_group_wait(buildGroup(), DISPATCH_TIME_FOREVER);
+            time_t mytime = time(0);
+            fprintf(stderr, "Finished: %s", asctime(localtime(&mytime)));
+
+            if (preflight && !allBuildsSucceeded) {
+                exit(-1);
+            }
+
+            exit(0);
+        });
+    }
+    dispatch_main();
+    return 0;
+}
diff --git a/dyld3/shared-cache/update_dyld_shared_cache.cpp b/dyld3/shared-cache/update_dyld_shared_cache.cpp
new file mode 100644 (file)
index 0000000..1e82704
--- /dev/null
@@ -0,0 +1,839 @@
+/* -*- 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 <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <mach/mach.h>
+#include <mach/mach_time.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <fcntl.h>
+#include <dlfcn.h>
+#include <signal.h>
+#include <errno.h>
+#include <assert.h>
+#include <sys/uio.h>
+#include <unistd.h>
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#include <sys/resource.h>
+#include <dirent.h>
+#include <rootless.h>
+#include <dscsym.h>
+#include <dispatch/dispatch.h>
+#include <pthread/pthread.h>
+#include <Bom/Bom.h>
+#include <CoreFoundation/CoreFoundation.h>
+
+#include <algorithm>
+#include <vector>
+#include <unordered_set>
+#include <unordered_set>
+#include <iostream>
+#include <fstream>
+
+#include "MachOParser.h"
+#include "FileUtils.h"
+#include "StringUtils.h"
+#include "DyldSharedCache.h"
+
+struct MappedMachOsByCategory
+{
+    std::string                                 archName;
+    std::vector<DyldSharedCache::MappedMachO>   dylibsForCache;
+    std::vector<DyldSharedCache::MappedMachO>   otherDylibsAndBundles;
+    std::vector<DyldSharedCache::MappedMachO>   mainExecutables;
+};
+
+static const char* sAllowedPrefixes[] = {
+    "/bin/",
+    "/sbin/",
+    "/usr/",
+    "/System",
+    "/Applications/App Store.app/",
+    "/Applications/Automator.app/",
+    "/Applications/Calculator.app/",
+    "/Applications/Calendar.app/",
+    "/Applications/Chess.app/",
+    "/Applications/Contacts.app/",
+//    "/Applications/DVD Player.app/",
+    "/Applications/Dashboard.app/",
+    "/Applications/Dictionary.app/",
+    "/Applications/FaceTime.app/",
+    "/Applications/Font Book.app/",
+    "/Applications/Image Capture.app/",
+    "/Applications/Launchpad.app/",
+    "/Applications/Mail.app/",
+    "/Applications/Maps.app/",
+    "/Applications/Messages.app/",
+    "/Applications/Mission Control.app/",
+    "/Applications/Notes.app/",
+    "/Applications/Photo Booth.app/",
+//    "/Applications/Photos.app/",
+    "/Applications/Preview.app/",
+    "/Applications/QuickTime Player.app/",
+    "/Applications/Reminders.app/",
+    "/Applications/Safari.app/",
+    "/Applications/Siri.app/",
+    "/Applications/Stickies.app/",
+    "/Applications/System Preferences.app/",
+    "/Applications/TextEdit.app/",
+    "/Applications/Time Machine.app/",
+    "/Applications/iBooks.app/",
+    "/Applications/iTunes.app/",
+    "/Applications/Utilities/Activity Monitor.app",
+    "/Applications/Utilities/AirPort Utility.app",
+    "/Applications/Utilities/Audio MIDI Setup.app",
+    "/Applications/Utilities/Bluetooth File Exchange.app",
+    "/Applications/Utilities/Boot Camp Assistant.app",
+    "/Applications/Utilities/ColorSync Utility.app",
+    "/Applications/Utilities/Console.app",
+    "/Applications/Utilities/Digital Color Meter.app",
+    "/Applications/Utilities/Disk Utility.app",
+    "/Applications/Utilities/Grab.app",
+    "/Applications/Utilities/Grapher.app",
+    "/Applications/Utilities/Keychain Access.app",
+    "/Applications/Utilities/Migration Assistant.app",
+    "/Applications/Utilities/Script Editor.app",
+    "/Applications/Utilities/System Information.app",
+    "/Applications/Utilities/Terminal.app",
+    "/Applications/Utilities/VoiceOver Utility.app",
+    "/Library/CoreMediaIO/Plug-Ins/DAL/"                // temp until plugins moved or closured working
+};
+
+static const char* sDontUsePrefixes[] = {
+    "/usr/share",
+    "/usr/local/",
+    "/System/Library/Assets",
+    "/System/Library/StagedFrameworks",
+    "/System/Library/Kernels/",
+    "/bin/zsh",                             // until <rdar://31026756> is fixed
+    "/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/Metadata.framework/Versions/A/Support/mdworker", // these load third party plugins
+    "/usr/bin/mdimport", // these load third party plugins
+};
+
+
+static bool verbose = false;
+
+
+
+static bool addIfMachO(const std::string& pathPrefix, const std::string& runtimePath, const struct stat& statBuf, bool requireSIP, std::vector<MappedMachOsByCategory>& files)
+{
+    // don't precompute closure info for any debug or profile dylibs
+    if ( endsWith(runtimePath, "_profile.dylib") || endsWith(runtimePath, "_debug.dylib") || endsWith(runtimePath, "_profile") || endsWith(runtimePath, "_debug") )
+        return false;
+
+    // read start of file to determine if it is mach-o or a fat file
+    std::string fullPath = pathPrefix + runtimePath;
+    int fd = ::open(fullPath.c_str(), O_RDONLY);
+    if ( fd < 0 )
+        return false;
+    bool result = false;
+    const void* wholeFile = ::mmap(NULL, statBuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+    if ( wholeFile != MAP_FAILED ) {
+        Diagnostics diag;
+        bool usedWholeFile = false;
+        for (MappedMachOsByCategory& file : files) {
+            size_t sliceOffset;
+            size_t sliceLength;
+            bool fatButMissingSlice;
+            const void* slice = MAP_FAILED;
+            if ( dyld3::FatUtil::isFatFileWithSlice(diag, wholeFile, statBuf.st_size, file.archName, sliceOffset, sliceLength, fatButMissingSlice) ) {
+                slice = ::mmap(NULL, sliceLength, PROT_READ, MAP_PRIVATE | MAP_RESILIENT_CODESIGN, fd, sliceOffset);
+                if ( slice != MAP_FAILED ) {
+                    //fprintf(stderr, "mapped slice at %p size=0x%0lX, offset=0x%0lX for %s\n", p, len, offset, fullPath.c_str());
+                    if ( !dyld3::MachOParser::isValidMachO(diag, file.archName, dyld3::Platform::macOS, slice, sliceLength, fullPath.c_str(), false) ) {
+                        ::munmap((void*)slice, sliceLength);
+                        slice = MAP_FAILED;
+                    }
+                }
+            }
+            else if ( !fatButMissingSlice && dyld3::MachOParser::isValidMachO(diag, file.archName, dyld3::Platform::macOS, wholeFile, statBuf.st_size, fullPath.c_str(), false) ) {
+                slice           = wholeFile;
+                sliceLength     = statBuf.st_size;
+                sliceOffset     = 0;
+                usedWholeFile   = true;
+                //fprintf(stderr, "mapped whole file at %p size=0x%0lX for %s\n", p, len, inputPath.c_str());
+            }
+            std::vector<std::string> nonArchWarnings;
+            for (const std::string& warning : diag.warnings()) {
+                if ( !contains(warning, "required architecture") && !contains(warning, "not a dylib") )
+                    nonArchWarnings.push_back(warning);
+            }
+            diag.clearWarnings();
+            if ( !nonArchWarnings.empty() ) {
+                fprintf(stderr, "update_dyld_shared_cache: warning: %s for %s: ", file.archName.c_str(), runtimePath.c_str());
+                for (const std::string& warning : nonArchWarnings) {
+                    fprintf(stderr, "%s ", warning.c_str());
+                }
+                fprintf(stderr, "\n");
+            }
+            if ( slice != MAP_FAILED ) {
+                const mach_header* mh = (mach_header*)slice;
+                dyld3::MachOParser parser((mach_header*)slice);
+                bool sipProtected = isProtectedBySIP(fd);
+                bool issetuid = false;
+                if ( parser.isDynamicExecutable() ) {
+                    // When SIP enabled, only build closures for SIP protected programs
+                    if ( !requireSIP || sipProtected ) {
+                        //fprintf(stderr, "requireSIP=%d, sipProtected=%d, path=%s\n", requireSIP, sipProtected, fullPath.c_str());
+                        issetuid = (statBuf.st_mode & (S_ISUID|S_ISGID));
+                        file.mainExecutables.emplace_back(runtimePath, mh, sliceLength, issetuid, sipProtected, sliceOffset, statBuf.st_mtime, statBuf.st_ino);
+                    }
+                }
+                else if ( parser.canBePlacedInDyldCache(runtimePath) ) {
+                    // when SIP is enabled, only dylib protected by SIP can go in cache
+                    if ( !requireSIP || sipProtected )
+                        file.dylibsForCache.emplace_back(runtimePath, mh, sliceLength, issetuid, sipProtected, sliceOffset, statBuf.st_mtime, statBuf.st_ino);
+                    else
+                        file.otherDylibsAndBundles.emplace_back(runtimePath, mh, sliceLength, issetuid, sipProtected, sliceOffset, statBuf.st_mtime, statBuf.st_ino);
+                }
+                else {
+                    if ( parser.fileType() == MH_DYLIB ) {
+                        std::string installName = parser.installName();
+                        if ( startsWith(installName, "@") && !contains(runtimePath, ".app/") ) {
+                            if (  startsWith(runtimePath, "/usr/lib/") || startsWith(runtimePath, "/System/Library/") )
+                                fprintf(stderr, "update_dyld_shared_cache: warning @rpath install name for system framework: %s\n", runtimePath.c_str());
+                        }
+                    }
+                    file.otherDylibsAndBundles.emplace_back(runtimePath, mh, sliceLength, issetuid, sipProtected, sliceOffset, statBuf.st_mtime, statBuf.st_ino);
+                }
+                result = true;
+            }
+        }
+        if ( !usedWholeFile )
+            ::munmap((void*)wholeFile, statBuf.st_size);
+    }
+    ::close(fd);
+    return result;
+}
+
+static void findAllFiles(const std::vector<std::string>& pathPrefixes, bool requireSIP, std::vector<MappedMachOsByCategory>& files)
+{
+    std::unordered_set<std::string> skipDirs;
+    for (const char* s : sDontUsePrefixes)
+        skipDirs.insert(s);
+
+    __block std::unordered_set<std::string> alreadyUsed;
+    bool multiplePrefixes = (pathPrefixes.size() > 1);
+    for (const std::string& prefix : pathPrefixes) {
+        // get all files from overlay for this search dir
+        for (const char* searchDir : sAllowedPrefixes ) {
+            iterateDirectoryTree(prefix, searchDir, ^(const std::string& dirPath) { return (skipDirs.count(dirPath) != 0); }, ^(const std::string& path, const struct stat& statBuf) {
+                // ignore files that don't have 'x' bit set (all runnable mach-o files do)
+                const bool hasXBit = ((statBuf.st_mode & S_IXOTH) == S_IXOTH);
+                if ( !hasXBit && !endsWith(path, ".dylib") )
+                    return;
+
+                // ignore files too small
+                if ( statBuf.st_size < 0x3000 )
+                    return;
+
+                // don't add paths already found using previous prefix
+                if ( multiplePrefixes && (alreadyUsed.count(path) != 0) )
+                    return;
+
+                // if the file is mach-o, add to list
+                if ( addIfMachO(prefix, path, statBuf, requireSIP, files) ) {
+                    if ( multiplePrefixes )
+                        alreadyUsed.insert(path);
+                }
+            });
+        }
+    }
+}
+
+
+static void findOSFilesViaBOMS(const std::vector<std::string>& pathPrefixes, bool requireSIP, std::vector<MappedMachOsByCategory>& files)
+{
+    __block std::unordered_set<std::string> runtimePathsFound;
+    for (const std::string& prefix : pathPrefixes) {
+        iterateDirectoryTree(prefix, "/System/Library/Receipts", ^(const std::string&) { return false; }, ^(const std::string& path, const struct stat& statBuf) {
+            if ( !contains(path, "com.apple.pkg.") )
+                return;
+            if ( !endsWith(path, ".bom") )
+                return;
+            std::string fullPath = prefix + path;
+            BOMBom bom = BOMBomOpenWithSys(fullPath.c_str(), false, NULL);
+            if ( bom == nullptr )
+                return;
+            BOMFSObject rootFso = BOMBomGetRootFSObject(bom);
+            if ( rootFso == nullptr ) {
+                BOMBomFree(bom);
+                return;
+            }
+            BOMBomEnumerator e = BOMBomEnumeratorNew(bom, rootFso);
+            if ( e == nullptr ) {
+                fprintf(stderr, "Can't get enumerator for BOM root FSObject\n");
+                return;
+            }
+            BOMFSObjectFree(rootFso);
+            //fprintf(stderr, "using BOM %s\n", path.c_str());
+            while (BOMFSObject fso = BOMBomEnumeratorNext(e)) {
+                if ( BOMFSObjectIsBinaryObject(fso) ) {
+                    const char* runPath = BOMFSObjectPathName(fso);
+                    if ( (runPath[0] == '.') && (runPath[1] == '/') )
+                        ++runPath;
+                    if ( runtimePathsFound.count(runPath) == 0 ) {
+                        // only add files from sAllowedPrefixes and not in sDontUsePrefixes
+                        bool inSearchDir = false;
+                        for (const char* searchDir : sAllowedPrefixes ) {
+                            if ( strncmp(searchDir, runPath, strlen(searchDir)) == 0 )  {
+                                inSearchDir = true;
+                                break;
+                            }
+                        }
+                        if ( inSearchDir ) {
+                            bool inSkipDir = false;
+                            for (const char* skipDir : sDontUsePrefixes) {
+                                if ( strncmp(skipDir, runPath, strlen(skipDir)) == 0 )  {
+                                    inSkipDir = true;
+                                    break;
+                                }
+                            }
+                            if ( !inSkipDir ) {
+                                for (const std::string& prefix2 : pathPrefixes) {
+                                    struct stat statBuf2;
+                                    std::string fullPath2 = prefix2 + runPath;
+                                    if ( stat(fullPath2.c_str(), &statBuf2) == 0 ) {
+                                        addIfMachO(prefix2, runPath, statBuf2, requireSIP, files);
+                                        runtimePathsFound.insert(runPath);
+                                        break;
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+                BOMFSObjectFree(fso);
+            }
+
+            BOMBomEnumeratorFree(e);
+            BOMBomFree(bom);
+        });
+    }
+}
+
+
+static bool dontCache(const std::string& volumePrefix, const std::string& archName,
+                      const std::unordered_set<std::string>& pathsWithDuplicateInstallName,
+                      const DyldSharedCache::MappedMachO& aFile, bool warn,
+                      const std::unordered_set<std::string>& skipDylibs)
+{
+    if ( skipDylibs.count(aFile.runtimePath) )
+        return true;
+    if ( startsWith(aFile.runtimePath, "/usr/lib/system/introspection/") )
+        return true;
+    if ( startsWith(aFile.runtimePath, "/System/Library/QuickTime/") )
+        return true;
+    if ( startsWith(aFile.runtimePath, "/System/Library/Tcl/") )
+        return true;
+    if ( startsWith(aFile.runtimePath, "/System/Library/Perl/") )
+        return true;
+    if ( startsWith(aFile.runtimePath, "/System/Library/MonitorPanels/") )
+        return true;
+    if ( startsWith(aFile.runtimePath, "/System/Library/Accessibility/") )
+        return true;
+    if ( startsWith(aFile.runtimePath, "/usr/local/") )
+        return true;
+
+    // anything inside a .app bundle is specific to app, so should not be in shared cache
+    if ( aFile.runtimePath.find(".app/") != std::string::npos )
+        return true;
+
+    if ( archName == "i386" ) {
+        if ( startsWith(aFile.runtimePath, "/System/Library/CoreServices/") )
+            return true;
+        if ( startsWith(aFile.runtimePath, "/System/Library/Extensions/") )
+            return true;
+    }
+
+    if ( aFile.runtimePath.find("//") != std::string::npos ) {
+        if (warn) fprintf(stderr, "update_dyld_shared_cache: warning: %s skipping because of bad install name %s\n", archName.c_str(), aFile.runtimePath.c_str());
+        return true;
+    }
+
+    dyld3::MachOParser parser(aFile.mh);
+    const char* installName = parser.installName();
+    if ( (pathsWithDuplicateInstallName.count(aFile.runtimePath) != 0) && (aFile.runtimePath != installName) ) {
+        if (warn) fprintf(stderr, "update_dyld_shared_cache: warning: %s skipping because of duplicate install name %s\n", archName.c_str(), aFile.runtimePath.c_str());
+        return true;
+    }
+
+    if ( aFile.runtimePath != installName ) {
+        // see if install name is a symlink to actual path
+        std::string fullInstall = volumePrefix + installName;
+        char resolvedPath[PATH_MAX];
+        if ( realpath(fullInstall.c_str(), resolvedPath) != NULL ) {
+            std::string resolvedSymlink = resolvedPath;
+            if ( !volumePrefix.empty() ) {
+                resolvedSymlink = resolvedSymlink.substr(volumePrefix.size());
+            }
+            if ( aFile.runtimePath == resolvedSymlink ) {
+                return false;
+            }
+        }
+        if (warn) fprintf(stderr, "update_dyld_shared_cache: warning: %s skipping because of bad install name %s\n", archName.c_str(), aFile.runtimePath.c_str());
+        return true;
+    }
+    return false;
+}
+
+static void pruneCachedDylibs(const std::string& volumePrefix, const std::unordered_set<std::string>& skipDylibs, MappedMachOsByCategory& fileSet)
+{
+    std::unordered_set<std::string> pathsWithDuplicateInstallName;
+
+    std::unordered_map<std::string, std::string> installNameToFirstPath;
+    for (DyldSharedCache::MappedMachO& aFile : fileSet.dylibsForCache) {
+        dyld3::MachOParser parser(aFile.mh);
+        const char* installName = parser.installName();
+        auto pos = installNameToFirstPath.find(installName);
+        if ( pos == installNameToFirstPath.end() ) {
+            installNameToFirstPath[installName] = aFile.runtimePath;
+        }
+        else {
+            pathsWithDuplicateInstallName.insert(aFile.runtimePath);
+            pathsWithDuplicateInstallName.insert(installNameToFirstPath[installName]);
+        }
+    }
+
+    for (DyldSharedCache::MappedMachO& aFile : fileSet.dylibsForCache) {
+        if ( dontCache(volumePrefix, fileSet.archName, pathsWithDuplicateInstallName, aFile, true, skipDylibs) )
+            fileSet.otherDylibsAndBundles.push_back(aFile);
+    }
+    fileSet.dylibsForCache.erase(std::remove_if(fileSet.dylibsForCache.begin(), fileSet.dylibsForCache.end(),
+        [&](const DyldSharedCache::MappedMachO& aFile) { return dontCache(volumePrefix, fileSet.archName, pathsWithDuplicateInstallName, aFile, false, skipDylibs); }),
+        fileSet.dylibsForCache.end());
+}
+
+static void pruneOtherDylibs(const std::string& volumePrefix, MappedMachOsByCategory& fileSet)
+{
+    // other OS dylibs should not contain dylibs that are embedded in some .app bundle
+    fileSet.otherDylibsAndBundles.erase(std::remove_if(fileSet.otherDylibsAndBundles.begin(), fileSet.otherDylibsAndBundles.end(),
+        [&](const DyldSharedCache::MappedMachO& aFile) { return (aFile.runtimePath.find(".app/") != std::string::npos); }),
+        fileSet.otherDylibsAndBundles.end());
+}
+
+
+static void pruneExecutables(const std::string& volumePrefix, MappedMachOsByCategory& fileSet)
+{
+    // don't build closures for xcode shims in /usr/bin (e.g. /usr/bin/clang) which re-exec themselves to a tool inside Xcode.app
+    fileSet.mainExecutables.erase(std::remove_if(fileSet.mainExecutables.begin(), fileSet.mainExecutables.end(),
+        [&](const DyldSharedCache::MappedMachO& aFile) {
+            if ( !startsWith(aFile.runtimePath, "/usr/bin/") )
+                return false;
+            dyld3::MachOParser parser(aFile.mh);
+            __block bool isXcodeShim = false;
+            parser.forEachDependentDylib(^(const char* loadPath, bool, bool, bool, uint32_t, uint32_t, bool &stop) {
+                if ( strcmp(loadPath, "/usr/lib/libxcselect.dylib") == 0 )
+                    isXcodeShim = true;
+            });
+            return isXcodeShim;
+        }), fileSet.mainExecutables.end());
+}
+
+static bool existingCacheUpToDate(const std::string& existingCache, const std::vector<DyldSharedCache::MappedMachO>& currentDylibs)
+{
+    // if no existing cache, it is not up-to-date
+    int fd = ::open(existingCache.c_str(), O_RDONLY);
+    if ( fd < 0 )
+        return false;
+
+    // build map of found dylibs
+    std::unordered_map<std::string, const DyldSharedCache::MappedMachO*> currentDylibMap;
+    for (const DyldSharedCache::MappedMachO& aFile : currentDylibs) {
+        //fprintf(stderr, "0x%0llX 0x%0llX  %s\n", aFile.inode, aFile.modTime, aFile.runtimePath.c_str());
+        currentDylibMap[aFile.runtimePath] = &aFile;
+    }
+
+    // make sure all dylibs in existing cache have same mtime and inode as found dylib
+    __block bool foundMismatch = false;
+    const uint64_t cacheMapLen = 0x40000000;
+    void *p = ::mmap(NULL, cacheMapLen, PROT_READ, MAP_PRIVATE, fd, 0);
+    if ( p != MAP_FAILED ) {
+        const DyldSharedCache* cache = (DyldSharedCache*)p;
+        cache->forEachImageEntry(^(const char* installName, uint64_t mTime, uint64_t inode) {
+            bool foundMatch = false;
+            auto pos = currentDylibMap.find(installName);
+            if ( pos != currentDylibMap.end() ) {
+                const DyldSharedCache::MappedMachO* foundDylib = pos->second;
+                if ( (foundDylib->inode == inode) && (foundDylib->modTime == mTime) ) {
+                    foundMatch = true;
+                }
+            }
+            if ( !foundMatch ) {
+                // use slow path and look for any dylib with a matching inode and mtime
+                bool foundSlow = false;
+                for (const DyldSharedCache::MappedMachO& aFile : currentDylibs) {
+                    if ( (aFile.inode == inode) && (aFile.modTime == mTime) ) {
+                        foundSlow = true;
+                        break;
+                    }
+                }
+                if ( !foundSlow ) {
+                    foundMismatch = true;
+                    if ( verbose )
+                        fprintf(stderr, "rebuilding dyld cache because dylib changed: %s\n", installName);
+                }
+            }
+         });
+        ::munmap(p, cacheMapLen);
+    }
+
+    ::close(fd);
+
+    return !foundMismatch;
+}
+
+
+inline uint32_t absolutetime_to_milliseconds(uint64_t abstime)
+{
+    return (uint32_t)(abstime/1000/1000);
+}
+
+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 ) {          \
+            fprintf(stderr, s );        \
+            return 1;                   \
+        }                               \
+    } while ( 0 )
+
+int main(int argc, const char* argv[])
+{
+    std::string                     rootPath;
+    std::string                     overlayPath;
+    std::string                     dylibListFile;
+    bool                            universal = false;
+    bool                            force = false;
+    bool                            searchDisk = false;
+    bool                            dylibsRemoved = false;
+    std::string                     cacheDir;
+    std::unordered_set<std::string> archStrs;
+    std::unordered_set<std::string> skipDylibs;
+
+    // parse command line options
+    for (int i = 1; i < argc; ++i) {
+        const char* arg = argv[i];
+        if (strcmp(arg, "-debug") == 0) {
+            verbose = true;
+        }
+        else if (strcmp(arg, "-verbose") == 0) {
+            verbose = true;
+        }
+        else if (strcmp(arg, "-dont_map_local_symbols") == 0) {
+            //We are going to ignore this
+        }
+        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, "-search_disk") == 0) {
+            searchDisk = true;
+        }
+        else if (strcmp(arg, "-dylibs_removed_in_mastering") == 0) {
+            dylibsRemoved = true;
+        }
+        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 if (strcmp(arg, "-skip") == 0) {
+            TERMINATE_IF_LAST_ARG("-skip missing argument\n");
+            skipDylibs.insert(argv[++i]);
+        }
+        else {
+            //usage();
+            fprintf(stderr, "update_dyld_shared_cache: unknown option: %s\n", arg);
+            return 1;
+        }
+    }
+
+    if ( !rootPath.empty() & !overlayPath.empty() ) {
+        fprintf(stderr, "-root and -overlay cannot be used together\n");
+        return 1;
+    }
+    // canonicalize rootPath
+    if ( !rootPath.empty() ) {
+        char resolvedPath[PATH_MAX];
+        if ( realpath(rootPath.c_str(), resolvedPath) != NULL ) {
+            rootPath = resolvedPath;
+        }
+        // <rdar://problem/33223984> when building closures for boot volume, pathPrefixes should be empty
+        if ( rootPath == "/" ) {
+            rootPath = "";
+        }
+    }
+    // canonicalize overlayPath
+    if ( !overlayPath.empty() ) {
+        char resolvedPath[PATH_MAX];
+        if ( realpath(overlayPath.c_str(), resolvedPath) != NULL ) {
+            overlayPath = resolvedPath;
+        }
+    }
+    //
+    // pathPrefixes for three modes:
+    //   1) no options: { "" }           // search only boot volume
+    //   2) -overlay:   { overlay, "" }  // search overlay, then boot volume
+    //   3) -root:      { root }         // search only -root volume
+    //
+    std::vector<std::string> pathPrefixes;
+    if ( !overlayPath.empty() )
+        pathPrefixes.push_back(overlayPath);
+    pathPrefixes.push_back(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;
+    }
+
+    int err = mkpath_np(cacheDir.c_str(), S_IRWXU | S_IRGRP|S_IXGRP | S_IROTH|S_IXOTH);
+    if ( (err != 0) && (err != EEXIST) ) {
+        fprintf(stderr, "mkpath_np fail: %d", err);
+        return 1;
+    }
+
+    if ( archStrs.empty() ) {
+        if ( universal ) {
+            // <rdar://problem/26182089> -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");
+        }
+    }
+
+    uint64_t t1 = mach_absolute_time();
+
+    // find all mach-o files for requested architectures
+    bool requireDylibsBeRootlessProtected = isProtectedBySIP(cacheDir);
+    __block std::vector<MappedMachOsByCategory> allFileSets;
+    if ( archStrs.count("x86_64") )
+        allFileSets.push_back({"x86_64"});
+    if ( archStrs.count("x86_64h") )
+        allFileSets.push_back({"x86_64h"});
+    if ( archStrs.count("i386") )
+        allFileSets.push_back({"i386"});
+    if ( searchDisk )
+        findAllFiles(pathPrefixes, requireDylibsBeRootlessProtected, allFileSets);
+    else {
+        std::unordered_set<std::string> runtimePathsFound;
+        findOSFilesViaBOMS(pathPrefixes, requireDylibsBeRootlessProtected, allFileSets);
+    }
+
+    // nothing in OS uses i386 dylibs, so only dylibs used by third party apps need to be in cache
+    for (MappedMachOsByCategory& fileSet : allFileSets) {
+        pruneCachedDylibs(rootPath, skipDylibs, fileSet);
+        pruneOtherDylibs(rootPath, fileSet);
+        pruneExecutables(rootPath, fileSet);
+   }
+
+    uint64_t t2 = mach_absolute_time();
+    if ( verbose ) {
+        if ( searchDisk )
+            fprintf(stderr, "time to scan file system and construct lists of mach-o files: %ums\n", absolutetime_to_milliseconds(t2-t1));
+        else
+            fprintf(stderr, "time to read BOM and construct lists of mach-o files: %ums\n", absolutetime_to_milliseconds(t2-t1));
+    }
+
+    // build caches in parallel on machines with at leat 4GB of RAM
+    uint64_t memSize = 0;
+    size_t sz = sizeof(memSize);;
+    bool buildInParallel = false;
+    if ( sysctlbyname("hw.memsize", &memSize, &sz, NULL, 0) == 0 ) {
+        if ( memSize >= 0x100000000ULL )
+            buildInParallel = true;
+    }
+    dispatch_queue_t dqueue = buildInParallel ? dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
+                                              : dispatch_queue_create("serial-queue", DISPATCH_QUEUE_SERIAL);
+
+    // build all caches
+    __block bool cacheBuildFailure = false;
+    __block bool wroteSomeCacheFile = false;
+    dispatch_apply(allFileSets.size(), dqueue, ^(size_t index) {
+        MappedMachOsByCategory& fileSet = allFileSets[index];
+        const std::string outFile = cacheDir + "/dyld_shared_cache_" + fileSet.archName;
+
+        DyldSharedCache::MappedMachO (^loader)(const std::string&) = ^DyldSharedCache::MappedMachO(const std::string& runtimePath) {
+            if ( skipDylibs.count(runtimePath) )
+                return DyldSharedCache::MappedMachO();
+            for (const std::string& prefix : pathPrefixes) {
+                std::string fullPath = prefix + runtimePath;
+                struct stat statBuf;
+                if ( stat(fullPath.c_str(), &statBuf) == 0 ) {
+                    std::vector<MappedMachOsByCategory> mappedFiles;
+                    mappedFiles.push_back({fileSet.archName});
+                    if ( addIfMachO(prefix, runtimePath, statBuf, requireDylibsBeRootlessProtected, mappedFiles) ) {
+                        if ( !mappedFiles.back().dylibsForCache.empty() )
+                            return mappedFiles.back().dylibsForCache.back();
+                    }
+                }
+            }
+            return DyldSharedCache::MappedMachO();
+        };
+        size_t startCount = fileSet.dylibsForCache.size();
+        std::vector<std::pair<DyldSharedCache::MappedMachO, std::set<std::string>>> excludes;
+        DyldSharedCache::verifySelfContained(fileSet.dylibsForCache, loader, excludes);
+        for (size_t i=startCount; i < fileSet.dylibsForCache.size(); ++i) {
+            fprintf(stderr, "update_dyld_shared_cache: warning: %s not in .bom, but adding required dylib %s\n", fileSet.archName.c_str(), fileSet.dylibsForCache[i].runtimePath.c_str());
+        }
+        for (auto& exclude : excludes) {
+            std::string reasons = "(\"";
+            for (auto i = exclude.second.begin(); i != exclude.second.end(); ++i) {
+                reasons += *i;
+                if (i != --exclude.second.end()) {
+                    reasons += "\", \"";
+                }
+            }
+            reasons += "\")";
+            fprintf(stderr, "update_dyld_shared_cache: warning: %s rejected from cached dylibs: %s (%s)\n", fileSet.archName.c_str(), exclude.first.runtimePath.c_str(), reasons.c_str());
+            fileSet.otherDylibsAndBundles.push_back(exclude.first);
+        }
+
+        // check if cache is already up to date
+        if ( !force ) {
+            if ( existingCacheUpToDate(outFile, fileSet.dylibsForCache) )
+                return;
+        }
+
+         // add any extra dylibs needed which were not in .bom
+        fprintf(stderr, "update_dyld_shared_cache: %s incorporating %lu OS dylibs, tracking %lu others, building closures for %lu executables\n", fileSet.archName.c_str(), fileSet.dylibsForCache.size(), fileSet.otherDylibsAndBundles.size(), fileSet.mainExecutables.size());
+        //for (const DyldSharedCache::MappedMachO& aFile : fileSet.dylibsForCache) {
+        //    fprintf(stderr, "  %s\n", aFile.runtimePath.c_str());
+        //}
+
+
+        // build cache new cache file
+        DyldSharedCache::CreateOptions options;
+        options.archName                     = fileSet.archName;
+        options.platform                     = dyld3::Platform::macOS;
+        options.excludeLocalSymbols          = false;
+        options.optimizeStubs                = false;
+        options.optimizeObjC                 = true;
+        options.codeSigningDigestMode        = DyldSharedCache::SHA256only;
+        options.dylibsRemovedDuringMastering = dylibsRemoved;
+        options.inodesAreSameAsRuntime       = true;
+        options.cacheSupportsASLR            = (fileSet.archName != "i386");
+        options.forSimulator                 = false;
+        options.verbose                      = verbose;
+        options.evictLeafDylibsOnOverflow    = true;
+        options.pathPrefixes                 = pathPrefixes;
+        DyldSharedCache::CreateResults results = DyldSharedCache::create(options, fileSet.dylibsForCache, fileSet.otherDylibsAndBundles, fileSet.mainExecutables);
+
+        // print any warnings
+        for (const std::string& warn : results.warnings) {
+            fprintf(stderr, "update_dyld_shared_cache: warning: %s %s\n", fileSet.archName.c_str(), warn.c_str());
+        }
+        if ( !results.errorMessage.empty() ) {
+            // print error (if one)
+            fprintf(stderr, "update_dyld_shared_cache: %s\n", results.errorMessage.c_str());
+            cacheBuildFailure = true;
+        }
+        else {
+            // save new cache file to disk and write new .map file
+            assert(results.cacheContent != nullptr);
+            if ( !safeSave(results.cacheContent, results.cacheLength, outFile) )
+                cacheBuildFailure = true;
+            if ( !cacheBuildFailure ) {
+                std::string mapStr = results.cacheContent->mapFile();
+                std::string outFileMap = cacheDir + "/dyld_shared_cache_" + fileSet.archName + ".map";
+                safeSave(mapStr.c_str(), mapStr.size(), outFileMap);
+                wroteSomeCacheFile = true;
+            }
+            // free created cache buffer
+            vm_deallocate(mach_task_self(), (vm_address_t)results.cacheContent, results.cacheLength);
+        }
+    });
+
+
+    // Save off spintrace data
+    if ( wroteSomeCacheFile ) {
+        void* h = dlopen("/usr/lib/libdscsym.dylib", 0);
+        if ( h != nullptr ) {
+            typedef int (*dscym_func)(const char*);
+            dscym_func func = (dscym_func)dlsym(h, "dscsym_save_dscsyms_for_current_caches");
+            std::string nuggetRoot = rootPath;
+            if ( nuggetRoot.empty() )
+                 nuggetRoot = overlayPath;
+            if ( nuggetRoot.empty() )
+                 nuggetRoot = "/";
+            (*func)(nuggetRoot.c_str());
+        }
+    }
+
+
+    // we could unmap all input files, but tool is about to quit
+
+    return (cacheBuildFailure ? 1 : 0);
+}
+
diff --git a/dyld3/shared-cache/update_dyld_shared_cache_entitlements.plist b/dyld3/shared-cache/update_dyld_shared_cache_entitlements.plist
new file mode 100644 (file)
index 0000000..5ed9d1c
--- /dev/null
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+    <dict>
+      <key>com.apple.rootless.storage.dyld</key>
+      <true/>
+    </dict>
+</plist>
diff --git a/dyld3/shared-cache/update_dyld_sim_shared_cache.cpp b/dyld3/shared-cache/update_dyld_sim_shared_cache.cpp
new file mode 100644 (file)
index 0000000..404bdc4
--- /dev/null
@@ -0,0 +1,541 @@
+/* -*- 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 <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <mach/mach.h>
+#include <mach/mach_time.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <fcntl.h>
+#include <dlfcn.h>
+#include <signal.h>
+#include <errno.h>
+#include <assert.h>
+#include <sys/uio.h>
+#include <unistd.h>
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#include <sys/resource.h>
+#include <dirent.h>
+#include <rootless.h>
+#include <dscsym.h>
+#include <dispatch/dispatch.h>
+#include <pthread/pthread.h>
+
+#include <algorithm>
+#include <vector>
+#include <unordered_set>
+#include <unordered_set>
+#include <iostream>
+#include <fstream>
+
+#include "MachOParser.h"
+#include "FileUtils.h"
+#include "StringUtils.h"
+#include "DyldSharedCache.h"
+
+
+
+struct MappedMachOsByCategory
+{
+    std::string                                 archName;
+    std::vector<DyldSharedCache::MappedMachO>   dylibsForCache;
+    std::vector<DyldSharedCache::MappedMachO>   otherDylibsAndBundles;
+    std::vector<DyldSharedCache::MappedMachO>   mainExecutables;
+};
+
+static const char* sSearchDirs[] = {
+    "/bin",
+    "/sbin",
+    "/usr",
+    "/System",
+};
+
+static const char* sSkipDirs[] = {
+    "/usr/share",
+    "/usr/local/include",
+};
+
+
+static const char* sMacOsAdditions[] = {
+    "/usr/lib/system/libsystem_kernel.dylib",
+    "/usr/lib/system/libsystem_platform.dylib",
+    "/usr/lib/system/libsystem_pthread.dylib",
+};
+
+
+static bool verbose = false;
+
+static bool addIfMachO(const std::string& simRuntimeRootPath, const std::string& runtimePath, const struct stat& statBuf, dyld3::Platform platform, std::vector<MappedMachOsByCategory>& files)
+{
+    // read start of file to determine if it is mach-o or a fat file
+    std::string fullPath = simRuntimeRootPath + runtimePath;
+    int fd = ::open(fullPath.c_str(), O_RDONLY);
+    if ( fd < 0 )
+        return false;
+    bool result = false;
+    const void* wholeFile = ::mmap(NULL, statBuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+    if ( wholeFile != MAP_FAILED ) {
+        Diagnostics diag;
+        bool usedWholeFile = false;
+        for (MappedMachOsByCategory& file : files) {
+            size_t sliceOffset;
+            size_t sliceLength;
+            bool fatButMissingSlice;
+            const void* slice = MAP_FAILED;
+            if ( dyld3::FatUtil::isFatFileWithSlice(diag, wholeFile, statBuf.st_size, file.archName, sliceOffset, sliceLength, fatButMissingSlice) ) {
+                slice = ::mmap(NULL, sliceLength, PROT_READ, MAP_PRIVATE, fd, sliceOffset);
+                if ( slice != MAP_FAILED ) {
+                    //fprintf(stderr, "mapped slice at %p size=0x%0lX, offset=0x%0lX for %s\n", p, len, offset, fullPath.c_str());
+                    if ( !dyld3::MachOParser::isValidMachO(diag, file.archName, platform, slice, sliceLength, fullPath.c_str(), false) ) {
+                        ::munmap((void*)slice, sliceLength);
+                        slice = MAP_FAILED;
+                    }
+                }
+            }
+            else if ( !fatButMissingSlice && dyld3::MachOParser::isValidMachO(diag, file.archName, platform, wholeFile, statBuf.st_size, fullPath.c_str(), false) ) {
+                slice           = wholeFile;
+                sliceLength     = statBuf.st_size;
+                sliceOffset     = 0;
+                usedWholeFile   = true;
+                //fprintf(stderr, "mapped whole file at %p size=0x%0lX for %s\n", p, len, inputPath.c_str());
+            }
+            if ( slice != MAP_FAILED ) {
+                const mach_header* mh = (mach_header*)slice;
+                dyld3::MachOParser parser(mh);
+                if ( parser.platform() != platform ) {
+                    fprintf(stderr, "skipped wrong platform binary: %s\n", fullPath.c_str());
+                    result = false;
+                }
+                else {
+                    bool sip = true; // assume anything found in the simulator runtime is a platform binary
+                    if ( parser.isDynamicExecutable() ) {
+                        bool issetuid = (statBuf.st_mode & (S_ISUID|S_ISGID));
+                        file.mainExecutables.emplace_back(runtimePath, mh, sliceLength, issetuid, sip, sliceOffset, statBuf.st_mtime, statBuf.st_ino);
+                    }
+                    else {
+                        if ( parser.canBePlacedInDyldCache(runtimePath) ) {
+                            file.dylibsForCache.emplace_back(runtimePath, mh, sliceLength, false, sip, sliceOffset, statBuf.st_mtime, statBuf.st_ino);
+                        }
+                        else {
+                            file.otherDylibsAndBundles.emplace_back(runtimePath, mh, sliceLength, false, sip, sliceOffset, statBuf.st_mtime, statBuf.st_ino);
+                        }
+                    }
+                    result = true;
+                }
+            }
+        }
+        if ( !usedWholeFile )
+            ::munmap((void*)wholeFile, statBuf.st_size);
+    }
+    ::close(fd);
+    return result;
+}
+
+static void findAllFiles(const std::string& simRuntimeRootPath, dyld3::Platform platform, std::vector<MappedMachOsByCategory>& files)
+{
+    std::unordered_set<std::string> skipDirs;
+    for (const char* s : sSkipDirs)
+        skipDirs.insert(s);
+
+    for (const char* searchDir : sSearchDirs ) {
+        iterateDirectoryTree(simRuntimeRootPath, searchDir, ^(const std::string& dirPath) { return (skipDirs.count(dirPath) != 0); }, ^(const std::string& path, const struct stat& statBuf) {
+            // ignore files that don't have 'x' bit set (all runnable mach-o files do)
+            const bool hasXBit = ((statBuf.st_mode & S_IXOTH) == S_IXOTH);
+            if ( !hasXBit && !endsWith(path, ".dylib") )
+                return;
+
+            // ignore files too small
+            if ( statBuf.st_size < 0x3000 )
+                return;
+
+            // if the file is mach-o add to list
+            addIfMachO(simRuntimeRootPath, path, statBuf, platform, files);
+         });
+    }
+}
+
+static void addMacOSAdditions(std::vector<MappedMachOsByCategory>& allFileSets)
+{
+    for (const char* addPath : sMacOsAdditions) {
+        struct stat statBuf;
+        if ( stat(addPath, &statBuf) == 0 )
+            addIfMachO("", addPath, statBuf, dyld3::Platform::macOS, allFileSets);
+    }
+}
+
+
+static bool dontCache(const std::string& simRuntimeRootPath, const std::string& archName,
+                      const std::unordered_set<std::string>& pathsWithDuplicateInstallName,
+                      const DyldSharedCache::MappedMachO& aFile, bool warn)
+{
+    if ( startsWith(aFile.runtimePath, "/usr/lib/system/introspection/") )
+        return true;
+    if ( startsWith(aFile.runtimePath, "/usr/local/") )
+        return true;
+
+    // anything inside a .app bundle is specific to app, so should be in shared cache
+    if ( aFile.runtimePath.find(".app/") != std::string::npos )
+        return true;
+
+    if ( aFile.runtimePath.find("//") != std::string::npos ) {
+        if (warn) fprintf(stderr, "update_dyld_sim_shared_cache: warning: %s double-slash in install name %s\n", archName.c_str(), aFile.runtimePath.c_str());
+    }
+
+    dyld3::MachOParser parser(aFile.mh);
+    const char* installName = parser.installName();
+    if ( (pathsWithDuplicateInstallName.count(aFile.runtimePath) != 0) && (aFile.runtimePath != installName) ) {
+        if (warn) fprintf(stderr, "update_dyld_sim_shared_cache: warning: %s skipping because of duplicate install name %s\n", archName.c_str(), aFile.runtimePath.c_str());
+        return true;
+    }
+
+    if ( aFile.runtimePath != installName ) {
+        // see if install name is a symlink to actual path
+        std::string fullInstall = simRuntimeRootPath + installName;
+        char resolvedPath[PATH_MAX];
+        if ( realpath(fullInstall.c_str(), resolvedPath) != NULL ) {
+            std::string resolvedSymlink = resolvedPath;
+            if ( !simRuntimeRootPath.empty() ) {
+                resolvedSymlink = resolvedSymlink.substr(simRuntimeRootPath.size());
+            }
+            if ( aFile.runtimePath == resolvedSymlink ) {
+                return false;
+            }
+        }
+        if (warn) fprintf(stderr, "update_dyld_sim_shared_cache: warning: %s skipping because of bad install name %s\n", archName.c_str(), aFile.runtimePath.c_str());
+        return true;
+    }
+
+    return false;
+}
+
+static void pruneCachedDylibs(const std::string& simRuntimeRootPath, MappedMachOsByCategory& fileSet)
+{
+    std::unordered_set<std::string> pathsWithDuplicateInstallName;
+
+    std::unordered_map<std::string, std::string> installNameToFirstPath;
+    for (DyldSharedCache::MappedMachO& aFile : fileSet.dylibsForCache) {
+        //fprintf(stderr, "dylib: %s\n", aFile.runtimePath.c_str());
+        dyld3::MachOParser parser(aFile.mh);
+        const char* installName = parser.installName();
+        auto pos = installNameToFirstPath.find(installName);
+        if ( pos == installNameToFirstPath.end() ) {
+            installNameToFirstPath[installName] = aFile.runtimePath;
+        }
+        else {
+            pathsWithDuplicateInstallName.insert(aFile.runtimePath);
+            pathsWithDuplicateInstallName.insert(installNameToFirstPath[installName]);
+        }
+    }
+
+    for (DyldSharedCache::MappedMachO& aFile : fileSet.dylibsForCache) {
+        if ( dontCache(simRuntimeRootPath, fileSet.archName, pathsWithDuplicateInstallName, aFile, true) )
+            fileSet.otherDylibsAndBundles.push_back(aFile);
+     }
+    fileSet.dylibsForCache.erase(std::remove_if(fileSet.dylibsForCache.begin(), fileSet.dylibsForCache.end(),
+        [&](const DyldSharedCache::MappedMachO& aFile) { return dontCache(simRuntimeRootPath, fileSet.archName, pathsWithDuplicateInstallName, aFile, false); }),
+        fileSet.dylibsForCache.end());
+}
+
+static bool existingCacheUpToDate(const std::string& existingCache, const std::vector<DyldSharedCache::MappedMachO>& currentDylibs)
+{
+    // if no existing cache, it is not up-to-date
+    int fd = ::open(existingCache.c_str(), O_RDONLY);
+    if ( fd < 0 )
+        return false;
+
+    // build map of found dylibs
+    std::unordered_map<std::string, const DyldSharedCache::MappedMachO*> currentDylibMap;
+    for (const DyldSharedCache::MappedMachO& aFile : currentDylibs) {
+        //fprintf(stderr, "0x%0llX 0x%0llX  %s\n", aFile.inode, aFile.modTime, aFile.runtimePath.c_str());
+        currentDylibMap[aFile.runtimePath] = &aFile;
+    }
+
+    // make sure all dylibs in existing cache have same mtime and inode as found dylib
+    __block bool foundMismatch = false;
+    const uint64_t cacheMapLen = 0x40000000;
+    void *p = ::mmap(NULL, cacheMapLen, PROT_READ, MAP_PRIVATE, fd, 0);
+    if ( p != MAP_FAILED ) {
+        const DyldSharedCache* cache = (DyldSharedCache*)p;
+        cache->forEachImageEntry(^(const char* installName, uint64_t mTime, uint64_t inode) {
+            bool foundMatch = false;
+            auto pos = currentDylibMap.find(installName);
+            if ( pos != currentDylibMap.end() ) {
+                const DyldSharedCache::MappedMachO* foundDylib = pos->second;
+                if ( (foundDylib->inode == inode) && (foundDylib->modTime == mTime) ) {
+                    foundMatch = true;
+                }
+            }
+            if ( !foundMatch ) {
+                // use slow path and look for any dylib with a matching inode and mtime
+                bool foundSlow = false;
+                for (const DyldSharedCache::MappedMachO& aFile : currentDylibs) {
+                    if ( (aFile.inode == inode) && (aFile.modTime == mTime) ) {
+                        foundSlow = true;
+                        break;
+                    }
+                }
+                if ( !foundSlow ) {
+                    foundMismatch = true;
+                    if ( verbose )
+                        fprintf(stderr, "rebuilding dyld cache because dylib changed: %s\n", installName);
+                }
+            }
+         });
+        ::munmap(p, cacheMapLen);
+    }
+
+    ::close(fd);
+
+    return !foundMismatch;
+}
+
+
+inline uint32_t absolutetime_to_milliseconds(uint64_t abstime)
+{
+    return (uint32_t)(abstime/1000/1000);
+}
+
+
+#define TERMINATE_IF_LAST_ARG( s )      \
+    do {                                \
+        if ( i == argc - 1 ) {          \
+            fprintf(stderr, s );        \
+            return 1;                   \
+        }                               \
+    } while ( 0 )
+
+int main(int argc, const char* argv[])
+{
+    std::string                     rootPath;
+    std::string                     dylibListFile;
+    bool                            force = false;
+    std::string                     cacheDir;
+    std::unordered_set<std::string> archStrs;
+
+    dyld3::Platform platform = dyld3::Platform::iOS;
+
+    // parse command line options
+    for (int i = 1; i < argc; ++i) {
+        const char* arg = argv[i];
+        if (strcmp(arg, "-debug") == 0) {
+            verbose = true;
+        }
+        else if (strcmp(arg, "-verbose") == 0) {
+            verbose = true;
+        }
+        else if (strcmp(arg, "-tvOS") == 0) {
+            platform = dyld3::Platform::tvOS;
+        }
+        else if (strcmp(arg, "-iOS") == 0) {
+            platform = dyld3::Platform::iOS;
+        }
+        else if (strcmp(arg, "-watchOS") == 0) {
+            platform = dyld3::Platform::watchOS;
+        }
+        else if ( strcmp(arg, "-runtime_dir") == 0 ) {
+            TERMINATE_IF_LAST_ARG("-runtime_dir missing path argument\n");
+            rootPath = 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 {
+            //usage();
+            fprintf(stderr, "update_dyld_sim_shared_cache: unknown option: %s\n", arg);
+            return 1;
+        }
+    }
+
+    if ( cacheDir.empty() ) {
+        fprintf(stderr, "missing -cache_dir <path> option to specify directory in which to write cache file(s)\n");
+        return 1;
+    }
+
+    if ( rootPath.empty() ) {
+        fprintf(stderr, "missing -runtime_dir <path> option to specify directory which is root of simulator runtime)\n");
+        return 1;
+    }
+    else {
+        // canonicalize rootPath
+        char resolvedPath[PATH_MAX];
+        if ( realpath(rootPath.c_str(), resolvedPath) != NULL ) {
+            rootPath = resolvedPath;
+        }
+    }
+
+    int err = mkpath_np(cacheDir.c_str(), S_IRWXU | S_IRGRP|S_IXGRP | S_IROTH|S_IXOTH);
+    if ( (err != 0) && (err != EEXIST) ) {
+        fprintf(stderr, "mkpath_np fail: %d", err);
+        return 1;
+    }
+
+    if ( archStrs.empty() ) {
+        switch ( platform ) {
+            case dyld3::Platform::iOS:
+                archStrs.insert("x86_64");
+                break;
+            case dyld3::Platform::tvOS:
+                archStrs.insert("x86_64");
+                break;
+            case dyld3::Platform::watchOS:
+                archStrs.insert("i386");
+                break;
+             case dyld3::Platform::unknown:
+             case dyld3::Platform::macOS:
+                assert(0 && "macOS does not have a simulator");
+                break;
+             case dyld3::Platform::bridgeOS:
+                assert(0 && "bridgeOS does not have a simulator");
+                break;
+       }
+    }
+
+    uint64_t t1 = mach_absolute_time();
+
+    // find all mach-o files for requested architectures
+    __block std::vector<MappedMachOsByCategory> allFileSets;
+    if ( archStrs.count("x86_64") )
+        allFileSets.push_back({"x86_64"});
+    if ( archStrs.count("i386") )
+        allFileSets.push_back({"i386"});
+    findAllFiles(rootPath, platform, allFileSets);
+    addMacOSAdditions(allFileSets);
+    for (MappedMachOsByCategory& fileSet : allFileSets) {
+        pruneCachedDylibs(rootPath, fileSet);
+    }
+
+    uint64_t t2 = mach_absolute_time();
+
+    fprintf(stderr, "time to scan file system and construct lists of mach-o files: %ums\n", absolutetime_to_milliseconds(t2-t1));
+
+    // build all caches in parallel
+    __block bool cacheBuildFailure = false;
+    dispatch_apply(allFileSets.size(), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(size_t index) {
+        MappedMachOsByCategory& fileSet = allFileSets[index];
+        const std::string outFile = cacheDir + "/dyld_shared_cache_" + fileSet.archName;
+        __block std::unordered_set<std::string> knownMissingDylib;
+
+        DyldSharedCache::MappedMachO (^loader)(const std::string&) = ^DyldSharedCache::MappedMachO(const std::string& runtimePath) {
+            std::string fullPath = rootPath + runtimePath;
+            struct stat statBuf;
+            if ( stat(fullPath.c_str(), &statBuf) == 0 ) {
+                std::vector<MappedMachOsByCategory> mappedFiles;
+                mappedFiles.push_back({fileSet.archName});
+                if ( addIfMachO(rootPath, runtimePath, statBuf, platform, mappedFiles) ) {
+                    if ( !mappedFiles.back().dylibsForCache.empty() )
+                        return mappedFiles.back().dylibsForCache.back();
+                }
+            }
+            if ( knownMissingDylib.count(runtimePath) == 0 ) {
+                fprintf(stderr, "update_dyld_sim_shared_cache: warning: %s could not use in dylid cache: %s\n", fileSet.archName.c_str(), runtimePath.c_str());
+                knownMissingDylib.insert(runtimePath);
+            }
+            return DyldSharedCache::MappedMachO();
+        };
+        size_t startCount = fileSet.dylibsForCache.size();
+        std::vector<std::pair<DyldSharedCache::MappedMachO, std::set<std::string>>> excludes;
+        DyldSharedCache::verifySelfContained(fileSet.dylibsForCache, loader, excludes);
+        for (size_t i=startCount; i < fileSet.dylibsForCache.size(); ++i) {
+            fprintf(stderr, "update_dyld_sim_shared_cache: warning: %s not found in initial scan, but adding required dylib %s\n", fileSet.archName.c_str(), fileSet.dylibsForCache[i].runtimePath.c_str());
+        }
+        for (auto& exclude : excludes) {
+            std::string reasons = "(\"";
+            for (auto i = exclude.second.begin(); i != exclude.second.end(); ++i) {
+                reasons += *i;
+                if (i != --exclude.second.end()) {
+                    reasons += "\", \"";
+                }
+            }
+            reasons += "\")";
+            fprintf(stderr, "update_dyld_shared_cache: warning: %s rejected from cached dylibs: %s (%s)\n", fileSet.archName.c_str(), exclude.first.runtimePath.c_str(), reasons.c_str());
+            fileSet.otherDylibsAndBundles.push_back(exclude.first);
+        }
+
+        // check if cache is already up to date
+        if ( !force ) {
+            if ( existingCacheUpToDate(outFile, fileSet.dylibsForCache) )
+                return;
+        }
+        fprintf(stderr, "make %s cache with %lu dylibs, %lu other dylibs, %lu programs\n", fileSet.archName.c_str(), fileSet.dylibsForCache.size(), fileSet.otherDylibsAndBundles.size(), fileSet.mainExecutables.size());
+
+        // build cache new cache file
+        DyldSharedCache::CreateOptions options;
+        options.archName                     = fileSet.archName;
+        options.platform                     = platform;
+        options.excludeLocalSymbols          = false;
+        options.optimizeStubs                = false;
+        options.optimizeObjC                 = true;
+        options.codeSigningDigestMode        = DyldSharedCache::SHA256only;
+        options.dylibsRemovedDuringMastering = false;
+        options.inodesAreSameAsRuntime       = true;
+        options.cacheSupportsASLR            = false;
+        options.forSimulator                 = true;
+        options.verbose                      = verbose;
+        options.evictLeafDylibsOnOverflow    = true;
+        options.pathPrefixes                 = { rootPath };
+        DyldSharedCache::CreateResults results = DyldSharedCache::create(options, fileSet.dylibsForCache, fileSet.otherDylibsAndBundles, fileSet.mainExecutables);
+
+        // print any warnings
+        for (const std::string& warn : results.warnings) {
+            fprintf(stderr, "update_dyld_sim_shared_cache: warning: %s %s\n", fileSet.archName.c_str(), warn.c_str());
+        }
+        if ( !results.errorMessage.empty() ) {
+            // print error (if one)
+            fprintf(stderr, "update_dyld_sim_shared_cache: %s\n", results.errorMessage.c_str());
+            cacheBuildFailure = true;
+        }
+        else {
+            // save new cache file to disk and write new .map file
+            assert(results.cacheContent != nullptr);
+            if ( !safeSave(results.cacheContent, results.cacheLength, outFile) )
+                cacheBuildFailure = true;
+            if ( !cacheBuildFailure ) {
+                std::string mapStr = results.cacheContent->mapFile();
+                std::string outFileMap = cacheDir + "/dyld_shared_cache_" + fileSet.archName + ".map";
+                safeSave(mapStr.c_str(), mapStr.size(), outFileMap);
+            }
+            // free created cache buffer
+            vm_deallocate(mach_task_self(), (vm_address_t)results.cacheContent, results.cacheLength);
+        }
+    });
+
+    // we could unmap all input files, but tool is about to quit
+
+    return (cacheBuildFailure ? 1 : 0);
+}
+
index ce411476b8d6ce1562163626cd592511eea956e8..770abd497fe3fd893141d5e05518f198352ee30f 100644 (file)
@@ -93,8 +93,28 @@ extern int _NSGetExecutablePath(char* buf, uint32_t* bufsize)                 __
 
 
 
 
 
 
+/*
+ * Registers a function to be called when the current thread terminates.
+ * Called by c++ compiler to implement destructors on thread_local object variables.
+ */
+extern void _tlv_atexit(void (*termFunc)(void* objAddr), void* objAddr)      __OSX_AVAILABLE_STARTING(__MAC_10_10, __IPHONE_8_0);
+
+
+/*
+ * Never called. On-disk thread local variables contain a pointer to this.  Once
+ * the thread local is prepared, the pointer changes to a real handler such as tlv_get_addr.
+ */
+extern void _tlv_bootstrap()                                                 __OSX_AVAILABLE_STARTING(__MAC_10_10, __IPHONE_8_0);
 
 
 
 
+// FIXME: remove when Availability.h updated
+#ifndef __BRIDGEOS_UNAVAILABLE
+    #ifdef __ENVIRONMENT_BRIDGE_OS_VERSION_MIN_REQUIRED__
+        #define __BRIDGEOS_UNAVAILABLE   __OS_AVAILABILITY(bridgeos,unavailable)
+    #else
+        #define __BRIDGEOS_UNAVAILABLE
+    #endif
+#endif
 
 /*
  * The following dyld API's are deprecated as of Mac OS X 10.5.  They are either  
 
 /*
  * The following dyld API's are deprecated as of Mac OS X 10.5.  They are either  
@@ -134,24 +154,25 @@ typedef enum {
 
 typedef struct __NSObjectFileImage*  NSObjectFileImage;
 
 
 typedef struct __NSObjectFileImage*  NSObjectFileImage;
 
+
+
 /* NSObjectFileImage can only be used with MH_BUNDLE files */
 /* 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);
+extern NSObjectFileImageReturnCode NSCreateObjectFileImageFromFile(const char* pathName, NSObjectFileImage *objectFileImage)               __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, "dlopen()");
+extern NSObjectFileImageReturnCode NSCreateObjectFileImageFromMemory(const void *address, size_t size, NSObjectFileImage *objectFileImage) __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, "");
+extern bool                        NSDestroyObjectFileImage(NSObjectFileImage objectFileImage)                                             __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, "dlclose()");
+
+extern uint32_t     NSSymbolDefinitionCountInObjectFileImage(NSObjectFileImage objectFileImage)                   __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, "");
+extern const char*  NSSymbolDefinitionNameInObjectFileImage(NSObjectFileImage objectFileImage, uint32_t ordinal)  __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, "");
+extern uint32_t     NSSymbolReferenceCountInObjectFileImage(NSObjectFileImage objectFileImage)                    __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, "");
+extern const char*  NSSymbolReferenceNameInObjectFileImage(NSObjectFileImage objectFileImage, uint32_t ordinal, bool *tentative_definition) __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, "");
+extern bool         NSIsSymbolDefinedInObjectFileImage(NSObjectFileImage objectFileImage, const char* symbolName) __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.4, "dlsym()");
+extern void*        NSGetSectionDataInObjectFileImage(NSObjectFileImage objectFileImage, const char* segmentName, const char* sectionName, size_t *size) __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, "getsectiondata()");
 
 typedef struct __NSModule* NSModule;
 
 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 const char*  NSNameOfModule(NSModule m)         __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, "");
+extern const char*  NSLibraryNameForModule(NSModule m) __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, "");
 
 
-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);
+extern NSModule NSLinkModule(NSObjectFileImage objectFileImage, const char* moduleName, uint32_t options) __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, "dlopen()");
 #define NSLINKMODULE_OPTION_NONE                         0x0
 #define NSLINKMODULE_OPTION_BINDNOW                      0x1
 #define NSLINKMODULE_OPTION_PRIVATE                      0x2
 #define NSLINKMODULE_OPTION_NONE                         0x0
 #define NSLINKMODULE_OPTION_BINDNOW                      0x1
 #define NSLINKMODULE_OPTION_PRIVATE                      0x2
@@ -159,27 +180,27 @@ extern NSModule NSLinkModule(NSObjectFileImage objectFileImage, const char* modu
 #define NSLINKMODULE_OPTION_DONT_CALL_MOD_INIT_ROUTINES  0x8
 #define NSLINKMODULE_OPTION_TRAILING_PHYS_NAME          0x10
 
 #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);
+extern bool NSUnLinkModule(NSModule module, uint32_t options) __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, "");
 #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;
 #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);
+extern bool     NSIsSymbolNameDefined(const char* symbolName)                                                    __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.4, "dlsym()");
+extern bool     NSIsSymbolNameDefinedWithHint(const char* symbolName, const char* libraryNameHint)               __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.4, "dlsym()");
+extern bool     NSIsSymbolNameDefinedInImage(const struct mach_header* image, const char* symbolName)            __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.4, "dlsym()");
+extern NSSymbol NSLookupAndBindSymbol(const char* symbolName)                                                    __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.4, "dlsym()");
+extern NSSymbol NSLookupAndBindSymbolWithHint(const char* symbolName, const char* libraryNameHint)               __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.4, "dlsym()");
+extern NSSymbol NSLookupSymbolInModule(NSModule module, const char* symbolName)                                  __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, "dlsym()");
+extern NSSymbol NSLookupSymbolInImage(const struct mach_header* image, const char* symbolName, uint32_t options) __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, "dlsym()");
 #define NSLOOKUPSYMBOLINIMAGE_OPTION_BIND            0x0
 #define NSLOOKUPSYMBOLINIMAGE_OPTION_BIND_NOW        0x1
 #define NSLOOKUPSYMBOLINIMAGE_OPTION_BIND_FULLY      0x2
 #define NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR 0x4
 #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);
+extern const char*  NSNameOfSymbol(NSSymbol symbol)    __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, "");
+extern void *       NSAddressOfSymbol(NSSymbol symbol) __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, "dlsym()");
+extern NSModule     NSModuleForSymbol(NSSymbol symbol) __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, "dladdr()");
 
 /* error handling API */
 typedef enum {
 
 /* error handling API */
 typedef enum {
@@ -207,7 +228,7 @@ typedef enum {
     NSOtherErrorInvalidArgs
 } NSOtherErrorNumbers;
 
     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);
+extern void NSLinkEditError(NSLinkEditErrors *c, int *errorNumber, const char** fileName, const char** errorString) __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, "dlerror()");
 
 typedef struct {
      void     (*undefined)(const char* symbolName);
 
 typedef struct {
      void     (*undefined)(const char* symbolName);
@@ -216,28 +237,27 @@ typedef struct {
                           const char* fileName, const char* errorString);
 } NSLinkEditErrorHandlers;
 
                           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 void NSInstallLinkEditErrorHandlers(const NSLinkEditErrorHandlers *handlers) __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, "");
 
 
-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);
+extern bool                      NSAddLibrary(const char* pathName)                   __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.4, "dlopen()");
+extern bool                      NSAddLibraryWithSearching(const char* pathName)      __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.4, "dlopen()");
+extern const struct mach_header* NSAddImage(const char* image_name, uint32_t options) __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, "dlopen()");
 #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
 
 #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);
+extern bool _dyld_present(void)                                                              __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, "always true");
+extern bool _dyld_launched_prebound(void)                                                    __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, "moot");
+extern bool _dyld_all_twolevel_modules_prebound(void)                                        __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.3, 10.5, "moot");
+extern bool _dyld_bind_fully_image_containing_address(const void* address)                   __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, "dlopen(RTLD_NOW)");
+extern bool _dyld_image_containing_address(const void* address)                              __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.3, 10.5, "dladdr()");
+extern void _dyld_lookup_and_bind(const char* symbol_name, void **address, NSModule* module) __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.4, "dlsym()");
+extern void _dyld_lookup_and_bind_with_hint(const char* symbol_name, const char* library_name_hint, void** address, NSModule* module) __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.4, "dlsym()");
+extern void _dyld_lookup_and_bind_fully(const char* symbol_name, void** address, NSModule* module) __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, "dlsym()");
+
+extern const struct mach_header*  _dyld_get_image_header_containing_address(const void* address) __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.3, 10.5, "dladdr()");
 
 
 #if __cplusplus
 
 
 #if __cplusplus
index eaadf6488b8c5e32e81b0f7ea05799c23f11093d..f2495159dceeec1115f88e5d48264cdf5420e431 100644 (file)
 #ifndef _DYLD_GDB_
 #define _DYLD_GDB_
 
 #ifndef _DYLD_GDB_
 #define _DYLD_GDB_
 
-/*
- * For Mac OS X 10.4 or later, use the interface in mach-o/dylib_images.h
- */
-#include <mach-o/dyld_images.h>
-
-#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_ */
 
 #endif /* _DYLD_GDB_ */
index 25e4afc60d4731af8fdc43ff1e2ad1ac58b3ef92..4b2da8f13b5a738f4d4c8626f2f9a306353b3233 100644 (file)
@@ -135,11 +135,13 @@ struct dyld_all_image_infos {
 #if __LP64__
        uintptr_t                                               reserved[13-(DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT/2)];
 #else
 #if __LP64__
        uintptr_t                                               reserved[13-(DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT/2)];
 #else
-       uintptr_t                                               reserved[12-DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT];
+       uintptr_t                                               reserved[13-DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT];
 #endif
 #endif
+       /* the following field is only in version 16 (macOS 10.13, iOS 11.0) and later */
+    uintptr_t                       compact_dyld_image_info_addr;
+    size_t                          compact_dyld_image_info_size;
 };
 
 };
 
-
 /*
  * 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
 /*
  * 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
@@ -158,7 +160,7 @@ struct dyld_shared_cache_ranges {
                uintptr_t       length;
        }                                                       ranges[4];                      /* max regions */
 };
                uintptr_t       length;
        }                                                       ranges[4];                      /* max regions */
 };
-extern struct dyld_shared_cache_ranges dyld_shared_cache_ranges;
+extern struct dyld_shared_cache_ranges dyld_shared_cache_ranges __attribute__((visibility("hidden")));
 
 
 
 
 
 
index 2cd4006c1bfea1dfb7d25870a4c18d49d8186175..93f1b90ffab3dca98f24d865e39851743ab86a48 100644 (file)
@@ -26,6 +26,7 @@
 
 #include <stdbool.h>
 #include <Availability.h>
 
 #include <stdbool.h>
 #include <Availability.h>
+#include <TargetConditionals.h>
 #include <mach-o/dyld.h>
 #include <mach-o/dyld_images.h>
 
 #include <mach-o/dyld.h>
 #include <mach-o/dyld_images.h>
 
@@ -35,11 +36,6 @@ extern "C" {
 
 
 
 
 
 
-//
-// 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
 //
 //
 // private interface between libSystem.dylib and dyld
 //
@@ -136,14 +132,6 @@ dyld_enumerate_tlv_storage(dyld_tlv_state_change_handler handler);
 extern intptr_t _dyld_get_image_slide(const struct mach_header* mh);
 
 
 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
 {
 
 struct dyld_unwind_sections
 {
@@ -164,7 +152,9 @@ struct dyld_unwind_sections
 //  info->compact_unwind_section_length        length of __TEXT/__unwind_info section
 //
 // Exists in Mac OS X 10.6 and later 
 //  info->compact_unwind_section_length        length of __TEXT/__unwind_info section
 //
 // Exists in Mac OS X 10.6 and later 
+#if !__USING_SJLJ_EXCEPTIONS__
 extern bool _dyld_find_unwind_sections(void* addr, struct dyld_unwind_sections* info);
 extern bool _dyld_find_unwind_sections(void* addr, struct dyld_unwind_sections* info);
+#endif
 
 
 //
 
 
 //
@@ -205,8 +195,8 @@ 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.
 //
 // 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
+// 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 
 // platform specific sdk version use dyld_get_program_sdk_watch_os_version().
 //
 // Exists in Mac OS X 10.8 and later 
@@ -214,20 +204,36 @@ extern uint32_t dyld_get_sdk_version(const struct mach_header* mh);
 extern uint32_t dyld_get_program_sdk_version();
 
 
 extern uint32_t dyld_get_program_sdk_version();
 
 
-// Watch OS only.
+#if __WATCH_OS_VERSION_MIN_REQUIRED
+// watchOS only.
 // This finds the Watch OS SDK version that the main executable was built against.
 // Exists in Watch OS 2.0 and later
 // 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);
+extern uint32_t dyld_get_program_sdk_watch_os_version() __IOS_UNAVAILABLE __OSX_UNAVAILABLE __WATCHOS_AVAILABLE(2.0);
 
 
 
 
-// Watch OS only.
+// watchOS 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 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);
+#endif
 
 
 
 
+#if TARGET_OS_BRIDGE
+// bridgeOS only.
+// This finds the bridgeOS SDK version that the main executable was built against.
+// Exists in bridgeOSOS 2.0 and later
+extern uint32_t dyld_get_program_sdk_bridge_os_version();
+
+// bridgeOS 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 bridgeOS version (e.g. 2.0).
+// Exists in bridgeOS 2.0 and later
+extern uint32_t dyld_get_program_min_bridge_os_version();
+#endif
+
 //
 // 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.
 //
 // 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.
@@ -301,9 +307,13 @@ struct dyld_shared_cache_dylib_text_info {
        uint64_t                textSegmentSize; 
        uuid_t                  dylibUuid;
        const char*             path;                   // pointer invalid at end of iterations
        uint64_t                textSegmentSize; 
        uuid_t                  dylibUuid;
        const char*             path;                   // pointer invalid at end of iterations
+       // following fields all exist in version 2
+       uint64_t        textSegmentOffset;  // offset from start of cache
 };
 typedef struct dyld_shared_cache_dylib_text_info dyld_shared_cache_dylib_text_info;
 
 };
 typedef struct dyld_shared_cache_dylib_text_info dyld_shared_cache_dylib_text_info;
 
+
+#ifdef __BLOCKS__
 //
 // 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.
 //
 // 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.
@@ -321,6 +331,7 @@ extern int dyld_shared_cache_iterate_text(const uuid_t cacheUuid, void (^callbac
 // 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));
 // 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));
+#endif /* __BLOCKS */
 
 
 //
 
 
 //
@@ -389,6 +400,32 @@ struct dyld_abort_payload {
 };
 typedef struct dyld_abort_payload dyld_abort_payload;
 
 };
 typedef struct dyld_abort_payload dyld_abort_payload;
 
+
+// These global variables are implemented in libdyld.dylib
+// Old programs that used crt1.o also defined these globals.
+// The ones in dyld are not used when an old program is run.
+extern int          NXArgc;
+extern const char** NXArgv;
+extern       char** environ;       // POSIX says this not const, because it pre-dates const
+extern const char*  __progname;
+
+
+// called by libSystem_initializer only
+extern void _dyld_initializer();
+
+// never called from source code. Used by static linker to implement lazy binding
+extern void dyld_stub_binder() __asm__("dyld_stub_binder");
+
+
+// called by exit() before it calls cxa_finalize() so that thread_local
+// objects are destroyed before global objects.
+extern void _tlv_exit();
+
+
+// temp exports to keep tapi happy, until ASan stops using dyldVersionNumber
+extern double      dyldVersionNumber;
+extern const char* dyldVersionString;
+
 #if __cplusplus
 }
 #endif /* __cplusplus */
 #if __cplusplus
 }
 #endif /* __cplusplus */
index 8e7629f80a801d39b209b19f16cefa79b60687fb..7db413f8075893481ac36d17ee1c80ab522d13e8 100644 (file)
@@ -43,10 +43,11 @@ extern "C" {
 // 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.
 //
 // 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 };
 
 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[]);
 
 
-void _dyld_debugger_notification(enum dyld_notify_mode, unsigned long count, uint64_t machHeaders[]);
 
 
 struct dyld_process_cache_info {
 
 
 struct dyld_process_cache_info {
diff --git a/interlinked-dylibs/AdjustForNewSegmentLocation.cpp b/interlinked-dylibs/AdjustForNewSegmentLocation.cpp
deleted file mode 100644 (file)
index f03abc6..0000000
+++ /dev/null
@@ -1,1059 +0,0 @@
-/* -*- 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 <dirent.h>
-#include <sys/errno.h>
-#include <sys/fcntl.h>
-#include <mach-o/loader.h>
-#include <mach-o/fat.h>
-#include <assert.h>
-
-#include <fstream>
-#include <iostream>
-#include <string>
-#include <algorithm>
-#include <unordered_map>
-#include <unordered_set>
-
-#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 <typename P>
-class Adjustor {
-public:
-    Adjustor(void* cacheBuffer, macho_header<P>* mh, const std::vector<uint64_t>& segNewStartAddresses,
-             const std::vector<uint64_t>& segCacheFileOffset, const std::vector<uint64_t>& segCacheFileSizes);
-    void adjustImageForNewSegmentLocations(std::vector<void*>& pointersForASLR);
-
-private:
-       void                    adjustReferencesUsingInfoV2(std::vector<void*>& 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<void*>& pointersForASLR, uint32_t*& lastMappedAddr32,
-                                    uint32_t& lastKind, uint64_t& lastToNewAddress);
-    void            adjustDataPointers(std::vector<void*>& pointersForASLR);
-    void            slidePointer(int segIndex, uint64_t segOffset, uint8_t type, std::vector<void*>& pointersForASLR);
-    void            adjustSymbolTable();
-    void            adjustExportsTrie(std::vector<uint8_t>& 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<P>*                        _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<P>*                _symTabCmd          = nullptr;
-    macho_dysymtab_command<P>*              _dynSymTabCmd       = nullptr;
-    macho_dyld_info_command<P>*             _dyldInfo           = nullptr;
-    macho_linkedit_data_command<P>*         _splitSegInfoCmd    = nullptr;
-    macho_linkedit_data_command<P>*         _functionStartsCmd  = nullptr;
-    macho_linkedit_data_command<P>*         _dataInCodeCmd      = nullptr;
-    std::vector<uint64_t>                   _segOrigStartAddresses;
-    std::vector<uint64_t>                   _segNewStartAddresses;
-    std::vector<uint64_t>                   _segCacheOffsets;
-    std::vector<uint64_t>                   _segCacheSizes;
-    std::vector<uint64_t>                   _segSlides;
-    std::vector<macho_segment_command<P>*>  _segCmds;
-};
-
-template <typename P>
-Adjustor<P>::Adjustor(void* cacheBuffer, macho_header<P>* mh, const std::vector<uint64_t>& segNewStartAddresses,
-                      const std::vector<uint64_t>& segCacheFileOffsets, const std::vector<uint64_t>& segCacheFileSizes)
-    : _mh(mh), _cacheBuffer(cacheBuffer), _segNewStartAddresses(segNewStartAddresses),
-      _segCacheOffsets(segCacheFileOffsets), _segCacheSizes(segCacheFileSizes)
-{
-    macho_segment_command<P>* segCmd;
-    const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)mh + sizeof(macho_header<P>));
-    const uint32_t cmd_count = mh->ncmds();
-    const macho_load_command<P>* 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<P>*)cmd)->name();
-                break;
-            case LC_SYMTAB:
-                _symTabCmd = (macho_symtab_command<P>*)cmd;
-                break;
-            case LC_DYSYMTAB:
-                _dynSymTabCmd = (macho_dysymtab_command<P>*)cmd;
-                break;
-            case LC_DYLD_INFO:
-            case LC_DYLD_INFO_ONLY:
-                _dyldInfo = (macho_dyld_info_command<P>*)cmd;
-                break;
-            case LC_SEGMENT_SPLIT_INFO:
-                _splitSegInfoCmd = (macho_linkedit_data_command<P>*)cmd;
-                break;
-            case LC_FUNCTION_STARTS:
-                _functionStartsCmd = (macho_linkedit_data_command<P>*)cmd;
-                break;
-            case LC_DATA_IN_CODE:
-                _dataInCodeCmd = (macho_linkedit_data_command<P>*)cmd;
-                break;
-            case macho_segment_command<P>::CMD:
-                segCmd = (macho_segment_command<P>*)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<P>*)(((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 <typename P>
-void Adjustor<P>::adjustImageForNewSegmentLocations(std::vector<void*>& pointersForASLR)
-{
-    if ( _splitSegInfoV2 ) {
-        adjustReferencesUsingInfoV2(pointersForASLR);
-    }
-    else {
-        adjustDataPointers(pointersForASLR);
-        adjustCode();
-    }
-    adjustSymbolTable();
-    rebuildLinkEditAndLoadCommands();
-}
-
-template <typename P>
-uint64_t Adjustor<P>::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 <typename P>
-void Adjustor<P>::rebuildLinkEditAndLoadCommands()
-{
-    // Exports trie is only data structure in LINKEDIT that might grow
-    std::vector<uint8_t> 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<P>);
-    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<P>* const cmds = (macho_load_command<P>*)((uint8_t*)_mh + sizeof(macho_header<P>));
-    uint32_t cmd_count = _mh->ncmds();
-    const macho_load_command<P>* 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<P>*           symTabCmd;
-        macho_dysymtab_command<P>*         dynSymTabCmd;
-        macho_dyld_info_command<P>*        dyldInfo;
-        macho_linkedit_data_command<P>*    functionStartsCmd;
-        macho_linkedit_data_command<P>*    dataInCodeCmd;
-        macho_linkedit_data_command<P>*    splitSegInfoCmd;
-        macho_segment_command<P>*          segCmd;
-        macho_routines_command<P>*         routinesCmd;
-        macho_dylib_command<P>*            dylibIDCmd;
-        uint32_t cmdSize = cmd->cmdsize();
-        int32_t segFileOffsetDelta;
-        bool remove = false;
-        switch ( cmd->cmd() ) {
-            case LC_ID_DYLIB:
-                dylibIDCmd = (macho_dylib_command<P>*)cmd;
-                dylibIDCmd->set_timestamp(2); // match what static linker sets in LC_LOAD_DYLIB
-                break;
-            case LC_SYMTAB:
-                symTabCmd = (macho_symtab_command<P>*)cmd;
-                symTabCmd->set_symoff(linkeditStartOffset+symbolTableOffset);
-                symTabCmd->set_stroff(linkeditStartOffset+symbolStringsOffset);
-              break;
-            case LC_DYSYMTAB:
-                dynSymTabCmd = (macho_dysymtab_command<P>*)cmd;
-                dynSymTabCmd->set_indirectsymoff(linkeditStartOffset+indirectTableOffset);
-                break;
-            case LC_DYLD_INFO:
-            case LC_DYLD_INFO_ONLY:
-                dyldInfo = (macho_dyld_info_command<P>*)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<P>*)cmd;
-                functionStartsCmd->set_dataoff(linkeditStartOffset+funcStartsOffset);
-               break;
-            case LC_DATA_IN_CODE:
-                dataInCodeCmd = (macho_linkedit_data_command<P>*)cmd;
-                dataInCodeCmd->set_dataoff(linkeditStartOffset+dataInCodeOffset);
-                break;
-            case macho_routines_command<P>::CMD:
-                routinesCmd = (macho_routines_command<P>*)cmd;
-                routinesCmd->set_init_address(routinesCmd->init_address()+slideForOrigAddress(routinesCmd->init_address()));
-                break;
-            case macho_segment_command<P>::CMD:
-                segCmd = (macho_segment_command<P>*)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<P>* const sectionsStart = (macho_section<P>*)((uint8_t*)segCmd + sizeof(macho_segment_command<P>));
-                    macho_section<P>* const sectionsEnd = &sectionsStart[segCmd->nsects()];
-                    for (macho_section<P>*  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<P>*)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<P>* nextCmd = (macho_load_command<P>*)(((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 <typename P>
-void Adjustor<P>::adjustSymbolTable()
-{
-    macho_nlist<P>*  symbolTable = (macho_nlist<P>*)&_linkeditBias[_symTabCmd->symoff()];
-
-    // adjust global symbol table entries
-    macho_nlist<P>* lastExport = &symbolTable[_dynSymTabCmd->iextdefsym()+_dynSymTabCmd->nextdefsym()];
-    for (macho_nlist<P>* 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<P>*  lastLocal = &symbolTable[_dynSymTabCmd->ilocalsym()+_dynSymTabCmd->nlocalsym()];
-    for (macho_nlist<P>* 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 <typename P>
-void Adjustor<P>::slidePointer(int segIndex, uint64_t segOffset, uint8_t type, std::vector<void*>& 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 <typename P>
-void Adjustor<P>::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<void*>& 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 <typename P>
-void Adjustor<P>::adjustReferencesUsingInfoV2(std::vector<void*>& 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<uint64_t> sectionSlides;
-       std::vector<uint64_t> sectionNewAddress;
-       std::vector<uint8_t*> 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<P>* segCmd = _segCmds[segmentIndex];
-        macho_section<P>* const sectionsStart = (macho_section<P>*)((char*)segCmd + sizeof(macho_segment_command<P>));
-        macho_section<P>* const sectionsEnd = &sectionsStart[segCmd->nsects()];
-        for(macho_section<P>* 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                 :== <count> FromToSection+
-       // FromToSection :== <from-sect-index> <to-sect-index> <count> ToOffset+
-       // ToOffset              :== <to-sect-offset-delta> <count> FromOffset+
-       // FromOffset    :== <kind> <count> <from-sect-offset-delta>
-       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 <typename P>
-void Adjustor<P>::adjustDataPointers(std::vector<void*>& 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 <typename P>
-void Adjustor<P>::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 <typename P>
-void Adjustor<P>::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:  [ <kind> [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 <typename P>
-void Adjustor<P>::adjustExportsTrie(std::vector<uint8_t>& 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<ExportInfoTrie::Entry> originalExports;
-    if ( !ExportInfoTrie::parseTrie(start, end, originalExports) ) {
-        terminate("malformed exports trie in %s", _installName);
-    }
-
-    std::vector<ExportInfoTrie::Entry> 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<uint64_t>& segNewStartAddresses,
-                                                    const std::vector<uint64_t>& segCacheFileOffsets,
-                                                    const std::vector<uint64_t>& segCacheFileSizes,
-                                                    std::vector<void*>& 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<Pointer32<LittleEndian>> adjustor32(_buffer.get(), (macho_header<Pointer32<LittleEndian>>*)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<Pointer64<LittleEndian>> adjustor64(_buffer.get(), (macho_header<Pointer64<LittleEndian>>*)mh, segNewStartAddresses, segCacheFileOffsets, segCacheFileSizes);
-                adjustor64.adjustImageForNewSegmentLocations(pointersForASLR);
-            }
-            break;
-        default:
-            terminate("unsupported arch 0x%08X", _arch.arch);
-    }
-}
-
-
-
-
-
diff --git a/interlinked-dylibs/BindAllImages.cpp b/interlinked-dylibs/BindAllImages.cpp
deleted file mode 100644 (file)
index 0fbc09c..0000000
+++ /dev/null
@@ -1,705 +0,0 @@
-/* -*- 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 <dirent.h>
-#include <sys/errno.h>
-#include <sys/fcntl.h>
-#include <mach-o/loader.h>
-#include <mach-o/fat.h>
-#include <assert.h>
-
-#include <fstream>
-#include <iostream>
-#include <string>
-#include <algorithm>
-#include <unordered_map>
-#include <unordered_set>
-
-#include "dyld_cache_config.h"
-
-#include "MachOProxy.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 <typename P>
-class BindInfo {
-public:
-    BindInfo(void* cacheBuffer, const std::map<const MachOProxy*, std::vector<SharedCache::SegmentInfo>>& segmentMap, macho_header<P>* mh, MachOProxy* proxy);
-
-    void setReExports(const std::unordered_map<std::string, BindInfo<P>*>& dylibPathToBindInfo);
-    void setDependentDylibs(const std::unordered_map<std::string, BindInfo<P>*>& dylibPathToBindInfo);
-    void bind(const std::unordered_map<std::string, BindInfo<P>*>& dylibPathToBindInfo, std::vector<void*>& pointersForASLR);
-
-    static void bindAllImagesInCache(void* cacheBuffer, const std::vector<MachOProxy*> dylibs, const std::map<const MachOProxy*, std::vector<SharedCache::SegmentInfo>>& segmentMap, std::vector<void*>& pointersForASLR);
-
-private:
-    typedef typename P::uint_t pint_t;
-    typedef typename P::E E;
-
-    void bindImmediates(const std::unordered_map<std::string, BindInfo<P>*>& dylibPathToBindInfo, std::vector<void*>& pointersForASLR);
-    void bindLazyPointers(const std::unordered_map<std::string, BindInfo<P>*>& dylibPathToBindInfo, std::vector<void*>& 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<std::string, BindInfo<P>*>& dylibPathToBindInfo, std::vector<void*>& pointersForASLR);
-
-    bool findExportedSymbolAddress(const char* symbolName, const std::unordered_map<std::string, BindInfo<P>*>& dylibPathToBindInfo,
-                                   pint_t* address, BindInfo<P>** 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<std::string, std::unordered_set<BindInfo<P>*>> ResolverClientsMap;
-    typedef std::unordered_map<std::string, pint_t> ResolverToBlessedLazyPointerMap;
-
-    void*                                       _cacheBuffer;
-    macho_header<P>*                            _mh;
-    MachOProxy*                                 _proxy;
-    const uint8_t*                              _linkeditBias;
-    const char*                                 _installName;
-    const macho_symtab_command<P>*              _symTabCmd;
-    const macho_dysymtab_command<P>*            _dynSymTabCmd;
-    const macho_dyld_info_command<P>*           _dyldInfo;
-    std::vector<std::string>                    _dependentPaths;
-    std::vector<uint64_t>                       _segSizes;
-    std::vector<uint64_t>                       _segCacheOffsets;
-    std::vector<const macho_segment_command<P>*>_segCmds;
-    const std::map<const MachOProxy*, std::vector<SharedCache::SegmentInfo>>& _segmentMap;
-    std::vector<std::string>                    _reExportedDylibNames;
-    std::vector<BindInfo<P>*>                   _reExportedDylibs;
-    std::vector<BindInfo<P>*>                   _dependentDylibs;
-    pint_t                                      _baseAddress;
-    ResolverClientsMap                          _resolverClients;
-    ResolverToBlessedLazyPointerMap             _resolverBlessedMap;
-};
-
-template <typename P>
-BindInfo<P>::BindInfo(void* cacheBuffer, const std::map<const MachOProxy*, std::vector<SharedCache::SegmentInfo>>& segmentMap, macho_header<P>* mh, MachOProxy* proxy)
-    : _cacheBuffer(cacheBuffer)
-    , _mh(mh)
-    , _segmentMap(segmentMap)
-    , _proxy(proxy)
-    , _linkeditBias((uint8_t*)cacheBuffer)
-    , _symTabCmd(nullptr)
-    , _dynSymTabCmd(nullptr)
-    , _dyldInfo(nullptr)
-    , _baseAddress(0)
-{
-    macho_segment_command<P>* segCmd;
-    macho_dylib_command<P>* dylibCmd;
-    const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)mh + sizeof(macho_header<P>));
-    const uint32_t cmd_count = mh->ncmds();
-    unsigned segIndex = 0;
-    const macho_load_command<P>* cmd = cmds;
-    for (uint32_t i = 0; i < cmd_count; ++i) {
-        switch (cmd->cmd()) {
-            case LC_ID_DYLIB:
-                dylibCmd = (macho_dylib_command<P>*)cmd;
-                _installName = dylibCmd->name();
-                break;
-            case LC_SYMTAB:
-                _symTabCmd = (macho_symtab_command<P>*)cmd;
-                break;
-            case LC_DYSYMTAB:
-                _dynSymTabCmd = (macho_dysymtab_command<P>*)cmd;
-                break;
-            case LC_DYLD_INFO:
-            case LC_DYLD_INFO_ONLY:
-                _dyldInfo = (macho_dyld_info_command<P>*)cmd;
-                break;
-            case LC_REEXPORT_DYLIB:
-                dylibCmd = (macho_dylib_command<P>*)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<P>*)cmd;
-                _dependentPaths.push_back(dylibCmd->name());
-                break;
-           case macho_segment_command<P>::CMD:
-                segCmd = (macho_segment_command<P>*)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<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
-    }
-
-    // if no export info, no _exports map to build
-    if ( _dyldInfo->export_size() == 0 )
-        return;
-    std::vector<ExportInfoTrie::Entry> 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);
-    }
-}
-
-template <typename P>
-void BindInfo<P>::bind(const std::unordered_map<std::string, BindInfo<P>*>& dylibPathToBindInfo, std::vector<void*>& pointersForASLR)
-{
-    bindImmediates(dylibPathToBindInfo, pointersForASLR);
-    bindLazyPointers(dylibPathToBindInfo, pointersForASLR);
-    // weak bind info is processed at launch time
-}
-
-
-template <typename P>
-void BindInfo<P>::setReExports(const std::unordered_map<std::string, BindInfo<P>*>& 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 <typename P>
-void BindInfo<P>::setDependentDylibs(const std::unordered_map<std::string, BindInfo<P>*>& 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 <typename P>
-void BindInfo<P>::bindImmediates(const std::unordered_map<std::string, BindInfo<P>*>& dylibPathToBindInfo, std::vector<void*>& 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 <typename P>
-void BindInfo<P>::bindLazyPointers(const std::unordered_map<std::string, BindInfo<P>*>& dylibPathToBindInfo, std::vector<void*>& 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 <typename P>
-void BindInfo<P>::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<std::string, BindInfo<P>*>& dylibPathToBindInfo, std::vector<void*>& 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<P>* 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<P>* 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);
-    }
-
-
-    // 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 <typename P>
-bool BindInfo<P>::findExportedSymbolAddress(const char* symbolName, const std::unordered_map<std::string, BindInfo<P>*>& dylibPathToBindInfo,
-                                            pint_t* address, BindInfo<P>** foundIn, bool* isResolverSymbol, bool* isAbsolute)
-{
-    auto info = _proxy->symbolInfo(symbolName);
-    if (info != nullptr) {
-        if (info->isSymbolReExport) {
-            const char* importName = symbolName;
-            if (!info->reExportName.empty())
-                importName = info->reExportName.c_str();
-            std::string& depPath = _dependentPaths[info->reExportDylibIndex - 1];
-            auto pos2 = dylibPathToBindInfo.find(depPath);
-            if ( pos2 != dylibPathToBindInfo.end() ) {
-                BindInfo<P>* reExportFrom = pos2->second;
-                return reExportFrom->findExportedSymbolAddress(importName, dylibPathToBindInfo, address, foundIn, isResolverSymbol, isAbsolute);
-            }
-            else {
-                verboseLog("findExportedSymbolAddress(%s) => ???\n", symbolName);
-            }
-        }
-
-        *address = (pint_t)_proxy->addressOf(symbolName, _segmentMap);
-        *foundIn = this;
-        *isResolverSymbol = info->isResolver;
-        *isAbsolute = info->isAbsolute;
-        //verboseLog("findExportedSymbolAddress(%s) => 0x0%llX\n", symbolName, (uint64_t)*address);
-        return true;
-    }
-
-    for (BindInfo<P>* dep : _reExportedDylibs) {
-        if ( dep->findExportedSymbolAddress(symbolName, dylibPathToBindInfo, address, foundIn, isResolverSymbol, isAbsolute) )
-            return true;
-    }
-    return false;
-}
-
-template <typename P>
-typename P::uint_t BindInfo<P>::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;
-    const auto info = _proxy->symbolInfo(resolverSymbolName);
-    if (info) {
-        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<P>* 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<P>* const symbolTable = (macho_nlist<P>*)(&_linkeditBias[_symTabCmd->symoff()]);
-        const char* symbolStringPool = (char*)(&_linkeditBias[_symTabCmd->stroff()]);
-
-        for (const macho_segment_command<P>* seg : _segCmds) {
-            const macho_section<P>* const sectionsStart = (macho_section<P>*)((uint8_t*)seg + sizeof(macho_segment_command<P>));
-            const macho_section<P>* const sectionsEnd = &sectionsStart[seg->nsects()];
-            for (const macho_section<P>* 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<P>* 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<P>* 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 <typename P>
-void BindInfo<P>::switchStubToUseSharedLazyPointer(const std::string& resolverSymbolName, pint_t lpVMAddr)
-{
-    // find named stub
-    const uint32_t* const indirectTable = (uint32_t*)&_linkeditBias[_dynSymTabCmd->indirectsymoff()];
-    const macho_nlist<P>* const symbolTable = (macho_nlist<P>*)(&_linkeditBias[_symTabCmd->symoff()]);
-    const char* symbolStringPool = (char*)(&_linkeditBias[_symTabCmd->stroff()]);
-    for (const macho_segment_command<P>* seg : _segCmds) {
-        macho_section<P>* const sectionsStart = (macho_section<P>*)((char*)seg + sizeof(macho_segment_command<P>));
-        macho_section<P>* const sectionsEnd = &sectionsStart[seg->nsects()];
-        for(macho_section<P>* 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<P>* 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 <typename P>
-void BindInfo<P>::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 <typename P>
-void BindInfo<P>::bindAllImagesInCache(void* cacheBuffer, const std::vector<MachOProxy*> dylibs, const std::map<const MachOProxy*, std::vector<SharedCache::SegmentInfo>>& segmentMap, std::vector<void*>& pointersForASLR)
-{
-    std::unordered_map<std::string, BindInfo<P>*> dylibPathToBindInfo;
-    std::unordered_map<macho_header<P>*, BindInfo<P>*> headersToBindInfo;
-    for (auto& dylib : dylibs) {
-        auto dylibI = segmentMap.find(dylib);
-        assert(dylibI != segmentMap.end());
-        macho_header<P>* mh = (macho_header<P>*)((uint8_t*)cacheBuffer + dylibI->second[0].cacheFileOffset);
-        if (headersToBindInfo.count(mh) == 0) {
-            headersToBindInfo[mh] = new BindInfo<P>(cacheBuffer, segmentMap, mh, dylib);
-        }
-        dylibPathToBindInfo[dylib->installName] = headersToBindInfo[mh];
-        for (const auto& alias : dylib->installNameAliases) {
-            dylibPathToBindInfo[alias] = 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);
-    }
-
-    // clean up
-    for (const auto& entry: headersToBindInfo) {
-        delete entry.second;
-    }
-}
-
-
-} // anonymous namespace
-
-void SharedCache::bindAllImagesInCache(const std::vector<MachOProxy*> dylibs, const std::map<const MachOProxy*, std::vector<SegmentInfo>>& segmentMap, std::vector<void*>& pointersForASLR)
-{
-    switch ( _arch.arch ) {
-        case CPU_TYPE_ARM:
-        case CPU_TYPE_I386:
-            BindInfo<Pointer32<LittleEndian>>::bindAllImagesInCache(_buffer.get(), dylibs, segmentMap, pointersForASLR);
-            break;
-        case CPU_TYPE_X86_64:
-        case CPU_TYPE_ARM64:
-            BindInfo<Pointer64<LittleEndian>>::bindAllImagesInCache(_buffer.get(), dylibs, segmentMap, pointersForASLR);
-            break;
-        default:
-            terminate("unsupported arch 0x%08X", _arch.arch);
-    }
-}
-
-
-
diff --git a/interlinked-dylibs/CodeSigningTypes.h b/interlinked-dylibs/CodeSigningTypes.h
deleted file mode 100644 (file)
index e4a4049..0000000
+++ /dev/null
@@ -1,130 +0,0 @@
-/* -*- 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 <stdint.h>
-#include <stddef.h>
-
-
-//
-// 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/interlinked-dylibs/FileCache.cpp b/interlinked-dylibs/FileCache.cpp
deleted file mode 100644 (file)
index 4178426..0000000
+++ /dev/null
@@ -1,262 +0,0 @@
-/* -*- 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 <dirent.h>
-#include <sys/errno.h>
-#include <sys/fcntl.h>
-#include <sys/param.h>
-#include <mach-o/loader.h>
-#include <mach-o/fat.h>
-#include <mach-o/dyld.h>
-#include <assert.h>
-#include <Availability.h>
-
-#include <fstream>
-#include <iostream>
-#include <sstream>
-#include <string>
-#include <algorithm>
-#include <unordered_map>
-#include <unordered_set>
-
-#include <System/sys/csr.h>
-
-#include "dyld_cache_config.h"
-
-#include "OptimizerBranches.h"
-
-#include "CacheFileAbstraction.hpp"
-
-#include "mega-dylib-utils.h"
-#include "Logging.h"
-
-
-//#include <rootless.h>
-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<std::string> components;
-    std::vector<std::string> 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_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, QOS_CLASS_USER_INITIATED, 0));
-}
-
-
-void FileCache::preflightCache(const std::unordered_set<std::string>& 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) {
-            entries[normalizedPath] = fill(normalizedPath);
-        }
-    });
-}
-
-std::tuple<uint8_t *, struct stat, bool> FileCache::cacheLoad(const std::string path) {
-    bool found = false;
-    std::tuple<uint8_t*, struct stat, bool> retval;
-    std::string normalizedPath = normalize_absolute_file_path(path);
-    cacheBuilderDispatchSync(cache_queue, [=, &found, &retval] {
-        auto entry = entries.find(normalizedPath);
-        if (entry != entries.end()) {
-            retval = entry->second;
-            found = true;
-        }
-    });
-
-    if (!found) {
-        auto info = fill(normalizedPath);
-        cacheBuilderDispatchSync(cache_queue, [=, &found, &retval] {
-            auto entry = entries.find(normalizedPath);
-            if (entry != entries.end()) {
-                retval = entry->second;
-            }else {
-                retval = entries[normalizedPath] = info;
-                retval = info;
-            }
-        });
-    }
-
-    return retval;
-}
-
-//FIXME error handling
-std::tuple<uint8_t*, struct stat, bool>  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);
-        return std::make_tuple((uint8_t*)(-1), stat_buf, false);
-    }
-
-    if ( fstat(fd, &stat_buf) == -1) {
-        verboseLog("can't stat open file '%s', errno=%d", path.c_str(), errno);
-        ::close(fd);
-        return std::make_tuple((uint8_t*)(-1), stat_buf, false);
-    }
-
-    if ( stat_buf.st_size < 4096 ) {
-        verboseLog("file too small '%s'", path.c_str());
-        ::close(fd);
-        return std::make_tuple((uint8_t*)(-1), stat_buf, false);
-    }
-
-    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 file at %s failed, errno=%d", path.c_str(), errno);
-        ::close(fd);
-        return std::make_tuple((uint8_t*)(-1), stat_buf, false);
-    }
-
-    ::close(fd);
-
-    //PERF-HACK: touch bits of the MachO before we need them to speed things up on a spinning disk
-    madvise((void*)buffer_ptr, 4096, MADV_WILLNEED);
-
-    //if it is fat we need to touch each architecture
-    const fat_header* fh = (fat_header*)buffer_ptr;
-    if ( OSSwapBigToHostInt32( fh->magic ) == FAT_MAGIC ) {
-        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 ) {
-            uint32_t fileOffset = OSSwapBigToHostInt32( slices[i].offset );
-            madvise((void*)((uint8_t *)buffer_ptr+fileOffset), 4096, MADV_WILLNEED);
-        }
-    }
-
-    return std::make_tuple((uint8_t*)buffer_ptr, stat_buf, rootlessProtected);
-}
-
-
-
-
-
-
diff --git a/interlinked-dylibs/Logging.cpp b/interlinked-dylibs/Logging.cpp
deleted file mode 100644 (file)
index 296b551..0000000
+++ /dev/null
@@ -1,252 +0,0 @@
-/* -*- 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 <dispatch/dispatch.h>
-#include <string.h>
-#include <stdlib.h>
-#include <set>
-
-#include <assert.h>
-#include "mega-dylib-utils.h"
-
-#include "Logging.h"
-
-//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<LoggingContext>& 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<LoggingContext> getLoggingContext()
-{
-    if (void* val = pthread_getspecific(getLoggingContextKey()))
-        return *((std::shared_ptr<LoggingContext>*)val);
-    return nullptr;
-}
-
-void runBody(void* Ctx)
-{
-    std::unique_ptr<std::function<void(void)>>
-    Body(reinterpret_cast<std::function<void(void)>*>(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)
-{
-    dispatch_async(getLogQueue(), ^{
-        (void)fprintf(fd, "%s", str);
-        free((void*)str);
-    });
-}
-
-#define VLOG(header)                                                                 \
-    va_list list;                                                                    \
-    va_start(list, format);                                                          \
-    char *temp, *temp2;                                                              \
-    vasprintf(&temp, format, list);                                                  \
-    auto ctx = getLoggingContext();                                                  \
-    if (ctx && !ctx->name().empty()) {                                               \
-        asprintf(&temp2, "[%s] %s%s\n", ctx->name().c_str(), header, temp);          \
-    } else {                                                                         \
-        asprintf(&temp2, "%s%s\n", header, temp);                                    \
-    }                                                                                \
-    free(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<std::string> 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* blockTemp;
-    vasprintf(&blockTemp, format, list);
-
-    auto ctx = getLoggingContext();
-    if (ctx) {
-        for (auto& target : ctx->targets().second) {
-            ctx->targets().first->configuration(target.first).architecture(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(temp2);
-    } 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/interlinked-dylibs/Logging.h b/interlinked-dylibs/Logging.h
deleted file mode 100644 (file)
index 807261a..0000000
+++ /dev/null
@@ -1,132 +0,0 @@
-/* -*- 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 <stdio.h>
-#include <pthread.h>
-#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<LoggingContext>& context);
-std::shared_ptr<LoggingContext> 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 <typename BodyFtor>
-std::function<void(void)>* heapSafe(BodyFtor&& Body, std::shared_ptr<LoggingContext> context)
-{
-    auto retval = new std::function<void(void)>([ B = std::move(Body), context ]() mutable {
-        if (!context || !context->isTainted()) {
-
-            void* oldCtx = pthread_getspecific(getLoggingContextKey());
-            setLoggingContext(context);
-            try {
-                B();
-            } catch (std::string exception) {
-                if (context) {
-                    WarningTargets warningTargets = context->targets();
-                    for (auto& target : warningTargets.second) {
-                        warningTargets.first->configuration(target.first).architecture(target.second).results.failure = exception;
-                    }
-                    context->taint();
-                }
-            } catch (...) {
-                if (context) {
-                    context->taint();
-                }
-            }
-            pthread_setspecific(getLoggingContextKey(), oldCtx);
-        }
-    });
-    return retval;
-}
-
-template <typename BodyFtor>
-void cacheBuilderDispatchAsync(dispatch_queue_t queue, BodyFtor&& Body)
-{
-    dispatch_async_f(queue, heapSafe(Body, getLoggingContext()), runBody);
-}
-
-template <typename BodyFtor>
-void cacheBuilderDispatchGroupAsync(dispatch_group_t group, dispatch_queue_t queue, BodyFtor&& Body)
-{
-    dispatch_group_async_f(group, queue, heapSafe(Body, getLoggingContext()), runBody);
-}
-
-template <typename BodyFtor>
-void cacheBuilderDispatchSync(dispatch_queue_t queue, BodyFtor&& Body)
-{
-    dispatch_sync_f(queue, heapSafe(Body, getLoggingContext()), runBody);
-}
-
-#endif /* LOGGING_H */
diff --git a/interlinked-dylibs/MachOProxy.cpp b/interlinked-dylibs/MachOProxy.cpp
deleted file mode 100644 (file)
index a7418b9..0000000
+++ /dev/null
@@ -1,504 +0,0 @@
-//
-//  DylibProxy.cpp
-//  dyld
-//
-//  Created by Louis Gerbarg on 1/27/16.
-//
-//
-
-#include <mach-o/loader.h>
-#include <mach-o/fat.h>
-
-#include "mega-dylib-utils.h"
-#include "Logging.h"
-
-#include "Trie.hpp"
-#include "MachOProxy.h"
-
-#ifndef EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE
-#define EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE 0x02
-#endif
-
-namespace {
-std::vector<MachOProxy*> mapMachOFile(const std::string& buildPath, const std::string& path)
-{
-    std::vector<MachOProxy*> retval;
-    const uint8_t* p = (uint8_t*)( -1 );
-    struct stat stat_buf;
-       bool rootless;
-
-    std::tie(p, stat_buf, rootless) = fileCache.cacheLoad(buildPath);
-
-    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<uint32_t>( stat_buf.st_size );
-                retval.push_back(new MachOProxy(buildPath, path, stringForArch(arch), stat_buf.st_ino, stat_buf.st_mtime, fileOffset, fileSize, rootless));
-                //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<uint32_t>( stat_buf.st_size );
-        retval.push_back(new MachOProxy(buildPath, path, stringForArch(arch), stat_buf.st_ino, stat_buf.st_mtime, fileOffset, fileSize, rootless));
-        //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;
-}
-
-} /* Anonymous namespace */
-
-template <typename P>
-std::vector<std::string> MachOProxy::dependencies()
-{
-    const uint8_t*                     buffer = getBuffer();
-    const macho_header<P>*             mh = (const macho_header<P>*)buffer;
-    const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)mh + sizeof(macho_header<P>));
-    const uint32_t                     cmd_count = mh->ncmds();
-    const macho_load_command<P>*       cmd = cmds;
-    std::vector<std::string>           retval;
-
-    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: {
-                macho_dylib_command<P>* dylib = (macho_dylib_command<P>*)cmd;
-                std::string             depName = dylib->name();
-
-                retval.push_back(depName);
-            } break;
-        }
-        cmd = (const macho_load_command<P>*)(((uint8_t*)cmd) + cmd->cmdsize());
-    }
-
-    return retval;
-}
-
-template <typename P>
-std::vector<std::string> MachOProxy::reexports()
-{
-    const uint8_t*                     buffer = getBuffer();
-    const macho_header<P>*             mh = (const macho_header<P>*)buffer;
-    const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)mh + sizeof(macho_header<P>));
-    const uint32_t                     cmd_count = mh->ncmds();
-    const macho_load_command<P>*       cmd = cmds;
-    std::vector<std::string>           retval;
-
-    for (uint32_t i = 0; i < cmd_count; ++i) {
-        switch (cmd->cmd()) {
-        case LC_REEXPORT_DYLIB: {
-            macho_dylib_command<P>* dylib = (macho_dylib_command<P>*)cmd;
-            std::string             depName = dylib->name();
-
-            retval.push_back(depName);
-        } break;
-        }
-        cmd = (const macho_load_command<P>*)(((uint8_t*)cmd) + cmd->cmdsize());
-    }
-
-    return retval;
-}
-
-std::vector<std::string> MachOProxy::dependencies()
-{
-    switch (archForString(arch).arch) {
-        case CPU_TYPE_ARM:
-        case CPU_TYPE_I386:
-            return dependencies<Pointer32<LittleEndian>>();
-        case CPU_TYPE_X86_64:
-        case CPU_TYPE_ARM64:
-            return dependencies<Pointer64<LittleEndian>>();
-            break;
-        default:
-            return std::vector<std::string>();
-        }
-}
-
-std::vector<std::string> MachOProxy::reexports()
-{
-    switch (archForString(arch).arch) {
-        case CPU_TYPE_ARM:
-        case CPU_TYPE_I386:
-            return reexports<Pointer32<LittleEndian>>();
-        case CPU_TYPE_X86_64:
-        case CPU_TYPE_ARM64:
-            return reexports<Pointer64<LittleEndian>>();
-            break;
-        default:
-            return std::vector<std::string>();
-    }
-}
-
-template <typename P>
-std::string MachOProxy::machoParser(bool ignoreUncacheableDylibsInExecutables)
-{
-    const uint8_t*                     buffer = getBuffer();
-    bool                               hasSplitSegInfo = false;
-    bool                               hasDylidInfo = false;
-    const macho_header<P>*             mh = (const macho_header<P>*)buffer;
-    const macho_symtab_command<P>*     symTab = nullptr;
-    const macho_dysymtab_command<P>*   dynSymTab = nullptr;
-    const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)mh + sizeof(macho_header<P>));
-    const macho_dyld_info_command<P>*  dyldInfo = nullptr;
-    const uint32_t                     cmd_count = mh->ncmds();
-    const macho_load_command<P>*       cmd = cmds;
-    uint64_t                           baseAddr = 0;
-    _filetype = mh->filetype();
-    if (_filetype == MH_DYLIB_STUB) {
-        return "stub dylib";
-    }
-    if (_filetype == MH_DSYM) {
-        return "DSYM";
-    }
-    for (uint32_t i = 0; i < cmd_count; ++i) {
-        switch (cmd->cmd()) {
-        case LC_ID_DYLIB: {
-            macho_dylib_command<P>* dylib = (macho_dylib_command<P>*)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();
-            addAlias(path);
-        } break;
-        case LC_UUID: {
-            const macho_uuid_command<P>* uuidCmd = (macho_uuid_command<P>*)cmd;
-            uuid = UUID(uuidCmd->uuid());
-        } break;
-        case LC_LOAD_DYLIB:
-        case LC_LOAD_WEAK_DYLIB:
-        case LC_REEXPORT_DYLIB:
-        case LC_LOAD_UPWARD_DYLIB: {
-            macho_dylib_command<P>* dylib = (macho_dylib_command<P>*)cmd;
-            std::string             depName = dylib->name();
-            if ( isExecutable() && ignoreUncacheableDylibsInExecutables && !has_prefix(depName, "/usr/lib/") && !has_prefix(depName, "/System/Library/") ) {
-                // <rdar://problem/25918268> 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)";
-            }
-        } break;
-        case macho_segment_command<P>::CMD: {
-            const macho_segment_command<P>* segCmd = (macho_segment_command<P>*)cmd;
-            MachOProxySegment               seg;
-            seg.name = segCmd->segname();
-            seg.size = align(segCmd->vmsize(), 12);
-            seg.vmaddr = segCmd->vmaddr();
-            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<P>* const sectionsStart = (macho_section<P>*)((uint8_t*)segCmd + sizeof(macho_segment_command<P>));
-                const macho_section<P>* const sectionsLast = &sectionsStart[segCmd->nsects() - 1];
-                const macho_section<P>* const sectionsEnd = &sectionsStart[segCmd->nsects()];
-                for (const macho_section<P>* 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);
-            if (seg.name == "__TEXT") {
-                baseAddr =  seg.vmaddr;
-            }
-        } break;
-        case LC_SEGMENT_SPLIT_INFO:
-            hasSplitSegInfo = true;
-            break;
-        case LC_SYMTAB:
-            symTab = (macho_symtab_command<P>*)cmd;
-            break;
-        case LC_DYSYMTAB:
-            dynSymTab = (macho_dysymtab_command<P>*)cmd;
-            break;
-        case LC_DYLD_INFO:
-        case LC_DYLD_INFO_ONLY:
-            dyldInfo = (macho_dyld_info_command<P>*)cmd;
-            hasDylidInfo = true;
-            break;
-        }
-        cmd = (const macho_load_command<P>*)(((uint8_t*)cmd) + cmd->cmdsize());
-    }
-
-    identifier = uuid;
-
-    if (!hasDylidInfo) {
-        return "built for old OS";
-    }
-
-    if (dyldInfo && dyldInfo->bind_size() != 0) {
-        _bind_offset = dyldInfo->bind_off();
-        _bind_size = dyldInfo->bind_size();
-    }
-
-    if (dyldInfo && dyldInfo->lazy_bind_size() != 0) {
-        _lazy_bind_offset = dyldInfo->lazy_bind_off();
-        _lazy_bind_size = dyldInfo->lazy_bind_size();
-    }
-
-    // if no export info, no _exports map to build
-    if (dyldInfo && dyldInfo->export_size() != 0) {
-        std::vector<ExportInfoTrie::Entry> exports;
-        const uint8_t*                     exportsStart = &buffer[dyldInfo->export_off()];
-        const uint8_t*                     exportsEnd = &exportsStart[dyldInfo->export_size()];
-        if (!ExportInfoTrie::parseTrie(exportsStart, exportsEnd, exports)) {
-            terminate("malformed exports trie in %s", path.c_str());
-        }
-
-        for (const ExportInfoTrie::Entry& entry : exports) {
-            if (!_exports[entry.name].isAbsolute) {
-                for (const auto& seg : segments) {
-                    if (seg.size > 0 && (seg.vmaddr - baseAddr) <= entry.info.address && entry.info.address < (seg.vmaddr - baseAddr) + seg.size) {
-                        _exports[entry.name].segmentOffset = entry.info.address - (seg.vmaddr - baseAddr);
-                        _exports[entry.name].segmentName = seg.name;
-                        break;
-                    }
-                }
-            } else {
-                _exports[entry.name].segmentOffset = (uint64_t)entry.info.address;
-                _exports[entry.name].segmentName = "";
-            }
-
-            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;
-                    break;
-                default:
-                    terminate("non-regular symbol binding not supported for %s in %s", entry.name.c_str(), path.c_str());
-                    break;
-            }
-        }
-    }
-
-    if (!isDylib()) {
-        return "";
-    }
-
-    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<P>* symbolTable = (macho_nlist<P>*)((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 (_filetype == MH_DYLIB);
-}
-
-const bool MachOProxy::isExecutable()
-{
-    return (_filetype == MH_EXECUTE);
-}
-
-static std::map<ImageIdentifier, MachOProxy*> identifierMap;
-std::map<std::pair<std::string, std::string>, MachOProxy*> archMap;
-static dispatch_queue_t identifierQueue;
-
-MachOProxy* MachOProxy::forIdentifier(const ImageIdentifier& identifier, const std::string preferredArch)
-{
-    auto i = identifierMap.find(identifier);
-    // We need an identifier
-    if (i == identifierMap.end())
-        return nullptr;
-
-    // Is the identifier the arch we want?
-    if (i->second->arch == preferredArch)
-        return i->second;
-
-    // Fallback to a slow path to try to find a best fit
-    return forInstallnameAndArch(i->second->installName, preferredArch);
-}
-
-MachOProxy* MachOProxy::forInstallnameAndArch(const std::string& installname, const std::string& arch)
-{
-    auto i = archMap.find(std::make_pair(installname, arch));
-    if (i == archMap.end())
-        i = archMap.find(std::make_pair(installname, fallbackArchStringForArchString(arch)));
-    if (i != archMap.end())
-        return i->second;
-    return nullptr;
-}
-
-void MachOProxy::mapDependencies()
-{
-    // Build a complete map of all installname/alias,archs to their proxies
-    runOnAllProxies(false, [&](MachOProxy* proxy) {
-        archMap[std::make_pair(proxy->path, proxy->arch)] = proxy;
-        for (auto& alias : proxy->installNameAliases) {
-            archMap[std::make_pair(alias, proxy->arch)] = proxy;
-        }
-    });
-
-    //Wire up the dependencies
-    runOnAllProxies(false, [&](MachOProxy* proxy) {
-        auto dependencyInstallnames = proxy->dependencies();
-        for (auto dependencyInstallname : dependencyInstallnames) {
-            auto dependencyProxy = forInstallnameAndArch(dependencyInstallname, proxy->arch);
-            if (dependencyProxy == nullptr) {
-                proxy->error = "Missing dependency: " + dependencyInstallname;
-            } else {
-                proxy->requiredIdentifiers.push_back(dependencyProxy->identifier);
-                dependencyProxy->dependentIdentifiers.push_back(proxy->identifier);
-            }
-        }
-
-        auto reexportInstallnames = proxy->reexports();
-        for (auto reexportInstallname : reexportInstallnames) {
-            auto reexportProxy = forInstallnameAndArch(reexportInstallname, proxy->arch);
-            if (reexportProxy == nullptr) {
-                proxy->error = "Missing reexport dylib: " + reexportInstallname;
-            } else {
-                proxy->_reexportProxies.push_back(reexportProxy);
-            }
-        }
-
-    });
-}
-
-void MachOProxy::runOnAllProxies(bool concurrently, std::function<void(MachOProxy* proxy)> lambda)
-{
-    dispatch_group_t runGroup = dispatch_group_create();
-    dispatch_queue_t runQueue = dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, NULL);
-
-    for (auto& identifier : identifierMap) {
-        if (concurrently) {
-            cacheBuilderDispatchGroupAsync(runGroup, runQueue, [&] {
-                lambda(identifier.second);
-            });
-        } else {
-            lambda(identifier.second);
-        }
-    }
-
-    dispatch_group_wait(runGroup, DISPATCH_TIME_FOREVER);
-}
-
-std::map<std::string, MachOProxy*> MachOProxy::loadProxies(const std::string& buildPath, const std::string& path, bool warnOnProblems, bool ignoreUncacheableDylibsInExecutables)
-{
-    std::vector<MachOProxy*> slices = mapMachOFile(buildPath, path);
-    std::map<std::string, MachOProxy*> retval;
-
-    for ( auto& slice : slices ) {
-        std::string errorMessage;
-        verboseLog( "analyzing file '%s'", path.c_str() );
-        switch (archForString(slice->arch).arch) {
-            case CPU_TYPE_ARM:
-            case CPU_TYPE_I386:
-                errorMessage = slice->machoParser<Pointer32<LittleEndian>>(ignoreUncacheableDylibsInExecutables);
-                break;
-            case CPU_TYPE_X86_64:
-            case CPU_TYPE_ARM64:
-                errorMessage = slice->machoParser<Pointer64<LittleEndian>>(ignoreUncacheableDylibsInExecutables);
-                break;
-            default:
-                errorMessage = "unsupported arch '" + slice->arch + "'";
-                break;
-        }
-
-        if (errorMessage.empty()) {
-            static dispatch_once_t onceToken;
-            dispatch_once(&onceToken, ^{
-                identifierQueue = dispatch_queue_create("com.apple.dyld.cache.metabom.ids", DISPATCH_QUEUE_SERIAL);
-            });
-            retval[slice->arch] = slice;
-            dispatch_sync(identifierQueue, ^{
-                identifierMap[slice->identifier] = slice;
-            });
-        } else {
-            if (warnOnProblems)
-                warning("%s (%s)", errorMessage.c_str(), path.c_str());
-        }
-    }
-
-    return retval;
-}
-
-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(buildPath);
-    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/interlinked-dylibs/MachOProxy.h b/interlinked-dylibs/MachOProxy.h
deleted file mode 100644 (file)
index 4781504..0000000
+++ /dev/null
@@ -1,160 +0,0 @@
-//
-//  DylibProxy.h
-//  dyld
-//
-//  Created by Louis Gerbarg on 1/27/16.
-//
-//
-
-#ifndef MachOProxy_h
-#define MachOProxy_h
-
-#include <map>
-#include <set>
-#include <string>
-#include <vector>
-#include <cstdint>
-
-#include <sys/stat.h>
-
-#include "mega-dylib-utils.h"
-
-struct MachOProxy;
-
-struct MachOProxySegment {
-    std::string name;
-    uint64_t    size;
-    uint64_t    sizeOfSections;
-    uint64_t    vmaddr;
-    uint32_t    diskSize;
-    uint32_t    fileOffset;
-    uint8_t     p2align;
-    uint8_t     protection;
-};
-
-struct MachOProxy {
-    MachOProxy(const std::string& bp, const std::string& p, const std::string& a, ino_t i, time_t t, uint32_t o, uint32_t s, bool r)
-        : buildPath(bp)
-        , path(p)
-        , arch(a)
-        , fatFileOffset(o)
-        , fileSize(s)
-        , lastModTime(t)
-        , inode(i)
-        , installNameOffsetInTEXT(0)
-        , rootlessProtected(r)
-        , _bind_offset(0)
-        , _bind_size(0)
-        , queue(dispatch_queue_create("com.apple.dyld.proxy", NULL))
-    {
-    }
-
-    struct SymbolInfo {
-        SymbolInfo() {}
-        std::string segmentName;
-        uint64_t    segmentOffset = 0;
-        bool        isResolver = false;
-        bool        isAbsolute = false;
-        bool        isSymbolReExport = false;
-        bool        isThreadLocal = false;
-        int         reExportDylibIndex = 0;
-        std::string reExportName;
-    };
-
-    const std::string           buildPath;
-    const std::string           path;
-    const std::string           arch;
-    const uint32_t              fatFileOffset;
-    const uint32_t              fileSize;
-    const time_t                lastModTime;
-    const ino_t                 inode;
-    const bool                  rootlessProtected;
-    dispatch_queue_t            queue;
-    std::string                 installName;
-    std::set<std::string>       installNameAliases;
-    uint32_t                    installNameOffsetInTEXT;
-    std::string                 error;
-    std::vector<ImageIdentifier>   requiredIdentifiers;
-    std::vector<ImageIdentifier>   dependentIdentifiers;
-    UUID                        uuid;
-    ImageIdentifier             identifier;
-    std::vector<MachOProxySegment> segments;
-
-    const uint8_t* getBuffer();
-    const uint8_t* getBindStart() { return &(getBuffer())[_bind_offset]; }
-    const uint8_t* getBindEnd() { return &(getBuffer())[_bind_offset + _bind_size]; }
-    const uint8_t* getLazyBindStart() { return &(getBuffer())[_lazy_bind_offset]; }
-    const uint8_t* getLazyBindEnd() { return &(getBuffer())[_lazy_bind_offset + _lazy_bind_size]; }
-
-    const bool     isDylib();
-    const bool     isExecutable();
-    bool addAlias(const std::string& alias);
-    static void mapDependencies();
-
-    const uint64_t addressOf(const std::string& symbol, const std::map<const MachOProxy*, std::vector<SharedCache::SegmentInfo>>& segmentMap)
-    {
-        auto info = symbolInfo(symbol);
-        assert(info != nullptr);
-        if (info->isAbsolute)
-            return info->segmentOffset;
-        auto proxyI = segmentMap.find(this);
-        assert(proxyI != segmentMap.end());
-
-        for (const auto& seg : proxyI->second) {
-            if (seg.base->name == info->segmentName) {
-                assert(!info->segmentName.empty());
-                return seg.address + info->segmentOffset;
-            }
-        }
-
-        return 0;
-    }
-
-    SymbolInfo* symbolInfo(const std::string& symbol)
-    {
-        auto i = _exports.find(symbol);
-        if (i != _exports.end())
-            return &i->second;
-        return nullptr;
-    }
-
-    bool providesSymbol(const std::string& symbol)
-    {
-        if (_exports.find(symbol) != _exports.end())
-            return true;
-
-        for (const auto& proxy : _reexportProxies) {
-            if (proxy->providesSymbol(symbol))
-                return true;
-        }
-        return false;
-    }
-    static std::map<std::string, MachOProxy*> loadProxies(const std::string& prefix, const std::string& path, bool warnOnProblems = false, bool ignoreUncacheableDylibsInExecutables = false);
-    static void runOnAllProxies(bool concurrently, std::function<void(MachOProxy* proxy)> lambda);
-    static MachOProxy* forIdentifier(const ImageIdentifier& identifier, const std::string preferredArch);
-    static MachOProxy* forInstallnameAndArch(const std::string& installname, const std::string& arch);
-
-    std::vector<std::string> dependencies();
-    std::vector<std::string> reexports();
-
-private:
-    uint32_t _filetype;
-    std::map<std::string, SymbolInfo> _exports;
-    uint32_t                 _bind_offset;
-    uint32_t                 _bind_size;
-    uint32_t                 _lazy_bind_offset;
-    uint32_t                 _lazy_bind_size;
-    std::vector<MachOProxy*> _reexportProxies;
-
-    template <typename P>
-    std::string machoParser(bool ignoreUncacheableDylibsInExecutables);
-
-    template <typename P>
-    std::vector<std::string> dependencies();
-
-    template <typename P>
-    std::vector<std::string> reexports();
-};
-
-
-#endif /* MachOProxy_h */
diff --git a/interlinked-dylibs/Manifest.h b/interlinked-dylibs/Manifest.h
deleted file mode 100644 (file)
index 6611ef0..0000000
+++ /dev/null
@@ -1,253 +0,0 @@
-//
-//  Manifest.h
-//  dyld
-//
-//  Created by Louis Gerbarg on 7/23/15.
-//
-//
-
-#ifndef Manifest_h
-#define Manifest_h
-
-#include <map>
-#include <set>
-#include <string>
-#include <vector>
-
-#include <unordered_map>
-#include <unordered_set>
-
-#include <assert.h>
-
-struct MachOProxy;
-
-extern void terminate(const char* format, ...) __printflike(1, 2) __attribute__((noreturn));
-extern std::string toolDir();
-
-struct SharedCache;
-struct Manifest;
-
-struct Manifest {
-    struct Project {
-        std::vector<std::string> sources;
-    };
-
-    struct File {
-        MachOProxy* proxy;
-
-        File( MachOProxy* P ) : proxy( P ) {}
-    };
-
-    struct Anchor {
-        ImageIdentifier identifier;
-               bool required;
-        Anchor( const ImageIdentifier& I ) : identifier( I ) {}
-    };
-
-    struct SegmentInfo {
-        std::string name;
-        uint64_t    startAddr;
-        uint64_t    endAddr;
-       };
-
-       struct SegmentInfoHasher {
-               std::size_t operator()(const SegmentInfo &x) const {
-                       return std::hash<std::string>()(x.name) ^ std::hash<uint64_t>()(x.startAddr) ^ std::hash<uint64_t>()(x.endAddr);
-               }
-       };
-
-       struct CacheInfo {
-               std::vector<SegmentInfo> regions;
-               std::string cdHash;
-       };
-
-       struct DylibInfo {
-               bool included;
-               std::string exclusionInfo;
-               UUID uuid;
-        std::string installname;
-               std::vector<SegmentInfo> segments;
-               DylibInfo(void) : included(true) {}
-       };
-
-       struct Results {
-        std::string                             failure;
-        std::map<ImageIdentifier, DylibInfo>    dylibs;
-        std::vector<std::string>                warnings;
-        CacheInfo                               developmentCache;
-        CacheInfo                               productionCache;
-        DylibInfo& dylibForInstallname(const std::string& installname)
-        {
-            auto i = find_if(dylibs.begin(), dylibs.end(), [&installname](std::pair<ImageIdentifier, DylibInfo> d) { return d.second.installname == installname; });
-            assert(i != dylibs.end());
-            return i->second;
-        }
-        void exclude(MachOProxy* proxy, const std::string& reason);
-    };
-
-       struct Architecture {
-               std::vector<Anchor> anchors;
-               mutable Results results;
-
-        bool operator==(const Architecture& O) const
-        {
-            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
-                        || Odylib->second.uuid != dylib.second.uuid)
-                        return false;
-                }
-            }
-
-            for (const 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
-                        || dylib->second.uuid != Odylib.second.uuid)
-                        return false;
-                }
-            }
-
-            return true;
-        }
-
-        bool operator!=(const Architecture& other) const { return !(*this == other); }
-    };
-
-       struct Configuration {
-        std::string platformName;
-        std::string metabomTag;
-        std::set<std::string> metabomExcludeTags;
-        std::set<std::string> metabomRestrictTags;
-        std::set<std::string> restrictedInstallnames;
-        std::map<std::string, Architecture> architectures;
-
-        bool operator==(const Configuration& O) const
-        {
-            return architectures == O.architectures;
-        }
-
-        bool operator!=(const Configuration& other) const { return !(*this == other); }
-
-        const Architecture& architecture(const std::string& architecture) const
-        {
-            assert(architectures.find(architecture) != architectures.end());
-            return architectures.find(architecture)->second;
-        }
-
-        void forEachArchitecture(std::function<void(const std::string& archName)> lambda)
-        {
-            for (const auto& architecutre : architectures) {
-                lambda(architecutre.first);
-            }
-        }
-       };
-
-    const std::map<std::string, Project>& projects()
-    {
-        return _projects;
-    }
-
-    const Configuration& configuration(const std::string& configuration) const
-    {
-        assert(_configurations.find(configuration) != _configurations.end());
-        return _configurations.find(configuration)->second;
-    }
-
-    void forEachConfiguration(std::function<void(const std::string& configName)> lambda)
-    {
-        for (const auto& configuration : _configurations) {
-            lambda(configuration.first);
-        }
-    }
-
-    void addProjectSource(const std::string& project, const std::string& source, bool first = false)
-    {
-        auto& sources = _projects[project].sources;
-        if (std::find(sources.begin(), sources.end(), source) == sources.end()) {
-            if (first) {
-                sources.insert(sources.begin(), source);
-            } else {
-                sources.push_back(source);
-            }
-        }
-    }
-
-    const std::string projectPath(const std::string& projectName)
-    {
-        auto project = _projects.find(projectName);
-        if (project == _projects.end())
-            return "";
-        if (project->second.sources.size() == 0)
-            return "";
-        return project->second.sources[0];
-    }
-
-    
-    const bool empty(void) {
-        for (const auto& configuration : _configurations) {
-            if (configuration.second.architectures.size() != 0)
-                return false;
-        }
-        return true;
-    }
-    
-    const std::string dylibOrderFile() const { return _dylibOrderFile; };
-    void setDylibOrderFile(const std::string& dylibOrderFile) { _dylibOrderFile = dylibOrderFile; };
-
-    const std::string dirtyDataOrderFile() const { return  _dirtyDataOrderFile; };
-    void setDirtyDataOrderFile(const std::string& dirtyDataOrderFile) { _dirtyDataOrderFile = dirtyDataOrderFile; };
-
-    const std::string metabomFile() const { return _metabomFile; };
-    void setMetabomFile(const std::string& metabomFile) { _metabomFile = metabomFile; };
-
-    const std::string platform() const { return _platform; };
-    void setPlatform(const std::string& platform) { _platform = platform; };
-
-    const std::string& build() const { return _build; };
-    void setBuild(const std::string& build) { _build = build; };
-    const uint32_t                   version() const { return _manifestVersion; };
-    void setVersion(const uint32_t manifestVersion) { _manifestVersion = manifestVersion; };
-    bool                           normalized;
-
-    Manifest(void) {}
-    Manifest(const std::set<std::string>& archs, const std::string& overlayPath, const std::string& rootPath, const std::set<std::string>& paths);
-#if BOM_SUPPORT
-    Manifest(const std::string& path);
-    Manifest(const std::string& path, const std::set<std::string>& overlays);
-#endif
-    void write(const std::string& path);
-    void canonicalize(void);
-    void calculateClosure(bool enforeceRootless);
-    bool sameContentsAsCacheAtPath(const std::string& configuration, const std::string& architecture,
-        const std::string& path) const;
-    void remove(const std::string& config, const std::string& arch);
-    MachOProxy* removeLargestLeafDylib(const std::string& configuration, const std::string& architecture);
-    bool checkLinks();
-    void runConcurrently(dispatch_queue_t queue, dispatch_semaphore_t concurrencyLimitingSemaphore, std::function<void(const std::string configuration, const std::string architecture)> lambda);
-    bool filterForConfig(const std::string& configName);
-
-private:
-    uint32_t    _manifestVersion;
-    std::string _build;
-    std::string _dylibOrderFile;
-    std::string _dirtyDataOrderFile;
-    std::string _metabomFile;
-    std::string _platform;
-    std::map<std::string, Project>       _projects;
-    std::map<std::string, Configuration> _configurations;
-    void removeDylib(MachOProxy* proxy, const std::string& reason, const std::string& configuration, const std::string& architecture,
-        std::unordered_set<ImageIdentifier>& processedIdentifiers);
-    void calculateClosure(const std::string& configuration, const std::string& architecture);
-    void canonicalizeDylib(const std::string& installname);
-    template <typename P>
-    void canonicalizeDylib(const std::string& installname, const uint8_t* p);
-    void        addImplicitAliases(void);
-    MachOProxy* dylibProxy(const std::string& installname, const std::string& arch);
-};
-
-
-#endif /* Manifest_h */
diff --git a/interlinked-dylibs/Manifest.mm b/interlinked-dylibs/Manifest.mm
deleted file mode 100644 (file)
index dad8f43..0000000
+++ /dev/null
@@ -1,768 +0,0 @@
-//
-//  Manifest.mm
-//  dyld
-//
-//  Created by Louis Gerbarg on 7/23/15.
-//
-//
-
-#if BOM_SUPPORT
-extern "C" {
-#include <Bom/Bom.h>
-#include <Metabom/MBTypes.h>
-#include <Metabom/MBEntry.h>
-#include <Metabom/MBMetabom.h>
-#include <Metabom/MBIterator.h>
-};
-#endif /* BOM_SUPPORT */
-
-#include <algorithm>
-
-#include <Foundation/Foundation.h>
-#include <rootless.h>
-
-#include "MachOFileAbstraction.hpp"
-#include "FileAbstraction.hpp"
-#include "Trie.hpp"
-#include "Logging.h"
-
-#include <mach-o/loader.h>
-#include <mach-o/fat.h>
-
-#include <array>
-#include <vector>
-
-#include "dsc_iterator.h"
-#include "MachOProxy.h"
-#include "mega-dylib-utils.h"
-
-#include "Manifest.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()]; }
-
-std::string fileExists(const std::string& path)
-{
-    const uint8_t* p = (uint8_t*)(-1);
-    struct stat    stat_buf;
-
-    std::tie(p, stat_buf, rootless) = fileCache.cacheLoad(path);
-    if (p != (uint8_t*)(-1)) {
-        return normalize_absolute_file_path(path);
-    }
-
-    return "";
-}
-
-} /* Anonymous namespace */
-
-void Manifest::Results::exclude(MachOProxy* proxy, const std::string& reason)
-{
-    dylibs[proxy->identifier].uuid = proxy->uuid;
-    dylibs[proxy->identifier].installname = proxy->installName;
-    dylibs[proxy->identifier].included = false;
-    dylibs[proxy->identifier].exclusionInfo = reason;
-}
-
-Manifest::Manifest(const std::set<std::string>& archs, const std::string& overlayPath, const std::string& rootPath, const std::set<std::string>& paths)
-{
-    std::set<std::string> processedPaths;
-    std::set<std::string> unprocessedPaths = paths;
-    std::set<std::string> 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::loadProxies(fullPath, path);
-
-            for (const auto& arch : archs) {
-                auto proxyI = proxies.find(arch);
-                if (proxyI == proxies.end())
-                    proxyI = proxies.find(fallbackArchStringForArchString(arch));
-                if (proxyI == proxies.end())
-                    continue;
-
-                auto dependecies = proxyI->second->dependencies();
-                for (const auto& dependency : dependecies) {
-                    unprocessedPaths.insert(dependency);
-                }
-                _configurations["localhost"].architectures[arch].anchors.push_back(proxyI->second->identifier);
-            }
-
-            //Stuff
-        }
-
-        pathsToProcess.clear();
-        std::set_difference(unprocessedPaths.begin(), unprocessedPaths.end(), processedPaths.begin(), processedPaths.end(),
-            std::inserter(pathsToProcess, pathsToProcess.begin()));
-    }
-    MachOProxy::mapDependencies();
-}
-
-#if BOM_SUPPORT
-
-Manifest::Manifest(const std::string& path)
-    : Manifest(path, std::set<std::string>())
-{
-}
-
-Manifest::Manifest(const std::string& path, const std::set<std::string>& overlays)
-{
-    NSMutableDictionary* manifestDict = [NSMutableDictionary dictionaryWithContentsOfFile:cppToObjStr(path)];
-    std::map<std::string, std::string>           metabomTagMap;
-    std::map<std::string, std::set<std::string>> metabomExcludeTagMap;
-    std::map<std::string, std::set<std::string>> metabomRestrictedTagMap;
-    std::vector<std::pair<std::string, MachOProxy*>> configProxies;
-
-    setMetabomFile([manifestDict[@"metabomFile"] UTF8String]);
-
-    for (NSString* project in manifestDict[@"projects"]) {
-        for (NSString* source in manifestDict[@"projects"][project]) {
-            addProjectSource([project UTF8String], [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];
-    }
-
-    setVersion([manifestDict[@"manifest-version"] unsignedIntValue]);
-    setBuild([manifestDict[@"build"] UTF8String]);
-    if (manifestDict[@"dylibOrderFile"]) {
-        setDylibOrderFile([manifestDict[@"dylibOrderFile"] UTF8String]);
-    }
-    if (manifestDict[@"dirtyDataOrderFile"]) {
-        setDirtyDataOrderFile([manifestDict[@"dirtyDataOrderFile"] UTF8String]);
-    }
-
-    auto    metabom = MBMetabomOpen(metabomFile().c_str(), false);
-    auto    metabomEnumerator = MBIteratorNewWithPath(metabom, ".", "");
-    MBEntry entry;
-
-    auto bomSemaphore = dispatch_semaphore_create(32);
-    auto bomGroup = dispatch_group_create();
-    auto bomQueue = dispatch_queue_create("com.apple.dyld.cache.metabom.bom", dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, QOS_CLASS_USER_INITIATED, 0));
-    auto archQueue = dispatch_queue_create("com.apple.dyld.cache.metabom.arch", dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, QOS_CLASS_USER_INITIATED, 0));
-    auto manifestQueue = dispatch_queue_create("com.apple.dyld.cache.metabom.arch", dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_CONCURRENT, QOS_CLASS_USER_INITIATED, 0));
-
-    // FIXME error handling (NULL metabom)
-
-    //First we iterate through the bom and build our objects
-
-    while ((entry = MBIteratorNext(metabomEnumerator))) {
-        dispatch_semaphore_wait(bomSemaphore, DISPATCH_TIME_FOREVER);
-        cacheBuilderDispatchGroupAsync(bomGroup, manifestQueue, [this, &bomSemaphore, &archQueue, &bomGroup, &bomQueue, &metabom, entry, &overlays, &metabomTagMap, &metabomRestrictedTagMap, &metabomExcludeTagMap, &manifestDict, &configProxies] {
-            BOMFSObject  fsObject = nullptr;
-            std::string  entryPath;
-            BOMFSObjType entryType;
-            cacheBuilderDispatchSync(bomQueue, [&entry, &fsObject, &entryPath, &entryType] {
-                fsObject = MBEntryGetFSObject(entry);
-                entryPath = BOMFSObjectPathName(fsObject);
-                if (entryPath[0] == '.') {
-                    entryPath.erase(0, 1);
-                }
-                entryType = BOMFSObjectType(fsObject);
-            });
-
-            MBTag tag;
-            auto  tagCount = MBEntryGetNumberOfProjectTags(entry);
-            if ( entryType == BOMFileType && BOMFSObjectIsBinaryObject(fsObject) && MBEntryGetNumberOfProjectTags(entry) != 0 && tagCount != 0 ) {
-                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<std::string> tagStrs;
-                    std::map<std::string, MBTag> tagStrMap;
-                    for (auto i = 0; i < tagCount; ++i) {
-                        cacheBuilderDispatchSync(bomQueue, [i, &metabom, &tagStrs, &tagStrMap, &tags] {
-                            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;
-                cacheBuilderDispatchSync(bomQueue, [&projectName, &metabom, &tag] {
-                    projectName = MBMetabomGetProjectForTag(metabom, tag);
-                });
-
-                std::map<std::string, MachOProxy*> proxies;
-                for (const auto& overlay : overlays) {
-                    proxies = MachOProxy::loadProxies(overlay + "/" + entryPath, entryPath);
-                    if (proxies.size() > 0)
-                        break;
-                }
-
-                if (proxies.size() == 0) {
-                    proxies = MachOProxy::loadProxies(projectPath(projectName) + "/" + entryPath, entryPath);
-                }
-
-                tagCount = MBEntryGetNumberOfPackageTags(entry);
-                MBTag* tags = (MBTag*)malloc(sizeof(MBTag) * tagCount);
-                MBEntryGetPackageTags(entry, tags);
-                std::set<std::string> tagStrs;
-
-                cacheBuilderDispatchSync(bomQueue, [&] {
-                    for (auto i = 0; i < tagCount; ++i) {
-                        tagStrs.insert(MBMetabomGetPackageForTag(metabom, tags[i]));
-                    }
-                });
-
-                for (auto& proxy : proxies) {
-                    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;
-                        }
-                        cacheBuilderDispatchGroupAsync(bomGroup, archQueue, [this, &manifestDict, &configProxies, configuration, proxy, tagStr] {
-                            if ([manifestDict[@"configurations"][cppToObjStr(configuration->second)][@"architectures"]
-                                    containsObject:cppToObjStr(proxy.second->arch)]) {
-                                _configurations[configuration->second].architectures[proxy.second->arch].anchors.push_back(proxy.second->identifier);
-                            }
-                        });
-                    }
-                }
-            }
-            dispatch_semaphore_signal(bomSemaphore);
-        });
-    }
-
-    dispatch_group_wait(bomGroup, DISPATCH_TIME_FOREVER);
-    MBIteratorFree(metabomEnumerator);
-    MBMetabomFree(metabom);
-    MachOProxy::mapDependencies();
-}
-
-#endif
-
-template <typename P>
-bool checkLink(MachOProxy* proxy, const uint8_t* p, const uint8_t* end)
-{
-    bool                     retval = true;
-    std::vector<std::string> dylibs = proxy->dependencies();
-
-    std::string symbolName;
-    int         libraryOrdinal = 0;
-    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:
-                break;
-            case BIND_OPCODE_SET_ADDEND_SLEB:
-                (void)read_sleb128(p, end);
-                break;
-            case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB:
-            case BIND_OPCODE_ADD_ADDR_ULEB:
-                (void)read_uleb128(p, end);
-                break;
-            case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB:
-                (void)read_uleb128(p, end);
-            case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB:
-                (void)read_uleb128(p, end);
-            case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED:
-            case BIND_OPCODE_DO_BIND: {
-                if (libraryOrdinal <= 0)
-                    break;
-                if ( libraryOrdinal > dylibs.size() ) {
-                    warning("Illegal library ordinal (%d) in dylib %s bind opcode (max ordinal %lu)", libraryOrdinal, proxy->path.c_str(), dylibs.size());
-                    retval = false;
-                }
-                else {
-                    auto dependencyProxy = MachOProxy::forInstallnameAndArch(dylibs[libraryOrdinal - 1], proxy->arch);
-                    if (!weakImport && (!dependencyProxy || !dependencyProxy->providesSymbol(symbolName))) {
-                        warning("Could not find symbol %s in dylib %s for %s", symbolName.c_str(), dylibs[libraryOrdinal - 1].c_str(), proxy->path.c_str());
-                        retval = false;
-                    }
-                }
-            } break;
-            default:
-                warning("bad bind opcode in binary 0x%02X in %s", *p, proxy->path.c_str());
-        }
-    }
-
-    return retval;
-}
-
-bool checkLink(MachOProxy* proxy)
-{
-    switch (archForString(proxy->arch).arch) {
-        case CPU_TYPE_ARM:
-        case CPU_TYPE_I386:
-            return (checkLink<Pointer32<LittleEndian>>(proxy, proxy->getBindStart(), proxy->getBindEnd())
-                    && checkLink<Pointer32<LittleEndian>>(proxy, proxy->getLazyBindStart(), proxy->getLazyBindEnd()));
-        case CPU_TYPE_ARM64:
-        case CPU_TYPE_X86_64:
-            return (checkLink<Pointer64<LittleEndian>>(proxy, proxy->getBindStart(), proxy->getBindEnd())
-                    && checkLink<Pointer64<LittleEndian>>(proxy, proxy->getLazyBindStart(), proxy->getLazyBindEnd()));
-        default:
-            terminate("unsupported arch 0x%08X", archForString(proxy->arch).arch);
-    }
-}
-
-bool Manifest::checkLinks()
-{
-    dispatch_queue_t     linkCheckQueue = dispatch_get_global_queue(QOS_CLASS_DEFAULT, NULL);
-    dispatch_semaphore_t linkCheckSemphore = dispatch_semaphore_create(32);
-
-    dispatch_group_t linkCheckGroup = dispatch_group_create();
-
-    runConcurrently(linkCheckQueue, linkCheckSemphore, [this](const std::string configuration, const std::string architecture) {
-        for (const auto& anchor : this->configuration(configuration).architecture(architecture).anchors) {
-            const auto identifier = anchor.identifier;
-            const auto proxy = MachOProxy::forIdentifier(identifier, architecture);
-            if (proxy->isExecutable()) {
-                checkLink(proxy);
-            }
-        }
-    });
-
-    dispatch_group_wait(linkCheckGroup, DISPATCH_TIME_FOREVER);
-
-    return true;
-}
-
-void Manifest::runConcurrently(dispatch_queue_t queue, dispatch_semaphore_t concurrencyLimitingSemaphore, std::function<void(const std::string configuration, const std::string architecture)> lambda)
-{
-    dispatch_group_t runGroup = dispatch_group_create();
-    for (auto& config : _configurations) {
-        for (auto& architecture : config.second.architectures) {
-            dispatch_semaphore_wait(concurrencyLimitingSemaphore, DISPATCH_TIME_FOREVER);
-            cacheBuilderDispatchGroupAsync(runGroup, queue, [&] {
-                WarningTargets targets;
-                targets.first = this;
-                targets.second.insert(std::make_pair(config.first, architecture.first));
-                auto ctx = std::make_shared<LoggingContext>(config.first + "/" + architecture.first, targets);
-                setLoggingContext(ctx);
-                lambda(config.first, architecture.first);
-                dispatch_semaphore_signal(concurrencyLimitingSemaphore);
-            });
-        }
-    }
-
-    dispatch_group_wait(runGroup, DISPATCH_TIME_FOREVER);
-}
-
-bool Manifest::filterForConfig(const std::string& configName)
-{
-    for (const auto configuration : _configurations) {
-        if (configName == configuration.first) {
-            std::map<std::string, Configuration> filteredConfigs;
-            filteredConfigs[configName] = configuration.second;
-
-            _configurations = filteredConfigs;
-
-            for (auto& arch : configuration.second.architectures) {
-                arch.second.results = Manifest::Results();
-            }
-            return true;
-        }
-    }
-    return false;
-}
-
-void Manifest::calculateClosure(bool enforceRootless)
-{
-    auto closureSemaphore = dispatch_semaphore_create(32);
-    auto closureGroup = dispatch_group_create();
-    auto closureQueue = dispatch_queue_create("com.apple.dyld.cache.closure", dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_CONCURRENT, QOS_CLASS_USER_INITIATED, 0));
-    rootless = enforceRootless;
-
-    for (auto& config : _configurations) {
-        for (auto& arch : config.second.architectures) {
-            dispatch_semaphore_wait(closureSemaphore, DISPATCH_TIME_FOREVER);
-            cacheBuilderDispatchGroupAsync(closureGroup, closureQueue, [&] {
-                calculateClosure(config.first, arch.first);
-                dispatch_semaphore_signal(closureSemaphore);
-            });
-        }
-    }
-
-    dispatch_group_wait(closureGroup, DISPATCH_TIME_FOREVER);
-}
-
-void Manifest::remove(const std::string& config, const std::string& arch)
-{
-    if (_configurations.count(config))
-        _configurations[config].architectures.erase(arch);
-}
-
-bool
-Manifest::sameContentsAsCacheAtPath(const std::string& configuration, const std::string& architecture, const std::string& path) const {
-    std::set<std::pair<std::string, UUID>> cacheDylibs;
-    std::set<std::pair<std::string, UUID>> manifestDylibs;
-    struct stat statbuf;
-    if (::stat(path.c_str(), &statbuf) == -1) {
-        // <rdar://problem/25912438> 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;
-
-        Architecture existingArch;
-        (void)dyld_shared_cache_iterate(mappedCache, (uint32_t)statbuf.st_size,
-            [&existingArch, &architecture](const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo) {
-                UUID      uuid = *dylibInfo->uuid;
-                DylibInfo info;
-                info.uuid = uuid;
-                existingArch.results.dylibs[ImageIdentifier(uuid)] = info;
-            });
-
-        return (existingArch == _configurations.find(configuration)->second.architectures.find(architecture)->second);
-}
-
-void Manifest::removeDylib(MachOProxy* proxy, const std::string& reason, const std::string& configuration,
-    const std::string& architecture, std::unordered_set<ImageIdentifier>& processedIdentifiers)
-{
-    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->identifier) == 0) {
-        archManifest.results.dylibs[proxy->identifier].uuid = proxy->uuid;
-        archManifest.results.dylibs[proxy->identifier].installname = proxy->installName;
-        processedIdentifiers.insert(proxy->identifier);
-    }
-    archManifest.results.exclude(MachOProxy::forIdentifier(proxy->identifier, architecture), reason);
-
-    processedIdentifiers.insert(proxy->identifier);
-
-    for (const auto& dependent : proxy->dependentIdentifiers) {
-        auto dependentProxy = MachOProxy::forIdentifier(dependent, architecture);
-        auto dependentResultIter = archManifest.results.dylibs.find(dependentProxy->identifier);
-        if ( dependentProxy &&
-             ( dependentResultIter == archManifest.results.dylibs.end() || dependentResultIter->second.included == true ) ) {
-            removeDylib(dependentProxy, "Missing dependency: " + proxy->installName, configuration, architecture,
-                processedIdentifiers);
-        }
-    }
-}
-
-MachOProxy* Manifest::removeLargestLeafDylib( const std::string& configuration, const std::string& architecture ) {
-    std::set<ImageIdentifier> activeIdentifiers;
-    std::set<MachOProxy*> 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 ) {
-            activeIdentifiers.insert(dylibInfo.first);
-        }
-    }
-    for (const auto& identifier : activeIdentifiers) {
-        auto dylib = MachOProxy::forIdentifier(identifier, architecture);
-        bool dependents = false;
-        for (const auto& depedent : dylib->dependentIdentifiers) {
-            if (depedent != identifier && activeIdentifiers.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<ImageIdentifier> empty;
-    removeDylib( largestLeafDylib, "VM space overflow", configuration, architecture, empty );
-    return largestLeafDylib;
-}
-
-void Manifest::calculateClosure( const std::string& configuration, const std::string& architecture ) {
-    auto&                               archManifest = _configurations[configuration].architectures[architecture];
-    std::unordered_set<ImageIdentifier> newIdentifiers;
-
-    for ( auto& anchor : archManifest.anchors ) {
-        newIdentifiers.insert(anchor.identifier);
-    }
-
-    std::unordered_set<ImageIdentifier> processedIdentifiers;
-
-    while (!newIdentifiers.empty()) {
-        std::unordered_set<ImageIdentifier> identifiersToProcess = newIdentifiers;
-        newIdentifiers.clear();
-
-        for (const auto& identifier : identifiersToProcess) {
-            if (processedIdentifiers.count(identifier) > 0) {
-                continue;
-            }
-
-            auto proxy = MachOProxy::forIdentifier(identifier, architecture);
-
-            if (proxy == nullptr) {
-                // No path
-                continue;
-            }
-
-            // HACK: This is a policy decision we may want to revisit.
-            if (!proxy->isDylib()) {
-                continue;
-            }
-
-            if (!proxy->error.empty()) {
-                archManifest.results.exclude(proxy, proxy->error);
-                processedIdentifiers.insert(proxy->identifier);
-                continue;
-            }
-
-            if (proxy->isDylib()) {
-                if (_configurations[configuration].restrictedInstallnames.count(proxy->installName) != 0) {
-                    removeDylib(proxy, "Dylib '" + proxy->installName + "' removed due to explict restriction", configuration, architecture,
-                        processedIdentifiers);
-                    continue;
-                }
-
-                if (archManifest.results.dylibs.count(proxy->identifier) == 0) {
-                    archManifest.results.dylibs[proxy->identifier].uuid = proxy->uuid;
-                    archManifest.results.dylibs[proxy->identifier].installname = proxy->installName;
-                    archManifest.results.dylibs[proxy->identifier].included = true;
-
-                    processedIdentifiers.insert(proxy->identifier);
-                }
-            }
-
-            for (const auto& dependency : proxy->requiredIdentifiers) {
-                if (processedIdentifiers.count(dependency) == 0) {
-                    newIdentifiers.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"] = @(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.second.installname )] = 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/interlinked-dylibs/MultiCacheBuilder.h b/interlinked-dylibs/MultiCacheBuilder.h
deleted file mode 100644 (file)
index cd29ebd..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-//
-//  MultiCacheBuilder.h
-//  dyld
-//
-//  Created by Louis Gerbarg on 6/16/15.
-//
-//
-
-#ifndef MultiCacheBuilder_h
-#define MultiCacheBuilder_h
-
-#include <dispatch/dispatch.h>
-
-#include "Manifest.h"
-
-#include "mega-dylib-utils.h"
-#include <stdlib.h>
-
-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 buildCache(const std::string cachePath, const std::set<std::string> configurations, const std::string architecture, bool development);
-    void write_cache(std::string cachePath, const std::set<std::string>& configurations, const std::string& architecture, std::shared_ptr<SharedCache> cache, bool developmentCache);
-};
-
-#endif /* MultiCacheBuilder_h */
diff --git a/interlinked-dylibs/MultiCacheBuilder.mm b/interlinked-dylibs/MultiCacheBuilder.mm
deleted file mode 100644 (file)
index 4658cbc..0000000
+++ /dev/null
@@ -1,421 +0,0 @@
-//
-//  SharedCacheBuilder.m
-//  dyld
-//
-//  Created by Louis Gerbarg on 6/15/15.
-//
-//
-
-#include <CommonCrypto/CommonCrypto.h>
-#include <Bom/Bom.h>
-
-#include <sys/types.h>
-#include <sys/sysctl.h>
-#include <pthread.h>
-#include <mach/mach.h>
-#include <unistd.h>
-
-#include <cstring>
-
-#include <array>
-#include <sstream>
-#include <iomanip>      // std::setfill, std::setw
-#include "mega-dylib-utils.h"
-#include "Logging.h"
-
-#include "MachOProxy.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<std::string>& configurations, const std::string& architecture, std::shared_ptr<SharedCache> 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<std::string> configurations, const std::string architecture, bool development)
-{
-    auto& configResults = _manifest.configuration(*configurations.begin()).architecture(architecture).results.dylibs;
-
-    if ( _skipBuilds ) {
-        log( "Build Skipped" );
-
-        for ( auto& config : configurations ) {
-            for ( auto& dylib : configResults ) {
-                _manifest.configuration(config).architecture(architecture).results.exclude(MachOProxy::forIdentifier(dylib.first, architecture), "All dylibs excluded");
-            }
-        }
-        return;
-       }
-
-       std::vector<std::unique_ptr<MachOProxy>> dylibs;
-       std::vector<std::string> emptyList;
-       std::shared_ptr<SharedCache> cache = std::make_shared<SharedCache>(_manifest, *configurations.begin(), architecture);
-
-       for (auto& config : configurations) {
-        auto& results = _manifest.configuration(config).architecture(architecture).results;
-
-        for (auto& dylib : configResults) {
-            if (dylib.second.included == false
-                && results.dylibs.count(dylib.first)
-                && results.dylibs[dylib.first].included == true) {
-                results.exclude(MachOProxy::forIdentifier(dylib.first, architecture), dylib.second.exclusionInfo);
-            }
-               }
-       }
-
-    if (development) {
-        cache->buildForDevelopment(cachePath);
-    } else {
-        cache->buildForProduction(cachePath);
-    }
-
-    std::vector<uint64_t> regionStartAddresses;
-       std::vector<uint64_t> regionSizes;
-       std::vector<uint64_t> 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.configuration(config).architecture(architecture).results.developmentCache.regions.push_back({ prot, vmAddr, vmAddr + size });
-            } else {
-                _manifest.configuration(config).architecture(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<MachOProxySegment>& 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.configuration(config).architecture(architecture).results.dylibForInstallname(installName).segments.push_back({ seg.name, vmAddr, vmAddr + seg.size });
-                if (_manifest.configuration(config).architecture(architecture).results.dylibForInstallname(installName).segments.size() == 0) {
-                    warning("Attempting to write info for non-existent dylib");
-                }
-            }
-        }
-    });
-    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.configuration(config).architecture(architecture).results.developmentCache.cdHash = cache->cdHashString();
-        } else {
-            _manifest.configuration(config).architecture(architecture).results.productionCache.cdHash = cache->cdHashString();
-        }
-       }
-}
-
-void MultiCacheBuilder::buildCaches(std::string masterDstRoot) {
-       if (_bniMode) {
-               std::vector<std::set<std::string>> dedupedCacheSets;
-        _manifest.forEachConfiguration([&dedupedCacheSets, this](const std::string& configName) {
-            auto config = _manifest.configuration(configName);
-            bool dupeFound = false;
-
-            for (auto& cacheSet : dedupedCacheSets) {
-                if (config == _manifest.configuration(*cacheSet.begin())) {
-                    cacheSet.insert(configName);
-                    dupeFound = true;
-                    break;
-                }
-            }
-
-            if (!dupeFound) {
-                std::set<std::string> temp;
-                temp.insert(configName);
-                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<uint8_t, CC_SHA1_DIGEST_LENGTH> 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.configuration(*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<LoggingContext>(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) {
-            _manifest.forEachConfiguration([this, &masterDstRoot](const std::string& configName) {
-                auto config = _manifest.configuration(configName);
-                //            for ( auto& configuration : _manifest.configurations ) {
-                std::vector<std::string> prodBomPaths;
-                std::vector<std::string> devBomPaths;
-
-                for (auto& arch : config.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(), configName.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(), configName.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(), configName.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 {
-        _manifest.runConcurrently(_buildQueue, _concurrencyLimitingSemaphore,
-            [&](const std::string configuration, const std::string architecture) {
-                cacheBuilderDispatchGroupAsync(_writeGroup, _buildQueue, [=] {
-                    std::set<std::string> 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/interlinked-dylibs/ObjC1Abstraction.hpp b/interlinked-dylibs/ObjC1Abstraction.hpp
deleted file mode 100644 (file)
index 93bf751..0000000
+++ /dev/null
@@ -1,231 +0,0 @@
-/* -*- 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 <typename P>
-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 <typename P>
-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 <typename P>
-struct objc_method_list {
-    enum { OBJC_FIXED_UP = 1771 };
-    uint32_t obsolete;      // struct objc_method_list *
-    uint32_t method_count;  // int
-    struct objc_method<P> 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 <typename P>
-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<P> *getIsa(ContentAccessor* cache) const INLINE { return (struct objc_class<P> *)cache->contentForVMAddr(P::E::get32(isa)); }
-    struct objc_method_list<P> *getMethodList(ContentAccessor* cache) const INLINE { return (struct objc_method_list<P> *)cache->contentForVMAddr(P::E::get32(methodList)); }
-};
-
-template <typename P>
-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<P> *getInstanceMethods(ContentAccessor* cache) const INLINE { return (struct objc_method_list<P> *)cache->contentForVMAddr(P::E::get32(instance_methods)); }
-    struct objc_method_list<P> *getClassMethods(ContentAccessor* cache) const INLINE { return (struct objc_method_list<P> *)cache->contentForVMAddr(P::E::get32(class_methods)); }
-};
-
-template <typename P>
-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<P> *getClass(ContentAccessor* cache, int index) const INLINE { return (struct objc_class<P> *)cache->contentForVMAddr(P::E::get32(defs[index])); }
-    struct objc_category<P> *getCategory(ContentAccessor* cache, int index) const INLINE { return (struct objc_category<P> *)cache->contentForVMAddr(P::E::get32(defs[getClassCount() + index])); }
-};
-
-template <typename P>
-struct objc_module {
-    uint32_t version;  // unsigned long
-    uint32_t size;     // unsigned long
-    uint32_t name;     // char*
-    uint32_t symtab;   // Symtab
-    
-    struct objc_symtab<P> *getSymtab(ContentAccessor* cache) const INLINE { return (struct objc_symtab<P> *)cache->contentForVMAddr(P::E::get32(symtab)); }
-};
-
-template <typename P>
-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 <typename P>
-struct objc_method_description_list {
-    uint32_t count;  // int
-    struct objc_method_description<P> list[0];
-    
-    uint32_t getCount() const INLINE { return P::E::get32(count); }
-};
-
-template <typename P>
-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<P> *getInstanceMethodDescriptions(ContentAccessor* cache) const INLINE { return (struct objc_method_description_list<P> *)cache->contentForVMAddr(P::E::get32(instance_methods)); }
-    struct objc_method_description_list<P> *getClassMethodDescriptions(ContentAccessor* cache) const INLINE { return (struct objc_method_description_list<P> *)cache->contentForVMAddr(P::E::get32(class_methods)); }
-};
-
-
-template <typename P, typename V>
-class LegacySelectorUpdater {
-    typedef typename P::uint_t pint_t;
-
-    static void visitMethodList(objc_method_list<P> *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<P> *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<P>* header, V& visitor)
-    {
-        ArraySection<P, objc_module<P> >
-            modules(cache, header, "__OBJC", "__module_info");
-        for (uint64_t m = 0; m < modules.count(); m++) {
-            objc_symtab<P> *symtab = modules.get(m).getSymtab(cache);
-            if (!symtab) continue;
-
-            // Method lists in classes
-            for (int c = 0; c < symtab->getClassCount(); c++) {
-                objc_class<P> *cls = symtab->getClass(cache, c);
-                objc_class<P> *isa = cls->getIsa(cache);
-                objc_method_list<P> *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<P> *cat = symtab->getCategory(cache, c);
-                objc_method_list<P> *mlist;
-                if ((mlist = cat->getInstanceMethods(cache))) {
-                    visitMethodList(mlist, visitor);
-                }
-                if ((mlist = cat->getClassMethods(cache))) {
-                    visitMethodList(mlist, visitor);
-                }
-            }
-        }
-
-        // Method description lists from protocols        
-        ArraySection<P, objc_protocol<P>>
-            protocols(cache, header, "__OBJC", "__protocol");
-        for (uint64_t p = 0; p < protocols.count(); p++) {
-            objc_protocol<P>& protocol = protocols.get(p);
-            objc_method_description_list<P> *mlist;
-            if ((mlist = protocol.getInstanceMethodDescriptions(cache))) {
-                visitMethodDescriptionList(mlist, visitor);
-            }
-            if ((mlist = protocol.getClassMethodDescriptions(cache))) {
-                visitMethodDescriptionList(mlist, visitor);
-            }
-        }
-
-        // Message refs
-        PointerSection<P, const char *> 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/interlinked-dylibs/ObjC2Abstraction.hpp b/interlinked-dylibs/ObjC2Abstraction.hpp
deleted file mode 100644 (file)
index 35e9aba..0000000
+++ /dev/null
@@ -1,1217 +0,0 @@
-/* -*- 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 <iterator>
-#include <deque>
-#include <set>
-
-// iterate an entsize-based list
-// typedef entsize_iterator<P, type_t<P>, type_list_t<P> > type_iterator;
-template <typename P, typename T, typename Tlist>
-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<P,T,Tlist>& operator += (ptrdiff_t count) {
-        current = (T*)((uint8_t *)current + count*entsize);
-        index += count;
-        return *this;
-    }
-    const entsize_iterator<P,T,Tlist>& operator -= (ptrdiff_t count) {
-        current = (T*)((uint8_t *)current - count*entsize);
-        index -= count;
-        return *this;
-    }
-    const entsize_iterator<P,T,Tlist> operator + (ptrdiff_t count) const {
-        return entsize_iterator(*this) += count;
-    }
-    const entsize_iterator<P,T,Tlist> operator - (ptrdiff_t count) const {
-        return entsize_iterator(*this) -= count;
-    }
-    
-    entsize_iterator<P,T,Tlist>& operator ++ () { *this += 1; return *this; }
-    entsize_iterator<P,T,Tlist>& operator -- () { *this -= 1; return *this; }
-    entsize_iterator<P,T,Tlist> operator ++ (int) { 
-        entsize_iterator<P,T,Tlist> result(*this); *this += 1; return result; 
-    }
-    entsize_iterator<P,T,Tlist> operator -- (int) { 
-        entsize_iterator<P,T,Tlist> result(*this); *this -= 1; return result; 
-    }
-    
-    ptrdiff_t operator - (const entsize_iterator<P,T,Tlist>& 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<P,T,Tlist>& rhs) {
-        return this->current == rhs.current;
-    }
-    bool operator != (const entsize_iterator<P,T,Tlist>& rhs) {
-        return this->current != rhs.current;
-    }
-    
-    bool operator < (const entsize_iterator<P,T,Tlist>& rhs) {
-        return this->current < rhs.current;
-    }        
-    bool operator > (const entsize_iterator<P,T,Tlist>& rhs) {
-        return this->current > rhs.current;
-    }
-
-    
-    static void overwrite(entsize_iterator<P,T,Tlist>& dst, const Tlist* srcList)
-    {
-        entsize_iterator<P,T,Tlist> src;
-        uint32_t ee = srcList->getEntsize();
-        for (src = srcList->begin(); src != srcList->end(); ++src) {
-            memcpy(&*dst, &*src, ee);
-            ++dst;
-        }
-    }
-};
-
-template <typename P> 
-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<P>* mh)
-        : data(0) {
-    }
-};
-
-template <typename P>
-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<P>* 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<P>* 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<P>* rwmh = const_cast<macho_header<P>*>(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 <typename P>
-class objc_method_list_t;  // forward reference
-
-
-template <typename P>
-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<P>;
-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<P>&, 
-                                    const objc_method_t<P>&, bool>
-    {
-        bool operator() (const objc_method_t<P>& lhs, 
-                         const objc_method_t<P>& rhs)
-        {
-            return lhs.getName() < rhs.getName();
-        }
-    };
-};
-
-template <typename P>
-class objc_method_list_t {
-    uint32_t entsize;
-    uint32_t count;
-    objc_method_t<P> first;
-
-    void* operator new (size_t, void* buf) { return buf; }
-
-public:
-
-    typedef entsize_iterator<P, objc_method_t<P>, objc_method_list_t<P> > 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<P>& get(uint32_t i) const { return *(objc_method_t<P> *)((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<P>)) { 
-        return sizeof(objc_method_list_t<P>) - sizeof(objc_method_t<P>) + 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<void*>& pointersToRemove) {
-        for(method_iterator it = begin(); it != end(); ++it) {
-            objc_method_t<P>& entry = *it;
-            pointersToRemove.insert(&(entry.name));
-            pointersToRemove.insert(&(entry.types));
-            pointersToRemove.insert(&(entry.imp));
-        }
-    }
-    
-    static void addPointers(uint8_t* methodList, std::vector<void*>& pointersToAdd) {
-        objc_method_list_t<P>* mlist = (objc_method_list_t<P>*)methodList;
-        for(method_iterator it = mlist->begin(); it != mlist->end(); ++it) {
-            objc_method_t<P>& entry = *it;
-            pointersToAdd.push_back(&(entry.name));
-            pointersToAdd.push_back(&(entry.types));
-            pointersToAdd.push_back(&(entry.imp));
-        }
-    }
-
-    static objc_method_list_t<P>* newMethodList(size_t newCount, uint32_t newEntsize) {
-        void *buf = ::calloc(byteSizeForCount(newCount, newEntsize), 1);
-        return new (buf) objc_method_list_t<P>(newCount, newEntsize);
-    }
-
-    void operator delete(void * p) { 
-        ::free(p); 
-    }
-
-    objc_method_list_t(uint32_t newCount, 
-                       uint32_t newEntsize = sizeof(objc_method_t<P>))
-        : entsize(newEntsize), count(newCount) 
-    { }
-
-private:
-    // use newMethodList instead
-    void* operator new (size_t);
-};
-
-
-template <typename P>
-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<<a;
-    }
-};
-
-template <typename P>
-class objc_ivar_list_t {
-    typedef typename P::uint_t pint_t;
-    uint32_t entsize;
-    uint32_t count;
-    objc_ivar_t<P> first;
-
-    void* operator new (size_t, void* buf) { return buf; }
-
-public:
-
-    typedef entsize_iterator<P, objc_ivar_t<P>, objc_ivar_list_t<P> > ivar_iterator;
-
-    uint32_t getCount() const { return P::E::get32(count); }
-
-    uint32_t getEntsize() const { return P::E::get32(entsize); }
-
-    objc_ivar_t<P>& get(pint_t i) const { return *(objc_ivar_t<P> *)((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<P>)) { 
-        return sizeof(objc_ivar_list_t<P>) - sizeof(objc_ivar_t<P>) + 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<P>* newIvarList(size_t newCount, uint32_t newEntsize) {
-        void *buf = ::calloc(byteSizeForCount(newCount, newEntsize), 1);
-        return new (buf) objc_ivar_list_t<P>(newCount, newEntsize);
-    }
-
-    void operator delete(void * p) { 
-        ::free(p); 
-    }
-
-    objc_ivar_list_t(uint32_t newCount, 
-                         uint32_t newEntsize = sizeof(objc_ivar_t<P>))
-        : entsize(newEntsize), count(newCount) 
-    { }
-private:
-    // use newIvarList instead
-    void* operator new (size_t);
-};
-
-
-template <typename P> class objc_property_list_t; // forward 
-
-template <typename P>
-class objc_property_t {
-    typedef typename P::uint_t pint_t;
-    pint_t name;
-    pint_t attributes;
-    friend class objc_property_list_t<P>;
-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 <typename P>
-class objc_property_list_t {
-    uint32_t entsize;
-    uint32_t count;
-    objc_property_t<P> first;
-
-    void* operator new (size_t, void* buf) { return buf; }
-
-public:
-
-    typedef entsize_iterator<P, objc_property_t<P>, objc_property_list_t<P> > property_iterator;
-
-    uint32_t getCount() const { return P::E::get32(count); }
-
-    uint32_t getEntsize() const { return P::E::get32(entsize); }
-
-    objc_property_t<P>& get(uint32_t i) const { return *(objc_property_t<P> *)((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<P>)) { 
-        return sizeof(objc_property_list_t<P>) - sizeof(objc_property_t<P>) + 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<void*>& pointersToRemove) {
-        for(property_iterator it = begin(); it != end(); ++it) {
-            objc_property_t<P>& entry = *it;
-            pointersToRemove.insert(&(entry.name));
-            pointersToRemove.insert(&(entry.attributes));
-        }
-    }
-
-    static void addPointers(uint8_t* propertyList, std::vector<void*>& pointersToAdd) {
-        objc_property_list_t<P>* plist = (objc_property_list_t<P>*)propertyList;
-        for(property_iterator it = plist->begin(); it != plist->end(); ++it) {
-            objc_property_t<P>& entry = *it;
-            pointersToAdd.push_back(&(entry.name));
-            pointersToAdd.push_back(&(entry.attributes));
-        }
-    }
-
-     static objc_property_list_t<P>* newPropertyList(size_t newCount, uint32_t newEntsize) {
-        void *buf = ::calloc(byteSizeForCount(newCount, newEntsize), 1);
-        return new (buf) objc_property_list_t<P>(newCount, newEntsize);
-    }
-
-    void operator delete(void * p) { 
-        ::free(p); 
-    }
-
-    objc_property_list_t(uint32_t newCount, 
-                         uint32_t newEntsize = sizeof(objc_property_t<P>))
-        : entsize(newEntsize), count(newCount) 
-    { }
-private:
-    // use newPropertyList instead
-    void* operator new (size_t);
-};
-
-
-template <typename A> class objc_protocol_list_t;  // forward reference
-
-template <typename P>
-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<P> *getProtocols(ContentAccessor* cache) const { return (objc_protocol_list_t<P> *)cache->contentForVMAddr(P::getP(protocols)); }
-
-    objc_method_list_t<P> *getInstanceMethods(ContentAccessor* cache) const { return (objc_method_list_t<P> *)cache->contentForVMAddr(P::getP(instanceMethods)); }
-
-    objc_method_list_t<P> *getClassMethods(ContentAccessor* cache) const { return (objc_method_list_t<P> *)cache->contentForVMAddr(P::getP(classMethods)); }
-
-    objc_method_list_t<P> *getOptionalInstanceMethods(ContentAccessor* cache) const { return (objc_method_list_t<P> *)cache->contentForVMAddr(P::getP(optionalInstanceMethods)); }
-
-    objc_method_list_t<P> *getOptionalClassMethods(ContentAccessor* cache) const { return (objc_method_list_t<P> *)cache->contentForVMAddr(P::getP(optionalClassMethods)); }
-
-    objc_property_list_t<P> *getInstanceProperties(ContentAccessor* cache) const { return (objc_property_list_t<P> *)cache->contentForVMAddr(P::getP(instanceProperties)); }
-
-    pint_t *getExtendedMethodTypes(ContentAccessor* cache) const {
-        if (getSize() < offsetof(objc_protocol_t<P>, 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<P>, 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<P>, demangledName) + sizeof(demangledName)) {
-            terminate("objc protocol has the wrong size");
-        }
-        P::setP(demangledName, cache->vmAddrForContent((void*)newName));
-    }
-
-    void addPointers(std::vector<void*>& 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 <typename P>
-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<P>* get(ContentAccessor* cache, pint_t i) {
-        return (objc_protocol_t<P>*)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<P>* 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<P>) + c*sizeof(pint_t);
-    }
-
-    void getPointers(std::set<void*>& pointersToRemove) {
-        for(int i=0 ; i < count; ++i) {
-            pointersToRemove.insert(&list[i]);
-        }
-    }
-
-     static void addPointers(uint8_t* protocolList, std::vector<void*>& pointersToAdd) {
-        objc_protocol_list_t<P>* plist = (objc_protocol_list_t<P>*)protocolList;
-        for(int i=0 ; i < plist->count; ++i) {
-            pointersToAdd.push_back(&plist->list[i]);
-        }
-    }
-
-    static objc_protocol_list_t<P>* newProtocolList(pint_t newCount) {
-        void *buf = ::calloc(byteSizeForCount(newCount), 1);
-        return new (buf) objc_protocol_list_t<P>(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 <typename P>
-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<P> *getMethodList(ContentAccessor* cache) const { return (objc_method_list_t<P> *)cache->contentForVMAddr(P::getP(baseMethods)); }
-
-    objc_protocol_list_t<P> *getProtocolList(ContentAccessor* cache) const { return (objc_protocol_list_t<P> *)cache->contentForVMAddr(P::getP(baseProtocols)); }
-
-    objc_ivar_list_t<P> *getIvarList(ContentAccessor* cache) const { return (objc_ivar_list_t<P> *)cache->contentForVMAddr(P::getP(ivars)); }
-    
-    objc_property_list_t<P> *getPropertyList(ContentAccessor* cache) const { return (objc_property_list_t<P> *)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<P>* mlist) {
-        P::setP(baseMethods, cache->vmAddrForContent(mlist));
-    }
-
-    void setProtocolList(ContentAccessor* cache, objc_protocol_list_t<P>* protolist) {
-        P::setP(baseProtocols, cache->vmAddrForContent(protolist));
-    }
-    void setPropertyList(ContentAccessor* cache, objc_property_list_t<P>* proplist) {
-        P::setP(baseProperties, cache->vmAddrForContent(proplist));
-    }
-    
-    void addMethodListPointer(std::vector<void*>& pointersToAdd) {
-        pointersToAdd.push_back(&this->baseMethods);
-    }
-    
-    void addPropertyListPointer(std::vector<void*>& pointersToAdd) {
-        pointersToAdd.push_back(&this->baseProperties);
-    }
-    
-    void addProtocolListPointer(std::vector<void*>& pointersToAdd) {
-        pointersToAdd.push_back(&this->baseProtocols);
-    }
-};
-
-template <typename P>
-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<P> *getIsa(ContentAccessor* cache) const { return (objc_class_t<P> *)cache->contentForVMAddr(P::getP(isa)); }
-
-    objc_class_t<P> *getSuperclass(ContentAccessor* cache) const { return (objc_class_t<P> *)cache->contentForVMAddr(P::getP(superclass)); }
-    
-    // Low bit marks Swift classes.
-    objc_class_data_t<P> *getData(ContentAccessor* cache) const { return (objc_class_data_t<P> *)cache->contentForVMAddr(P::getP(data & ~0x1LL)); }
-
-    objc_method_list_t<P> *getMethodList(ContentAccessor* cache) const {
-        objc_class_data_t<P>* d = getData(cache);
-        return d->getMethodList(cache);
-    }
-
-    objc_protocol_list_t<P> *getProtocolList(ContentAccessor* cache) const { return getData(cache)->getProtocolList(cache); }
-
-    objc_property_list_t<P> *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<P>* mlist) {
-        getData(cache)->setMethodList(cache, mlist);
-    }
-
-    void setProtocolList(ContentAccessor* cache, objc_protocol_list_t<P>* protolist) {
-        getData(cache)->setProtocolList(cache, protolist);
-    }
-
-    void setPropertyList(ContentAccessor* cache, objc_property_list_t<P>* proplist) {
-        getData(cache)->setPropertyList(cache, proplist);
-    }
-    
-    void addMethodListPointer(ContentAccessor* cache, std::vector<void*>& pointersToAdd) {
-        getData(cache)->addMethodListPointer(pointersToAdd);
-    }
-    
-    void addPropertyListPointer(ContentAccessor* cache, std::vector<void*>& pointersToAdd) {
-        getData(cache)->addPropertyListPointer(pointersToAdd);
-    }
-    
-    void addProtocolListPointer(ContentAccessor* cache, std::vector<void*>& pointersToAdd) {
-        getData(cache)->addProtocolListPointer(pointersToAdd);
-    }
-    
-};
-
-
-
-template <typename P>
-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<P> *getClass(ContentAccessor* cache) const { return (objc_class_t<P> *)cache->contentForVMAddr(P::getP(cls)); }
-
-    objc_method_list_t<P> *getInstanceMethods(ContentAccessor* cache) const { return (objc_method_list_t<P> *)cache->contentForVMAddr(P::getP(instanceMethods)); }
-
-    objc_method_list_t<P> *getClassMethods(ContentAccessor* cache) const { return (objc_method_list_t<P> *)cache->contentForVMAddr(P::getP(classMethods)); }
-
-    objc_protocol_list_t<P> *getProtocols(ContentAccessor* cache) const { return (objc_protocol_list_t<P> *)cache->contentForVMAddr(P::getP(protocols)); }
-    objc_property_list_t<P> *getInstanceProperties(ContentAccessor* cache) const { return (objc_property_list_t<P> *)cache->contentForVMAddr(P::getP(instanceProperties)); }
-
-    void getPointers(std::set<void*>& pointersToRemove) {
-        pointersToRemove.insert(&name);
-        pointersToRemove.insert(&cls);
-        pointersToRemove.insert(&instanceMethods);
-        pointersToRemove.insert(&classMethods);
-        pointersToRemove.insert(&protocols);
-        pointersToRemove.insert(&instanceProperties);
-    }
-
-
-};
-
-template <typename P>
-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 <typename P, typename V>
-class IvarWalker {
-    typedef typename P::uint_t pint_t;
-    V& ivarVisitor;
-public:
-    
-    IvarWalker(V& visitor) : ivarVisitor(visitor) { }
-    
-    void walk(ContentAccessor* cache, const macho_header<P>* header, objc_class_t<P> *cls)
-    {
-        objc_class_data_t<P> *data = cls->getData(cache);
-        objc_ivar_list_t<P> *ivars = data->getIvarList(cache);
-        if (ivars) {
-            for (pint_t i = 0; i < ivars->getCount(); i++) {
-                objc_ivar_t<P>& 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<P>* header, objc_class_t<P> *cls)
-    {
-        walk(cache, header, cls);
-    }
-};
-
-// Call visitor.visitClass() on every class.
-template <typename P, typename V>
-class ClassWalker {
-    typedef typename P::uint_t pint_t;
-    V& _visitor;
-public:
-    
-    ClassWalker(V& visitor) : _visitor(visitor) { }
-    
-    void walk(ContentAccessor* cache, const macho_header<P>* header)
-    {   
-        PointerSection<P, objc_class_t<P>*> classList(cache, header, "__DATA", "__objc_classlist");
-        
-        for (pint_t i = 0; i < classList.count(); i++) {
-            objc_class_t<P>* 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 <typename P, typename V>
-class ProtocolWalker {
-    typedef typename P::uint_t pint_t;
-    V& _protocolVisitor;
-public:
-    
-    ProtocolWalker(V& visitor) : _protocolVisitor(visitor) { }
-    
-    void walk(ContentAccessor* cache, const macho_header<P>* header)
-    {   
-        PointerSection<P, objc_protocol_t<P> *>
-            protocols(cache, header, "__DATA", "__objc_protolist");
-        
-        for (pint_t i = 0; i < protocols.count(); i++) {
-            objc_protocol_t<P> *proto = protocols.get(i);
-            _protocolVisitor.visitProtocol(cache, header, proto);
-        }
-    }
-};
-
-// Call visitor.visitProtocolReference() on every protocol.
-template <typename P, typename V>
-class ProtocolReferenceWalker {
-    typedef typename P::uint_t pint_t;
-    V& _visitor;
-
-    void visitProtocolList(ContentAccessor* cache,
-                           objc_protocol_list_t<P>* 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<P, ProtocolReferenceWalker<P, V>>;
-
-    void visitClass(ContentAccessor* cache, const macho_header<P>*,
-                    objc_class_t<P>* 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<P>* header)
-    {
-        // @protocol expressions
-        PointerSection<P, objc_protocol_t<P> *>
-            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<P, ProtocolReferenceWalker<P, V>> classes(*this);
-        classes.walk(cache, header);
-
-        // protocol lists in protocols
-        // __objc_protolists itself is NOT updated
-        PointerSection<P, objc_protocol_t<P> *>
-            protocols(cache, header, "__DATA", "__objc_protolist");
-        for (pint_t i = 0; i < protocols.count(); i++) {
-            objc_protocol_t<P>* 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 <typename P, typename V>
-class MethodListWalker {
-
-    typedef typename P::uint_t pint_t;
-
-    V& mVisitor;
-
-public: 
-    
-    MethodListWalker(V& visitor) : mVisitor(visitor) { }
-
-    void walk(ContentAccessor* cache, const macho_header<P>* header)
-    {   
-        // Method lists in classes
-        PointerSection<P, objc_class_t<P> *> 
-            classes(cache, header, "__DATA", "__objc_classlist");
-            
-        for (pint_t i = 0; i < classes.count(); i++) {
-            objc_class_t<P> *cls = classes.get(i);
-            objc_method_list_t<P> *mlist;
-            if ((mlist = cls->getMethodList(cache))) {
-                mVisitor.visitMethodList(mlist);
-            }
-            if ((mlist = cls->getIsa(cache)->getMethodList(cache))) {
-                mVisitor.visitMethodList(mlist);
-            }
-        }
-        
-        // Method lists from categories
-        PointerSection<P, objc_category_t<P> *> 
-            cats(cache, header, "__DATA", "__objc_catlist");
-        for (pint_t i = 0; i < cats.count(); i++) {
-            objc_category_t<P> *cat = cats.get(i);
-            objc_method_list_t<P> *mlist;
-            if ((mlist = cat->getInstanceMethods(cache))) {
-                mVisitor.visitMethodList(mlist);
-            }
-            if ((mlist = cat->getClassMethods(cache))) {
-                mVisitor.visitMethodList(mlist);
-            }
-        }
-
-        // Method description lists from protocols
-        PointerSection<P, objc_protocol_t<P> *>
-            protocols(cache, header, "__DATA", "__objc_protolist");
-        for (pint_t i = 0; i < protocols.count(); i++) {
-            objc_protocol_t<P> *proto = protocols.get(i);
-            objc_method_list_t<P> *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 <typename P, typename V>
-class SelectorOptimizer {
-
-    typedef typename P::uint_t pint_t;
-
-    V& mVisitor;
-
-    friend class MethodListWalker<P, SelectorOptimizer<P,V> >;
-    void visitMethodList(objc_method_list_t<P> *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<P> *mlist, pint_t *types)
-    {
-        visitMethodList(mlist);
-    }
-
-public:
-
-    SelectorOptimizer(V& visitor) : mVisitor(visitor) { }
-
-    void optimize(ContentAccessor* cache, const macho_header<P>* header)
-    {
-        // method lists in classes, categories, and protocols
-        MethodListWalker<P, SelectorOptimizer<P,V> > mw(*this);
-        mw.walk(cache, header);
-        
-        // @selector references
-        PointerSection<P, const char *> 
-            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<P, objc_message_ref_t<P> > 
-            msgrefs(cache, header, "__DATA", "__objc_msgrefs");
-        for (pint_t i = 0; i < msgrefs.count(); i++) {
-            objc_message_ref_t<P>& 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 <typename P>
-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<P>* /*unused, may be NULL*/, objc_class_t<P> *cls, objc_ivar_t<P> *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<P>* /*unused, may be NULL*/, objc_class_t<P> *cls)
-    {
-        objc_class_t<P> *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<P> *data = cls->getData(cache);
-            objc_class_data_t<P> *super_data = super->getData(cache);
-            int32_t diff = super_data->getInstanceSize() - data->getInstanceStart();
-            if (diff > 0) {
-                IvarWalker<P, IvarOffsetOptimizer<P> > 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<P>* header)
-    {
-        // The slide code cannot fix up GC layout strings so skip modules that support or require GC
-        const macho_section<P> *imageInfoSection = header->getSection("__DATA", "__objc_imageinfo");
-        if (imageInfoSection) {
-            objc_image_info<P> *info = (objc_image_info<P> *)cache->contentForVMAddr(imageInfoSection->addr());
-            if (!info->supportsGCFlagSet() && !info->requiresGCFlagSet()) {
-                ClassWalker<P, IvarOffsetOptimizer<P> > classVisitor(*this);
-                classVisitor.walk(cache, header);
-            } else {
-                //fprintf(stderr, "GC support present - skipped module\n");
-            }
-        }
-    }
-};
-
-
-// Detect classes that have missing weak-import superclasses.
-template <typename P>
-class WeakClassDetector {
-    bool noMissing;
-
-    friend class ClassWalker<P, WeakClassDetector<P>>;
-    void visitClass(ContentAccessor* cache, const macho_header<P>*, 
-                    objc_class_t<P>* 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<const macho_header<P>*> dylibs)
-    {
-        noMissing = true;
-        ClassWalker<P, WeakClassDetector<P>> classes(*this);
-        for (auto mh : dylibs) {
-            classes.walk(cache, mh);
-        }
-        return noMissing;
-    }
-};
-
-
-// Sort methods in place by selector.
-template <typename P>
-class MethodListSorter {
-
-    typedef typename P::uint_t pint_t;
-
-    uint32_t _optimized;
-
-    friend class MethodListWalker<P, MethodListSorter<P> >;
-    void visitMethodList(objc_method_list_t<P> *mlist)
-    {
-        typename objc_method_t<P>::SortBySELAddress sorter;
-        std::stable_sort(mlist->begin(), mlist->end(), sorter);
-        mlist->setFixedUp();
-        _optimized++;
-    }
-
-    void visitProtocolMethodList(objc_method_list_t<P> *mlist, pint_t *typelist)
-    {
-        typename objc_method_t<P>::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<P>& mi = mlist->get(i);
-                objc_method_t<P>& 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<P>* header)
-    {
-        MethodListWalker<P, MethodListSorter<P> > mw(*this);
-        mw.walk(cache, header);
-    }
-};
-
-
-template <typename P, typename InfoT>
-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<P>* mh, std::vector<void*>& pointersInData) {
-        InfoT* hi = new(&_hInfos[_count++]) InfoT(cache, mh);
-        (void)hi;
-    }
-
-    InfoT* hinfoForHeader(ContentAccessor* cache, const macho_header<P>* 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/interlinked-dylibs/OptimizerBranches.cpp b/interlinked-dylibs/OptimizerBranches.cpp
deleted file mode 100644 (file)
index 110b123..0000000
+++ /dev/null
@@ -1,1473 +0,0 @@
-/* -*- 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 <sys/types.h>
-#include <sys/stat.h>
-#include <sys/mman.h>
-#include <limits.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <unistd.h>
-
-#include <string>
-#include <unordered_map>
-#include <unordered_set>
-
-#include <CommonCrypto/CommonDigest.h>
-
-#include "MachOProxy.h"
-
-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",
-    // <rdar://problem/22050956> 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 <typename P>
-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<BranchPoolDylib<P>*>& branchIslandPools);
-    uint64_t                getBackBranch(uint64_t finalTargetAddr, const char* name, std::vector<BranchPoolDylib<P>*>& 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<uint64_t, uint32_t>      _targetToIslandIndex;
-       std::unordered_map<uint32_t, const char*>   _islandIndexToName;
-    macho_symtab_command<P>*                    _symbolTableCmd;
-       macho_dysymtab_command<P>*                  _dynamicSymbolTableCmd;
-    macho_uuid_command<P>*                      _uuidCmd;
-    uint32_t                                    _maxStubs;
-    uint32_t                                    _nextIndex;
-    uint32_t                                    _firstStubOffset;
-    uint32_t*                                   _stubInstructions;
-    macho_nlist<P>*                             _symbolTable;
-    char*                                       _nextString;
-    char*                                       _stringPoolStart;
-    char*                                       _stringPoolEnd;
-};
-
-
-template <typename P>
-BranchPoolDylib<P>::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<P>);
-    const uint32_t linkeditOffsetSymbolPoolOffset = linkeditOffsetIndirectSymbolTable + stubCount*sizeof(uint32_t);
-    _maxStubs = stubCount;
-
-    // write mach_header and load commands for pseudo dylib
-    macho_header<P>* mh = (macho_header<P>*)((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<P>* cmd = (macho_load_command<P>*)((uint8_t*)mh + sizeof(macho_header<P>));
-    macho_segment_command<P>* textSegCmd = (macho_segment_command<P>*)cmd;
-    textSegCmd->set_cmd(is64 ? LC_SEGMENT_64 : LC_SEGMENT);
-    textSegCmd->set_cmdsize(sizeof(macho_segment_command<P>)*2+sizeof(macho_section<P>));
-    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<P>* stubSection = (macho_section<P>*)((uint8_t*)textSegCmd + sizeof(macho_segment_command<P>));
-    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<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
-    macho_segment_command<P>* linkEditSegCmd = (macho_segment_command<P>*)cmd;
-    linkEditSegCmd->set_cmd(is64 ? LC_SEGMENT_64 :  LC_SEGMENT);
-    linkEditSegCmd->set_cmdsize(sizeof(macho_segment_command<P>));
-    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<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
-    macho_dylib_command<P>* installNameCmd = (macho_dylib_command<P>*)cmd;
-    installNameCmd->set_cmd(LC_ID_DYLIB);
-    installNameCmd->set_cmdsize(sizeof(macho_dylib_command<P>) + 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<P>), "dyld_shared_cache_branch_islands");
-    // LC_SYMTAB
-    cmd = (macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
-       _symbolTableCmd = (macho_symtab_command<P>*)cmd;
-       _symbolTableCmd->set_cmd(LC_SYMTAB);
-       _symbolTableCmd->set_cmdsize(sizeof(macho_symtab_command<P>));
-       _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<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
-       _dynamicSymbolTableCmd = (macho_dysymtab_command<P>*)cmd;
-       _dynamicSymbolTableCmd->set_cmd(LC_DYSYMTAB);
-       _dynamicSymbolTableCmd->set_cmdsize(sizeof(macho_dysymtab_command<P>));
-       _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<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
-    // LC_UUID
-       _uuidCmd = (macho_uuid_command<P>*)cmd;
-       _uuidCmd->set_cmd(LC_UUID);
-       _uuidCmd->set_cmdsize(sizeof(macho_uuid_command<P>));
-    cmd = (macho_load_command<P>*)(((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<P>*)(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], "<unused>");
-    _nextString = &_stringPoolStart[10];
-}
-
-
-template <typename P>
-void BranchPoolDylib<P>::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 <typename P>
-uint64_t BranchPoolDylib<P>::getForwardBranch(uint64_t finalTargetAddr, const char* name, std::vector<BranchPoolDylib<P>*>& 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<P>* 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<P>::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 <typename P>
-uint64_t BranchPoolDylib<P>::getBackBranch(uint64_t finalTargetAddr, const char* name, std::vector<BranchPoolDylib<P>*>& 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<P>* 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<P>::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 <typename P>
-void BranchPoolDylib<P>::printStats()
-{
-    verboseLog("  island pool at 0x%0llX has %u stubs and stringPool size=%lu", _startAddr, _nextIndex, _nextString-_stringPoolStart);
-}
-
-
-
-template <typename P>
-class StubOptimizer {
-public:
-                            StubOptimizer(void* cacheBuffer, macho_header<P>* mh);
-    void                    buildStubMap(const std::unordered_set<std::string>& neverStubEliminate);
-    void                    optimizeStubs(std::unordered_map<uint64_t,std::vector<uint64_t>>& targetToBranchIslands);
-    void                    bypassStubs(std::unordered_map<uint64_t,std::vector<uint64_t>>& targetToBranchIslands);
-    void                    optimizeCallSites(std::vector<BranchPoolDylib<P>*>& 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<bool(uint8_t callSiteKind, uint64_t callSiteAddr, uint64_t stubAddr, uint32_t& instruction)> CallSiteHandler;
-    typedef typename P::uint_t pint_t;
-    typedef typename P::E E;
-
-    void                    forEachCallSiteToAStub(CallSiteHandler);
-    void                    optimizeArm64CallSites(std::vector<BranchPoolDylib<P>*>& 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<pint_t, AddressAndName> StubVMAddrToTarget;
-
-    static const int64_t b128MegLimit = 0x07FFFFFF;
-    static const int64_t b16MegLimit  = 0x00FFFFFF;
-
-
-    macho_header<P>*                        _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<P>*          _symTabCmd           = nullptr;
-    const macho_dysymtab_command<P>*        _dynSymTabCmd        = nullptr;
-    const macho_dyld_info_command<P>*       _dyldInfo            = nullptr;
-    macho_linkedit_data_command<P>*         _splitSegInfoCmd     = nullptr;
-    const macho_section<P>*                 _textSection         = nullptr;
-    const macho_section<P>*                 _stubSection         = nullptr;
-    uint32_t                                _textSectionIndex    = 0;
-    uint32_t                                _stubSectionIndex    = 0;
-    pint_t                                  _textSegStartAddr    = 0;
-    uint32_t                                _textSegCacheOffset  = 0;
-    std::vector<macho_segment_command<P>*>  _segCmds;
-       std::unordered_map<pint_t, pint_t>      _stubAddrToLPAddr;
-       std::unordered_map<pint_t, pint_t>      _lpAddrToTargetAddr;
-       std::unordered_map<pint_t, const char*> _targetAddrToName;
-};
-
-
-
-template <typename P>
-StubOptimizer<P>::StubOptimizer(void* cacheBuffer, macho_header<P>* mh)
-: _mh(mh), _cacheBuffer(cacheBuffer)
-{
-    _linkeditBias = (uint8_t*)cacheBuffer;
-    const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)mh + sizeof(macho_header<P>));
-    const uint32_t cmd_count = mh->ncmds();
-    macho_segment_command<P>* segCmd;
-    uint32_t sectionIndex = 0;
-    const macho_load_command<P>* cmd = cmds;
-    for (uint32_t i = 0; i < cmd_count; ++i) {
-        switch (cmd->cmd()) {
-            case LC_ID_DYLIB:
-                _installName = ((macho_dylib_command<P>*)cmd)->name();
-                break;
-            case LC_SYMTAB:
-                _symTabCmd = (macho_symtab_command<P>*)cmd;
-                break;
-            case LC_DYSYMTAB:
-                _dynSymTabCmd = (macho_dysymtab_command<P>*)cmd;
-                break;
-            case LC_SEGMENT_SPLIT_INFO:
-                _splitSegInfoCmd = (macho_linkedit_data_command<P>*)cmd;
-                break;
-            case LC_DYLD_INFO:
-            case LC_DYLD_INFO_ONLY:
-                _dyldInfo = (macho_dyld_info_command<P>*)cmd;
-                break;
-            case macho_segment_command<P>::CMD:
-                segCmd =( macho_segment_command<P>*)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<P>* const sectionsStart = (macho_section<P>*)((char*)segCmd + sizeof(macho_segment_command<P>));
-                    const macho_section<P>* const sectionsEnd = &sectionsStart[segCmd->nsects()];
-                    for (const macho_section<P>* 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<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
-    }
-}
-
-
-
-template <typename P>
-uint32_t StubOptimizer<P>::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 <typename P>
-uint64_t StubOptimizer<P>::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 <typename P>
-void StubOptimizer<P>::buildStubMap(const std::unordered_set<std::string>& neverStubEliminate)
-{
-    // find all stubs and lazy pointers
-    const macho_nlist<P>* symbolTable = (const macho_nlist<P>*)(((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<P>* const cmds = (macho_load_command<P>*)((uint8_t*)_mh + sizeof(macho_header<P>));
-       const uint32_t cmd_count = _mh->ncmds();
-       const macho_load_command<P>* cmd = cmds;
-       for (uint32_t i = 0; i < cmd_count; ++i) {
-               if ( cmd->cmd() == macho_segment_command<P>::CMD ) {
-                       macho_segment_command<P>* seg = (macho_segment_command<P>*)cmd;
-                       macho_section<P>* const sectionsStart = (macho_section<P>*)((char*)seg + sizeof(macho_segment_command<P>));
-                       macho_section<P>* const sectionsEnd = &sectionsStart[seg->nsects()];
-                       for(macho_section<P>* 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<P>* 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<P>* 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<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
-       }
-}
-
-
-template <typename P>
-void StubOptimizer<P>::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                 :== <count> FromToSection+
-       // FromToSection :== <from-sect-index> <to-sect-index> <count> ToOffset+
-       // ToOffset              :== <to-sect-offset-delta> <count> FromOffset+
-       // FromOffset    :== <kind> <count> <from-sect-offset-delta>
-       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 <typename P>
-int32_t StubOptimizer<P>::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 <typename P>
-uint32_t StubOptimizer<P>::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 <typename P>
-void StubOptimizer<P>::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 <typename P>
-void StubOptimizer<P>::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 <typename P>
-void StubOptimizer<P>::optimizeArm64CallSites(std::vector<BranchPoolDylib<P>*>& 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<P>* 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<P>* 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 <typename P>
-void StubOptimizer<P>::optimizeCallSites(std::vector<BranchPoolDylib<P>*>& 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 <typename P>
-void SharedCache::bypassStubs(const std::vector<uint64_t>& branchPoolStartAddrs)
-{
-    verboseLog("Stub elimination optimization:");
-
-    // construct a StubOptimizer for each image
-    std::vector<StubOptimizer<P>*> optimizers;
-    forEachImage([&](const void* mh, const char*, time_t, ino_t, const std::vector<MachOProxySegment>&) {
-        optimizers.push_back(new StubOptimizer<P>(_buffer.get(), (macho_header<P>*)mh));
-    });
-
-    // construct a BranchPoolDylib for each pool
-    std::vector<BranchPoolDylib<P>*> 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<MachOProxySegment>& segs) {
-            for (const auto& 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<P>(_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<std::string> neverStubEliminate;
-    for (const char** p=sNeverStubEliminateSymbols; *p != nullptr; ++p) {
-        neverStubEliminate.insert(*p);
-    }
-    for (const char** d=sNeverStubEliminateDylibs; *d != nullptr; ++d) {
-        for (StubOptimizer<P>* 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<ExportInfoTrie::Entry> 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<P>* op : optimizers)
-        op->buildStubMap(neverStubEliminate);
-
-    // optimize call sites to by-pass stubs or jump through island
-    for (StubOptimizer<P>* op : optimizers)
-        op->optimizeCallSites(pools);
-
-   // final fix ups in branch pools
-    for (BranchPoolDylib<P>* pool : pools) {
-        pool->finalizeLoadCommands();
-        pool->printStats();
-    }
-
-    // write total optimization info
-    uint32_t callSiteCount = 0;
-    uint32_t callSiteDirectOptCount = 0;
-    uint32_t callSiteOneHopOptCount = 0;
-    for (StubOptimizer<P>* 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<P>* op : optimizers)
-        delete op;
-   for (BranchPoolDylib<P>* p : pools)
-        delete p;
-
-}
-
-
-void SharedCache::bypassStubs(const std::vector<uint64_t>& branchPoolStartAddrs) {
-    switch( _arch.arch ) {
-        case CPU_TYPE_ARM:
-            bypassStubs<Pointer32<LittleEndian>>(branchPoolStartAddrs);
-            break;
-        case CPU_TYPE_ARM64:
-            bypassStubs<Pointer64<LittleEndian>>(branchPoolStartAddrs);
-            break;
-        default:
-            // no stub optimization done for other arches
-            break;
-    }
-}
-
-
-/*
-template <typename P>
-void StubOptimizer<P>::optimizeStubs(std::unordered_map<uint64_t,std::vector<uint64_t>>& 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 <typename P>
-void StubOptimizer<P>::bypassStubs(std::unordered_map<uint64_t,std::vector<uint64_t>>& 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<uint64_t>& 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/interlinked-dylibs/OptimizerBranches.h b/interlinked-dylibs/OptimizerBranches.h
deleted file mode 100644 (file)
index ae50c53..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-/* -*- 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/interlinked-dylibs/OptimizerLinkedit.cpp b/interlinked-dylibs/OptimizerLinkedit.cpp
deleted file mode 100644 (file)
index a3b8f4a..0000000
+++ /dev/null
@@ -1,1185 +0,0 @@
-/* -*- 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 <dirent.h>
-#include <sys/errno.h>
-#include <sys/fcntl.h>
-#include <mach-o/loader.h>
-#include <mach-o/fat.h>
-#include <assert.h>
-
-#include <fstream>
-#include <iostream>
-#include <string>
-#include <algorithm>
-#include <unordered_map>
-#include <unordered_set>
-
-#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 <typename P>
-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<P>* symbolTable) {
-        // make sorted list of strings
-        std::vector<std::string> 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<std::string, std::vector<uint32_t>> _map;
-};
-
-
-
-
-struct LocalSymbolInfo
-{
-       uint32_t        dylibOffset;
-       uint32_t        nlistStartIndex;
-       uint32_t        nlistCount;
-};
-
-
-template <typename P>
-class LinkeditOptimizer {
-public:
-                    LinkeditOptimizer(void* cacheBuffer, macho_header<P>* 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<P>& stringPool, uint32_t& offset, uint32_t& symbolIndex);
-    void            copyImportedSymbols(uint8_t* newLinkEditContent, SortedStringPool<P>& stringPool, uint32_t& offset, uint32_t& symbolIndex);
-    void            copyLocalSymbols(uint8_t* newLinkEditContent, SortedStringPool<P>& stringPool, uint32_t& offset, uint32_t& symbolIndex,
-                                     bool redact, std::vector<LocalSymbolInfo>& localSymbolInfos,
-                                     std::vector<macho_nlist<P>>& unmappedLocalSymbols, SortedStringPool<P>& 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<P>*                        machHeader() { return _mh; }
-    const std::vector<const char*>          getDownwardDependents() { return _downDependentPaths; }
-    const std::vector<const char*>          getAllDependents() { return _allDependentPaths; }
-    const std::vector<const char*>          getReExportPaths() { return _reExportPaths; }
-    const std::vector<uint64_t>             initializerAddresses() { return _initializerAddresses; }
-    const std::vector<macho_section<P>*>    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<macho_segment_command<P>*>&  segCmds() { return _segCmds; }
-
-
-private:
-
-    typedef typename P::uint_t pint_t;
-    typedef typename P::E E;
-
-    macho_header<P>*                        _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<P>*                _symTabCmd          = nullptr;
-    macho_dysymtab_command<P>*              _dynSymTabCmd       = nullptr;
-    macho_dyld_info_command<P>*             _dyldInfo           = nullptr;
-    macho_linkedit_data_command<P>*         _functionStartsCmd  = nullptr;
-    macho_linkedit_data_command<P>*         _dataInCodeCmd      = nullptr;
-    std::vector<macho_segment_command<P>*>  _segCmds;
-    std::unordered_map<uint32_t,uint32_t>   _oldToNewSymbolIndexes;
-    std::vector<const char*>                _reExportPaths;
-    std::vector<const char*>                _downDependentPaths;
-    std::vector<const char*>                _allDependentPaths;
-    std::vector<uint64_t>                   _initializerAddresses;
-    std::vector<macho_section<P>*>          _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 <typename P>
-class AcceleratorTables {
-public:
-                AcceleratorTables(void* cacheBuffer, uint64_t linkeditStartAddr, const std::vector<LinkeditOptimizer<P>*>& optimizers);
-
-    uint32_t    totalSize() const;
-    void        copyTo(uint8_t* buffer);
-
-private:
-    typedef typename P::E  E;
-
-    struct NodeChain;
-
-    struct DepNode {
-        std::vector<DepNode*>   _dependents;
-        unsigned                _depth;
-        const char*             _installName;
-
-                                DepNode() : _depth(0), _installName(nullptr) { }
-        void                    computeDepth();
-        static void             verifyUnreachable(DepNode* target, NodeChain& chain, std::unordered_set<DepNode*>& visitedNodes, const std::vector<DepNode*>& from);
-    };
-
-    struct NodeChain {
-        NodeChain*   prev;
-        DepNode*     node;
-    };
-
-    std::unordered_map<macho_header<P>*, DepNode>                   _depDAG;
-    std::vector<dyldCacheImageInfoExtra<E>>                         _extraInfo;
-    std::vector<uint8_t>                                            _trieBytes;
-    std::vector<uint16_t>                                           _reExportArray;
-    std::vector<uint16_t>                                           _dependencyArray;
-    std::vector<uint16_t>                                           _bottomUpArray;
-    std::vector<dyldCacheAcceleratorInitializer<E>>                 _initializers;
-    std::vector<dyldCacheAcceleratorDOFEntry<E>>                    _dofSections;
-    std::vector<dyldCacheAcceleratorRangeEntry<E>>                  _rangeTable;
-    std::unordered_map<macho_header<P>*, uint32_t>                  _machHeaderToImageIndex;
-    std::unordered_map<std::string, macho_header<P>*>               _dylibPathToMachHeader;
-    std::unordered_map<macho_header<P>*, LinkeditOptimizer<P>*>     _machHeaderToOptimizer;
-    dyldCacheAcceleratorInfo<E>                                     _acceleratorInfoHeader;
-};
-
-
-template <typename P>
-void AcceleratorTables<P>::AcceleratorTables::DepNode::verifyUnreachable(AcceleratorTables<P>::DepNode* target, struct AcceleratorTables<P>::NodeChain& chain,
-                                                                        std::unordered_set<DepNode*>& visitedNodes, const std::vector<AcceleratorTables<P>::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 <typename P>
-AcceleratorTables<P>::AcceleratorTables(void* cacheBuffer, uint64_t linkeditStartAddr, const std::vector<LinkeditOptimizer<P>*>& optimizers)
-{
-    // build table mapping tables to map between mach_header, index, and optimizer
-    for ( LinkeditOptimizer<P>* 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<E>* header = (dyldCacheHeader<E>*)cacheBuffer;
-    const dyldCacheFileMapping<E>* mappings = (dyldCacheFileMapping<E>*)((uint8_t*)cacheBuffer + header->mappingOffset());
-    cacheStartAddress = mappings[0].address();
-    const dyldCacheImageInfo<E>* images = (dyldCacheImageInfo<E>*)((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<P>* mhMapped = (macho_header<P>*)((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<P>* op : optimizers) {
-        _depDAG[op->machHeader()]._installName = op->installName();
-    }
-    for (LinkeditOptimizer<P>* op : optimizers) {
-        DepNode& node = _depDAG[op->machHeader()];
-        for (const char* depPath : op->getDownwardDependents()) {
-            macho_header<P>* 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<DepNode*> 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<macho_header<P>*> sortedMachHeaders;
-    sortedMachHeaders.reserve(optimizers.size());
-    for (LinkeditOptimizer<P>* 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<P>* lmh, macho_header<P>* 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<E> 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<P>* 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<P>* mh : sortedMachHeaders) {
-        LinkeditOptimizer<P>* 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<P>* 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<P>* mh : sortedMachHeaders) {
-        LinkeditOptimizer<P>* 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<P>* 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<P>* mh : sortedMachHeaders) {
-        LinkeditOptimizer<P>* 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<E> 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<P>* mh : sortedMachHeaders) {
-        LinkeditOptimizer<P>* 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<E> 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<P>* mh : sortedMachHeaders) {
-        LinkeditOptimizer<P>* 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<P>* libdyldMH = _dylibPathToMachHeader["/usr/lib/system/libdyld.dylib"];
-    LinkeditOptimizer<P>* libdyldOp = _machHeaderToOptimizer[libdyldMH];
-    uint64_t dyldSectionAddr = libdyldOp->dyldSectionAddress();
-
-    // build range table for fast address->image lookups
-    for (macho_header<P>* mh : sortedMachHeaders) {
-        LinkeditOptimizer<P>* op = _machHeaderToOptimizer[mh];
-        unsigned imageIndex = _machHeaderToImageIndex[mh];
-        for ( const macho_segment_command<P>* segCmd : op->segCmds() ) {
-            if ( strcmp(segCmd->segname(), "__LINKEDIT") == 0 )
-                continue;
-            dyldCacheAcceleratorRangeEntry<E> 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<E>& lRange, const dyldCacheAcceleratorRangeEntry<E>& rRange) -> bool {
-                return (lRange.startAddress() < rRange.startAddress());
-              });
-
-    // build trie that maps install names to image index
-    std::vector<DylibIndexTrie::Entry> 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<E>& 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 <typename P>
-void AcceleratorTables<P>::DepNode::computeDepth()
-{
-    if ( _depth != 0 )
-        return;
-    _depth = 1;
-    for (DepNode* node : _dependents) {
-        node->computeDepth();
-        if ( node->_depth >= _depth )
-            _depth = node->_depth + 1;
-    }
-}
-
-template <typename P>
-uint32_t AcceleratorTables<P>::totalSize() const
-{
-    return (uint32_t)align(_acceleratorInfoHeader.rangeTableOffset() + _acceleratorInfoHeader.rangeTableCount()*sizeof(dyld_cache_range_entry), 14);
-}
-
-template <typename P>
-void AcceleratorTables<P>::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 <typename P>
-LinkeditOptimizer<P>::LinkeditOptimizer(void* cacheBuffer, macho_header<P>* mh)
-: _mh(mh), _cacheBuffer(cacheBuffer)
-{
-    _linkeditBias = (uint8_t*)cacheBuffer;
-    const unsigned origLoadCommandsSize = mh->sizeofcmds();
-    unsigned bytesRemaining = origLoadCommandsSize;
-    unsigned removedCount = 0;
-    const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)mh + sizeof(macho_header<P>));
-    const uint32_t cmdCount = mh->ncmds();
-    const macho_load_command<P>* cmd = cmds;
-    const macho_dylib_command<P>* dylibCmd;
-    const macho_routines_command<P>* routinesCmd;
-    macho_segment_command<P>* segCmd;
-    for (uint32_t i = 0; i < cmdCount; ++i) {
-        bool remove = false;
-        switch (cmd->cmd()) {
-            case LC_ID_DYLIB:
-                _installName = ((macho_dylib_command<P>*)cmd)->name();
-                break;
-            case LC_SYMTAB:
-                _symTabCmd = (macho_symtab_command<P>*)cmd;
-                break;
-            case LC_DYSYMTAB:
-                _dynSymTabCmd = (macho_dysymtab_command<P>*)cmd;
-                break;
-            case LC_DYLD_INFO:
-            case LC_DYLD_INFO_ONLY:
-                _dyldInfo = (macho_dyld_info_command<P>*)cmd;
-                _exportInfoSize = _dyldInfo->export_size();
-                break;
-            case LC_FUNCTION_STARTS:
-                _functionStartsCmd = (macho_linkedit_data_command<P>*)cmd;
-                break;
-            case LC_DATA_IN_CODE:
-                _dataInCodeCmd = (macho_linkedit_data_command<P>*)cmd;
-                break;
-            case LC_ROUTINES:
-            case LC_ROUTINES_64:
-                routinesCmd = (macho_routines_command<P>*)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<P>*)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<P>::CMD:
-                segCmd = (macho_segment_command<P>*)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<P>* const sectionsStart = (macho_section<P>*)((uint8_t*)segCmd + sizeof(macho_segment_command<P>));
-                    macho_section<P>* const sectionsEnd = &sectionsStart[segCmd->nsects()];
-                    for (macho_section<P>* 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<P>* nextCmd = (macho_load_command<P>*)(((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 <typename P>
-void LinkeditOptimizer<P>::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<P>* 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: <rdar://problem/20357466> 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<P>));
-    _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 <typename P>
-void LinkeditOptimizer<P>::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 <typename P>
-void LinkeditOptimizer<P>::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 <typename P>
-void LinkeditOptimizer<P>::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 <typename P>
-void LinkeditOptimizer<P>::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 <typename P>
-void LinkeditOptimizer<P>::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 <typename P>
-void LinkeditOptimizer<P>::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 <typename P>
-void LinkeditOptimizer<P>::copyLocalSymbols(uint8_t* newLinkEditContent, SortedStringPool<P>& stringPool, uint32_t& offset, uint32_t& symbolIndex,
-                                            bool redact, std::vector<LocalSymbolInfo>& localSymbolInfos,
-                                            std::vector<macho_nlist<P>>& unmappedLocalSymbols, SortedStringPool<P>& 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<P>* const symbolTable = (macho_nlist<P>*)(&_linkeditBias[_symTabCmd->symoff()]);
-    const macho_nlist<P>* const firstExport = &symbolTable[_dynSymTabCmd->ilocalsym()];
-    const macho_nlist<P>* const lastExport  = &symbolTable[_dynSymTabCmd->ilocalsym()+_dynSymTabCmd->nlocalsym()];
-    for (const macho_nlist<P>* 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<P>* newSymbolEntry = (macho_nlist<P>*)&newLinkEditContent[offset];
-        *newSymbolEntry = *entry;
-        if ( redact ) {
-            // if removing local symbols, change __text symbols to "<redacted>" so backtraces don't have bogus names
-            if ( entry->n_sect() == 1 ) {
-                stringPool.add(symbolIndex, "<redacted>");
-                ++symbolIndex;
-                offset += sizeof(macho_nlist<P>);
-            }
-            // 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<P>);
-       }
-    }
-    _newLocalSymbolCount = symbolIndex - _newLocalSymbolsStartIndex;
-       localInfo.nlistCount = (uint32_t)unmappedLocalSymbols.size() - localInfo.nlistStartIndex;
-       localSymbolInfos.push_back(localInfo);
-}
-
-
-template <typename P>
-void LinkeditOptimizer<P>::copyExportedSymbols(uint8_t* newLinkEditContent, SortedStringPool<P>& stringPool, uint32_t& offset, uint32_t& symbolIndex)
-{
-    _newExportedSymbolsStartIndex = symbolIndex;
-    const char* strings = (char*)&_linkeditBias[_symTabCmd->stroff()];
-    const macho_nlist<P>* const symbolTable = (macho_nlist<P>*)(&_linkeditBias[_symTabCmd->symoff()]);
-    const macho_nlist<P>* const firstExport = &symbolTable[_dynSymTabCmd->iextdefsym()];
-    const macho_nlist<P>* const lastExport  = &symbolTable[_dynSymTabCmd->iextdefsym()+_dynSymTabCmd->nextdefsym()];
-    uint32_t oldSymbolIndex = _dynSymTabCmd->iextdefsym();
-    for (const macho_nlist<P>* 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<P>* newSymbolEntry = (macho_nlist<P>*)&newLinkEditContent[offset];
-        *newSymbolEntry = *entry;
-        newSymbolEntry->set_n_strx(0);
-        stringPool.add(symbolIndex, name);
-        _oldToNewSymbolIndexes[oldSymbolIndex] = symbolIndex - _newLocalSymbolsStartIndex;
-        ++symbolIndex;
-        offset += sizeof(macho_nlist<P>);
-    }
-    _newExportedSymbolCount = symbolIndex - _newExportedSymbolsStartIndex;
-}
-
-template <typename P>
-void LinkeditOptimizer<P>::copyImportedSymbols(uint8_t* newLinkEditContent, SortedStringPool<P>& stringPool, uint32_t& offset, uint32_t& symbolIndex)
-{
-    _newImportedSymbolsStartIndex = symbolIndex;
-    const char* strings = (char*)&_linkeditBias[_symTabCmd->stroff()];
-    const macho_nlist<P>* const symbolTable = (macho_nlist<P>*)(&_linkeditBias[_symTabCmd->symoff()]);
-    const macho_nlist<P>* const firstImport = &symbolTable[_dynSymTabCmd->iundefsym()];
-    const macho_nlist<P>* const lastImport  = &symbolTable[_dynSymTabCmd->iundefsym()+_dynSymTabCmd->nundefsym()];
-    uint32_t oldSymbolIndex = _dynSymTabCmd->iundefsym();
-    for (const macho_nlist<P>* entry = firstImport; entry < lastImport; ++entry, ++oldSymbolIndex) {
-        if ( (entry->n_type() & N_TYPE) != N_UNDF)
-            continue;
-        const char* name = &strings[entry->n_strx()];
-        macho_nlist<P>* newSymbolEntry = (macho_nlist<P>*)&newLinkEditContent[offset];
-        *newSymbolEntry = *entry;
-        newSymbolEntry->set_n_strx(0);
-        stringPool.add(symbolIndex, name);
-        _oldToNewSymbolIndexes[oldSymbolIndex] = symbolIndex - _newLocalSymbolsStartIndex;
-        ++symbolIndex;
-        offset += sizeof(macho_nlist<P>);
-    }
-    _newImportedSymbolCount = symbolIndex - _newImportedSymbolsStartIndex;
-}
-
-template <typename P>
-void LinkeditOptimizer<P>::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 <typename P>
-uint64_t mergeLinkedits(SharedCache& cache, bool dontMapLocalSymbols, bool addAcceleratorTables, std::vector<LinkeditOptimizer<P>*>& optimizers)
-{
-    // allocate space for new linkedit data
-    uint32_t linkeditStartOffset = 0xFFFFFFFF;
-    uint32_t linkeditEndOffset = 0;
-    uint64_t linkeditStartAddr = 0;
-    for (LinkeditOptimizer<P>* 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<P> stringPool;
-    uint32_t offset = 0;
-
-    verboseLog("Merged LINKEDIT:");
-
-    // copy weak binding info
-    uint32_t startWeakBindInfosOffset = offset;
-    for (LinkeditOptimizer<P>* op : optimizers) {
-        op->copyWeakBindingInfo(newLinkEdit, offset);
-    }
-    verboseLog("  weak bindings size:      %5uKB", (offset-startWeakBindInfosOffset)/1024);
-
-    // copy export info
-    uint32_t startExportInfosOffset = offset;
-    for (LinkeditOptimizer<P>* 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<P>* op : optimizers) {
-            op->copyBindingInfo(newLinkEdit, offset);
-        }
-        verboseLog("  bindings size:           %5uKB", (offset-startBindingsInfosOffset)/1024);
-
-       // copy lazy binding info
-        uint32_t startLazyBindingsInfosOffset = offset;
-        for (LinkeditOptimizer<P>* op : optimizers) {
-            op->copyLazyBindingInfo(newLinkEdit, offset);
-        }
-        verboseLog("  lazy bindings size:      %5uKB", (offset-startLazyBindingsInfosOffset)/1024);
-    }
-
-    // copy symbol table entries
-    std::vector<macho_nlist<P>> unmappedLocalSymbols;
-    if ( dontMapLocalSymbols )
-        unmappedLocalSymbols.reserve(0x01000000);
-    std::vector<LocalSymbolInfo> localSymbolInfos;
-        localSymbolInfos.reserve(optimizers.size());
-    SortedStringPool<P> localSymbolsStringPool;
-    uint32_t symbolIndex = 0;
-    const uint32_t sharedSymbolTableStartOffset = offset;
-    uint32_t sharedSymbolTableExportsCount = 0;
-    uint32_t sharedSymbolTableImportsCount = 0;
-    for (LinkeditOptimizer<P>* 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<P>* 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<P>* op : optimizers) {
-        op->copyDataInCode(newLinkEdit, offset);
-    }
-    verboseLog("  data in code size:       %5uB", offset-startDataInCodeOffset);
-
-    // copy indirect symbol tables
-    for (LinkeditOptimizer<P>* 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<P>*)&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<P> 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<E>* infoHeader = (dyldCacheLocalSymbolsInfo<E>*)((uint8_t*)cache.buffer().get()+localSymbolsOffset);
-        const uint32_t entriesOffset = sizeof(dyldCacheLocalSymbolsInfo<E>);
-        const uint32_t entriesCount  = (uint32_t)localSymbolInfos.size();
-        const uint32_t nlistOffset   = (uint32_t)align(entriesOffset + entriesCount * sizeof(dyldCacheLocalSymbolEntry<E>), 4); // 16-byte align start
-        const uint32_t nlistCount    = (uint32_t)unmappedLocalSymbols.size();
-        const uint32_t stringsOffset = nlistOffset + nlistCount * sizeof(macho_nlist<P>);
-        // copy info for each dylib
-        dyldCacheLocalSymbolEntry<E>* entries = (dyldCacheLocalSymbolEntry<E>*)(((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<P>* newLocalsSymbolTable = (macho_nlist<P>*)(((uint8_t*)infoHeader)+nlistOffset);
-        ::memcpy(newLocalsSymbolTable, &unmappedLocalSymbols[0], nlistCount*sizeof(macho_nlist<P>));
-        // 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<P>* op : optimizers) {
-        op->updateLoadCommands(linkeditStartOffset, linkeditStartAddr, newLinkeditEnd-linkeditStartOffset,
-                               sharedSymbolTableStartOffset, sharedSymbolTableCount,
-                               sharedSymbolStringsOffset, sharedSymbolStringsSize);
-    }
-
-    return newFileSize;
-}
-
-} // anonymous namespace
-
-template <typename P>
-void SharedCache::optimizeLinkedit(bool dontMapLocalSymbols, bool addAcceleratorTables, const std::vector<uint64_t>& branchPoolOffsets)
-{
-    // construct a LinkeditOptimizer for each image
-    std::vector<LinkeditOptimizer<P>*> optimizers;
-    forEachImage([&](const void* mh, const char*, time_t, ino_t, const std::vector<MachOProxySegment>&) {
-        optimizers.push_back(new LinkeditOptimizer<P>(_buffer.get(), (macho_header<P>*)mh));
-    });
-    // add optimizer for each branch pool
-    for (uint64_t poolOffset : branchPoolOffsets) {
-        macho_header<P>* mh =  (macho_header<P>*)((char*)_buffer.get() + poolOffset);
-        optimizers.push_back(new LinkeditOptimizer<P>(_buffer.get(), mh));
-    }
-
-    // merge linkedit info
-    _fileSize = mergeLinkedits(*this, dontMapLocalSymbols, addAcceleratorTables, optimizers);
-
-    // delete optimizers
-    for (LinkeditOptimizer<P>* op : optimizers)
-        delete op;
-}
-
-void SharedCache::optimizeLinkedit(bool dontMapLocalSymbols, bool addAcceleratorTables, const std::vector<uint64_t>& branchPoolOffsets)
-{
-    switch ( _arch.arch ) {
-        case CPU_TYPE_ARM:
-        case CPU_TYPE_I386:
-            optimizeLinkedit<Pointer32<LittleEndian>>(dontMapLocalSymbols, addAcceleratorTables, branchPoolOffsets);
-            break;
-        case CPU_TYPE_X86_64:
-        case CPU_TYPE_ARM64:
-            optimizeLinkedit<Pointer64<LittleEndian>>(dontMapLocalSymbols, addAcceleratorTables, branchPoolOffsets);
-            break;
-        default:
-            terminate("unsupported arch 0x%08X", _arch.arch);
-    }
-}
-
-
-
diff --git a/interlinked-dylibs/OptimizerObjC.cpp b/interlinked-dylibs/OptimizerObjC.cpp
deleted file mode 100644 (file)
index 7b1eef3..0000000
+++ /dev/null
@@ -1,826 +0,0 @@
-/* -*- 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 <dirent.h>
-#include <sys/errno.h>
-#include <sys/fcntl.h>
-#include <mach-o/loader.h>
-#include <mach-o/fat.h>
-#include <assert.h>
-
-
-// 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<Info> _regions;
-};
-
-
-// Access a section containing a list of pointers
-template <typename P, typename T>
-class PointerSection 
-{
-    typedef typename P::uint_t   pint_t;
-public:
-    PointerSection(ContentAccessor* cache, const macho_header<P>* 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<macho_section<P>*>(_section)->set_size(_count * sizeof(pint_t));
-    }
-
-private:
-    ContentAccessor* const         _cache;
-    const macho_section<P>* const  _section;
-    pint_t* const                  _base;
-    pint_t const                   _count;
-};
-
-
-// Access a section containing an array of structures
-template <typename P, typename T>
-class ArraySection 
-{
-public:
-    ArraySection(ContentAccessor* cache, const macho_header<P>* 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<P>* 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 <typename P>
-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 <typename P>
-class ClassListBuilder
-{
-private:
-    objc_opt::string_map    _classNames;
-    objc_opt::class_map     _classes;
-    size_t                  _count = 0;
-    HeaderInfoOptimizer<P, objc_header_info_ro_t<P>>& _hInfos;
-
-public:
-
-    ClassListBuilder(HeaderInfoOptimizer<P, objc_header_info_ro_t<P>>& hinfos) : _hInfos(hinfos) { }
-
-    void visitClass(ContentAccessor* cache,
-                    const macho_header<P>* header,
-                    objc_class_t<P>* 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<uint64_t, uint64_t>(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 <typename P>
-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<P, ProtocolOptimizer<P>>;
-
-    pint_t visitProtocolReference(ContentAccessor* cache, pint_t oldValue)
-    {
-        objc_protocol_t<P>* proto = (objc_protocol_t<P>*)
-            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<P>* header)
-    {
-        PointerSection<P, objc_protocol_t<P> *>
-            protocols(cache, header, "__DATA", "__objc_protolist");
-        
-        for (pint_t i = 0; i < protocols.count(); i++) {
-            objc_protocol_t<P> *proto = protocols.get(i);
-
-            const char *name = proto->getName(cache);
-            if (_protocolNames.count(name) == 0) {
-                if (proto->getSize() > sizeof(objc_protocol_t<P>)) {
-                    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<void*>& 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<P>);
-        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<P>* oldProto = (objc_protocol_t<P>*)
-                cache->contentForVMAddr(iter->second);
-
-            // Create a new protocol object.
-            objc_protocol_t<P>* proto = (objc_protocol_t<P>*)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<P>));
-                // 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<P>* header)
-    {
-        ProtocolReferenceWalker<P, ProtocolOptimizer<P>> 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 <typename P>
-void optimizeObjC(SharedCache& cache, std::vector<void*>& 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<P> *optROSection = nullptr;
-    const macho_section<P> *optRWSection = nullptr;
-    const macho_section<P> *optPointerListSection = nullptr;
-    std::vector<const macho_header<P>*> objcDylibs;
-    cache.forEachImage([&](const void* machHeader, const char* installName,
-        time_t, ino_t, const std::vector<MachOProxySegment>& segments) {
-        const macho_header<P>* mh = (const macho_header<P>*)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<pint_t>)) {
-        warning("libobjc's pointer list section is too small (metadata not optimized)");
-        return;
-    }
-    const objc_opt::objc_opt_pointerlist_tt<pint_t> *optPointerList = (const objc_opt::objc_opt_pointerlist_tt<pint_t> *)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<const macho_header<P>*> addressSortedDylibs = objcDylibs;
-    std::sort(addressSortedDylibs.begin(), addressSortedDylibs.end(), [](const macho_header<P>* lmh, const macho_header<P>* 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<P, objc_header_info_ro_t<P>> hinfoROOptimizer;
-    const char* err = hinfoROOptimizer.init((uint32_t)objcDylibs.size(), optROData, optRORemaining);
-    if (err) {
-        warning("%s", err);
-        return;
-    }
-    else {
-        for (const macho_header<P>* 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<P, objc_header_info_rw_t<P>> hinfoRWOptimizer;
-    err = hinfoRWOptimizer.init((uint32_t)objcDylibs.size(), optRWData, optRWRemaining);
-    if (err) {
-        warning("%s", err);
-        return;
-    }
-    else {
-        for (const macho_header<P>* 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<P> uniq(&cacheAccessor);
-    std::vector<const macho_header<P>*> sizeSortedDylibs = objcDylibs;
-    std::sort(sizeSortedDylibs.begin(), sizeSortedDylibs.end(),  [](const macho_header<P>* lmh, const macho_header<P>* rmh) -> bool {
-        const macho_section<P>* lSection = lmh->getSection("__TEXT", "__objc_methname");
-        const macho_section<P>* rSection = rmh->getSection("__TEXT", "__objc_methname");
-        uint64_t lSelectorSize = (lSection ? lSection->size() : 0);
-        uint64_t rSelectorSize = (rSection ? rSection->size() : 0);
-        return lSelectorSize > rSelectorSize;
-    });
-
-    SelectorOptimizer<P, ObjCSelectorUniquer<P> > selOptimizer(uniq);
-    for (const macho_header<P>* mh : sizeSortedDylibs) {
-        LegacySelectorUpdater<P, ObjCSelectorUniquer<P>>::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<P> 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<P> classes(hinfoROOptimizer);
-    ClassWalker<P, ClassListBuilder<P>> classWalker(classes);
-    for (const macho_header<P>* 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<P> methodSorter;
-    for (const macho_header<P>* 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<P> protocolOptimizer;
-    for (const macho_header<P>* 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<P>* 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<P> ivarOffsetOptimizer;
-    for (const macho_header<P>* 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<P>* mh : sizeSortedDylibs) {
-        const macho_section<P>* imageInfoSection = mh->getSection("__DATA", "__objc_imageinfo");
-        if (!imageInfoSection) {
-            imageInfoSection = mh->getSection("__OBJC", "__image_info");
-        }
-        if (imageInfoSection) {
-            objc_image_info<P>* info = (objc_image_info<P>*)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<Pointer32<LittleEndian>>(*this, _pointersForASLR, forProduction);
-            break;
-        case CPU_TYPE_X86_64:
-        case CPU_TYPE_ARM64:
-            ::optimizeObjC<Pointer64<LittleEndian>>(*this, _pointersForASLR, forProduction);
-            break;
-        default:
-            terminate("unsupported arch 0x%08X", _arch.arch);
-    }
-}
diff --git a/interlinked-dylibs/SharedCache.cpp b/interlinked-dylibs/SharedCache.cpp
deleted file mode 100644 (file)
index 55ee8ae..0000000
+++ /dev/null
@@ -1,1625 +0,0 @@
-/* -*- 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 <dirent.h>
-#include <sys/errno.h>
-#include <sys/fcntl.h>
-#include <sys/param.h>
-#include <mach-o/loader.h>
-#include <mach-o/fat.h>
-#include <assert.h>
-#include <unistd.h>
-#include <CommonCrypto/CommonDigest.h>
-#include <CommonCrypto/CommonDigestSPI.h>
-
-#include <fstream>
-#include <iostream>
-#include <string>
-#include <map>
-#include <set>
-#include <array>
-#include <vector>
-#include <unordered_map>
-#include <unordered_set>
-
-#include "MachOProxy.h"
-
-#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; // <rdar://problem/18564532> 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; // <rdar://problem/18564532> 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<const char* const, const char* const, const ArchPair> 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.configuration(configuration).architecture(architecture))
-    , _buffer(nullptr)
-    , _fileSize(0)
-    , _vmSize(0)
-    , _aliasCount(0)
-    , _slideInfoFileOffset(0)
-    , _slideInfoBufferSize(0)
-{
-    auto maxCacheVMSize = sharedRegionRegionSize(_arch);
-
-    for (auto& includedIdentifier : _archManifest.results.dylibs) {
-        if (includedIdentifier.second.included) {
-            //assert(manifest.dylibs.count(includedDylib.first) > 0);
-            //assert(manifest.dylibs.find(includedDylib.first)->second.proxies.count(architecture) > 0);
-            MachOProxy* proxy = MachOProxy::forIdentifier(includedIdentifier.first, architecture);
-            assert(proxy != nullptr);
-            assert(proxy->isDylib());
-            _dylibs.push_back(proxy);
-        }
-    }
-
-    // <rdar://problem/21317611> 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<std::string, uint32_t> loadOrderFile(const std::string& orderFile) {
-    std::unordered_map<std::string, uint32_t> 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<std::string, uint32_t> 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<void>(calloc(_fileSize, 1), free);
-    writeCacheHeader();
-    writeCacheSegments();
-    rebaseAll();
-    bindAll();
-}
-
-template <typename P>
-void SharedCache::buildForDevelopment(const std::string& cachePath) {
-    typedef typename P::E   E;
-    std::vector<uint64_t> emptyBranchPoolOffsets;
-    buildUnoptimizedCache();
-    optimizeObjC(false/*not production*/);
-    if (_manifest.platform() == "osx") {
-        optimizeLinkedit(false, false, emptyBranchPoolOffsets);
-    } else {
-        optimizeLinkedit(true, false, emptyBranchPoolOffsets);
-    }
-    writeSlideInfoV2();
-
-    dyldCacheHeader<E>* header = (dyldCacheHeader<E>*)_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 <typename P>
-void SharedCache::buildForProduction(const std::string& cachePath) {
-    typedef typename P::E   E;
-    buildUnoptimizedCache();
-    optimizeObjC(true/*production*/);
-    uint64_t cacheStartAddress = sharedRegionStartExecutableAddress(_arch);
-
-    dyldCacheHeader<E>* header = (dyldCacheHeader<E>*)_buffer.get();
-    header->set_cacheType(kDyldSharedCacheTypeProduction);
-
-    // build vector of branch pool addresss
-    std::vector<uint64_t> branchPoolStartAddrs;
-    std::vector<uint64_t> 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<uint64_t> regionStartAddresses;
-    std::vector<uint64_t> regionSizes;
-    std::vector<uint64_t> 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<const void*> seenHeaders;
-    forEachImage([&](const void* machHeader, const char* installName, time_t mtime,
-        ino_t inode, const std::vector<MachOProxySegment>& segments) {
-        if ( !seenHeaders.count(machHeader) ) {
-            seenHeaders.insert(machHeader);
-
-            fprintf(fmap, "%s\n", installName);
-            for (const 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];
-                    }
-                }
-                fprintf(fmap, "\t%16s 0x%08llX -> 0x%08llX\n", seg.name.c_str(), vmAddr, vmAddr+seg.size);
-            }
-        }
-    });
-
-    ::fclose(fmap);
-    return true;
-}
-
-template <typename P>
-std::vector<MachOProxySegment> getSegments(const void* cacheBuffer, const void* machHeader)
-{
-    std::vector<MachOProxySegment> result;
-    macho_header<P>* mh = (macho_header<P>*)machHeader;
-    const uint32_t cmd_count = mh->ncmds();
-    const macho_load_command<P>* cmd = (macho_load_command<P>*)((uint8_t*)mh + sizeof(macho_header<P>));
-    for (uint32_t i = 0; i < cmd_count; ++i) {
-        if ( cmd->cmd() != macho_segment_command<P>::CMD )
-            continue;
-        macho_segment_command<P>* segCmd = (macho_segment_command<P>*)cmd;
-        MachOProxySegment seg;
-        seg.name        = segCmd->segname();
-        seg.name        = segCmd->segname();
-        seg.size        = segCmd->vmsize();
-        seg.vmaddr        = segCmd->vmaddr();
-        seg.diskSize    = (uint32_t)segCmd->filesize();
-        seg.fileOffset  = (uint32_t)segCmd->fileoff();
-        seg.protection  = segCmd->initprot();
-        // HACK until lldb fixed in <rdar://problem/20357466>
-        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<P>* const sectionsStart = (macho_section<P>*)((uint8_t*)segCmd + sizeof(macho_segment_command<P>));
-            const macho_section<P>* const sectionsEnd = &sectionsStart[segCmd->nsects()];
-            for (const macho_section<P>*  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<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
-    }
-    return result;
-}
-
-template <typename P>
-void SharedCache::forEachImage(DylibHandler handler)
-{
-#if NEW_CACHE_FILE_FORMAT
-    terminate("forEachImage() not implemented");
-#else
-    typedef typename P::E   E;
-    const dyldCacheHeader<E>*      header   = (dyldCacheHeader<E>*)_buffer.get();
-    const dyldCacheImageInfo<E>*   dylibs   = (dyldCacheImageInfo<E>*)((char*)_buffer.get() + header->imagesOffset());
-    const dyldCacheFileMapping<E>* mappings = (dyldCacheFileMapping<E>*)((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<P>(_buffer.get(), mh));
-    }
-#endif
-}
-
-
-template <typename P>
-void SharedCache::recomputeCacheUUID(void)
-{
-    uint8_t* uuidLoc = nullptr;
-#if NEW_CACHE_FILE_FORMAT
-    const macho_header<P>* mh = (macho_header<P>*)cacheBuffer;
-    const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)mh + sizeof(macho_header<P>));
-    const uint32_t cmd_count = mh->ncmds();
-    const macho_load_command<P>* cmd = cmds;
-    for (uint32_t i = 0; i < cmd_count; ++i) {
-        if ( cmd->cmd() == LC_UUID ) {
-            const macho_uuid_command<P>* uuidCmd = (macho_uuid_command<P>*)cmd;
-            uuidLoc = const_cast<uint8_t*>(uuidCmd->uuid());
-            break;
-        }
-        cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
-    }
-#else
-    dyldCacheHeader<P>* header = (dyldCacheHeader<P>*)_buffer.get();
-    uuidLoc = const_cast<uint8_t*>(header->uuid());
-#endif
-
-    // Clear existing UUID, then MD5 whole cache buffer.
-    bzero(uuidLoc, 16);
-    CC_MD5(_buffer.get(), (unsigned)_fileSize, uuidLoc);
-    // <rdar://problem/6723729> 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 <typename P>
-void SharedCache::setLinkeditsMappingEndFileOffset(uint64_t newFileSize)
-{
-#if NEW_CACHE_FILE_FORMAT
-    terminate("setLinkeditsMappingEndFileOffset() not implemented");
-#else
-    typedef typename P::E   E;
-    dyldCacheHeader<E>*      header   = (dyldCacheHeader<E>*)_buffer.get();
-    dyldCacheFileMapping<E>* mappings = (dyldCacheFileMapping<E>*)((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 <typename P>
-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<E>* header = (dyldCacheHeader<E>*)_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 <typename P>
-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<E>* header = (dyldCacheHeader<E>*)_buffer.get();
-    header->set_accelerateInfoAddr(accelInfoAddr);
-    header->set_accelerateInfoSize(accelInfoSize);
-#endif
-}
-
-template <typename P>
-void SharedCache::forEachRegion(RegionHandler handler)
-{
-#if NEW_CACHE_FILE_FORMAT
-    const macho_header<P>* mh = (macho_header<P>*)cacheBuffer;
-    const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)mh + sizeof(macho_header<P>));
-    const uint32_t cmd_count = mh->ncmds();
-    const macho_load_command<P>* cmd = cmds;
-    for (uint32_t i = 0; i < cmd_count; ++i) {
-        if ( cmd->cmd() == macho_segment_command<P>::CMD ) {
-            const macho_segment_command<P>* segCmd = (macho_segment_command<P>*)cmd;
-            handler((char*)cacheBuffer + segCmd->fileoff(), segCmd->vmaddr(), segCmd->vmsize(), segCmd->initprot());
-        }
-        cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
-    }
-#else
-    typedef typename P::E   E;
-    const dyldCacheHeader<E>*      header   = (dyldCacheHeader<E>*)_buffer.get();
-    const dyldCacheFileMapping<E>* mappings = (dyldCacheFileMapping<E>*)((char*)_buffer.get() + header->mappingOffset());
-    const dyldCacheFileMapping<E>* mappingsEnd = &mappings[header->mappingCount()];
-    for (const dyldCacheFileMapping<E>* m=mappings; m < mappingsEnd; ++m) {
-        handler((char*)_buffer.get() + m->file_offset(), m->address(), m->size(), m->init_prot());
-    }
-#endif
-}
-
-std::shared_ptr<void> 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<const SegmentInfo*, std::string> dataDirtySegPaths;
-
-    // co-locate similar __DATA* segments
-    std::vector<SegmentInfo*> dataSegs;
-    std::vector<SegmentInfo*> dataConstSegs;
-    std::vector<SegmentInfo*> 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;
-
-    // FIXME: Confirm these numbers for all platform/arch combos
-    // assume LINKEDIT optimzation reduces LINKEDITs to %40 of original size
-    if (_manifest.platform() == "osx") {
-        _vmSize = _readOnlyRegion.address + (_readOnlyRegion.size * 9 / 10) - _textRegion.address;
-    } else {
-        _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 <typename P>
-bool SharedCache::makeRebaseChain(uint8_t* pageContent, uint16_t lastLocationOffset, uint16_t offset, const dyldCacheSlideInfo2<typename P::E>* 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 <typename P>
-void SharedCache::addPageStarts(uint8_t* pageContent, const bool bitmap[], const dyldCacheSlideInfo2<typename P::E>* info,
-                                std::vector<uint16_t>& pageStarts, std::vector<uint16_t>& 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<P>(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 <typename P>
-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<LittleEndian>* header = (dyldCacheHeader<LittleEndian>*)_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 <rdar://24941083> 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<E>* info = (dyldCacheSlideInfo2<E>*)((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<uint16_t> pageStarts;
-    std::vector<uint16_t> pageExtras;
-    pageStarts.reserve(pageCount);
-    uint8_t* pageContent = dataStart;;
-    const bool* bitmapForPage = bitmap;
-    for (unsigned i=0; i < pageCount; ++i) {
-        //warning("page[%d]", i);
-        addPageStarts<P>(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<E>));
-    info->set_page_starts_count((unsigned)pageStarts.size());
-    info->set_page_extras_offset((unsigned)(sizeof(dyldCacheSlideInfo2<E>)+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<E>* header = (dyldCacheHeader<E>*)_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<LittleEndian>();
-            }
-            else {
-                writeSlideInfoV2<Pointer32<LittleEndian>>(0xE0000000, ARM_SHARED_REGION_START);
-            }
-            break;
-        case CPU_TYPE_I386:
-            writeSlideInfoV2<Pointer32<LittleEndian>>(0xE0000000, 0x90000000);
-            break;
-        case CPU_TYPE_X86_64:
-            writeSlideInfoV2<Pointer64<LittleEndian>>(0xFFFF000000000000, 0);
-            break;
-        case CPU_TYPE_ARM64:
-            writeSlideInfoV2<Pointer64<LittleEndian>>(0x00FFFF0000000000, 0);
-            break;
-        default:
-            warning("unsupported arch 0x%08X", _arch.arch);
-            return;
-    }
-}
-
-
-
-template <typename E>
-void SharedCache::writeSlideInfo(void)
-{
-    // i386 cache does not support sliding because stubs use absolute addressing (text relocs)
-    if (_arch.arch == CPU_TYPE_I386 ) {
-        dyldCacheHeader<E>* header = (dyldCacheHeader<E>*)_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<E>* slideInfo = (dyldCacheSlideInfo<E>*)((uint8_t*)_buffer.get() + _slideInfoFileOffset);
-    slideInfo->set_version(1);
-    slideInfo->set_toc_offset(sizeof(dyldCacheSlideInfo<E>));
-    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<E>* header = (dyldCacheHeader<E>*)_buffer.get();
-    header->set_slideInfoSize(slideInfoPageSize);
-#endif
-}
-
-template <typename P>
-void SharedCache::writeCacheHeader(void)
-{
-#if NEW_CACHE_FILE_FORMAT
-    macho_header<P>* mh = (macho_header<P>*)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<P>);
-
-    // write LC_SEGMENT for each region
-    macho_segment_command<P>* rxSegCmd = (macho_segment_command<P>*)cmd;
-    rxSegCmd->set_cmd(macho_segment_command<P>::CMD);
-    rxSegCmd->set_cmdsize(sizeof(macho_segment_command<P>));
-    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<P>* rwSegCmd = (macho_segment_command<P>*)cmd;
-    rwSegCmd->set_cmd(macho_segment_command<P>::CMD);
-    rwSegCmd->set_cmdsize(sizeof(macho_segment_command<P>));
-    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<P>* roSegCmd = (macho_segment_command<P>*)cmd;
-    roSegCmd->set_cmd(macho_segment_command<P>::CMD);
-    roSegCmd->set_cmdsize(sizeof(macho_segment_command<P>));
-    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<P>* dylibIdCmd = (macho_dylib_command<P>*)cmd;
-    const char* installName = "/System/Library/Frameworks/OS.framework/OS"; // FIXME
-    uint32_t sz = (uint32_t)align(sizeof(macho_dylib_command<P>) + 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<P>)], installName);
-    mh->set_ncmds(mh->ncmds()+1);
-    mh->set_sizeofcmds(mh->sizeofcmds()+sz);
-    cmd += dylibIdCmd->cmdsize();
-
-    // Add LC_UUID
-    macho_uuid_command<P>* uuidCmd = (macho_uuid_command<P>*)cmd;
-    uint8_t zeros[16];
-    bzero(zeros, 16);
-    uuidCmd->set_cmd(LC_UUID);
-    uuidCmd->set_cmdsize(sizeof(macho_uuid_command<P>));
-    uuidCmd->set_uuid(zeros);
-    cmd += uuidCmd->cmdsize();
-
-    // Build dylib trie
-    std::vector<mach_o::trie::Entry> 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<uint8_t> 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<P>* codeSigCmd = (macho_linkedit_data_command<P>*)cmd;
-    codeSigCmd->set_cmd(LC_CODE_SIGNATURE);
-    codeSigCmd->set_cmdsize(sizeof(macho_linkedit_data_command<P>));
-    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<E>* header = (dyldCacheHeader<E>*)_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<E>));
-    header->set_mappingCount(3);
-    header->set_imagesOffset((uint32_t)(header->mappingOffset() + 3*sizeof(dyldCacheFileMapping<E>) + 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<E>));
-    header->set_branchPoolsCount((uint32_t)_branchPoolStarts.size());
-    header->set_imagesTextOffset(0);
-    header->set_imagesTextCount(_dylibs.size());
-
-    // fill in mappings
-    dyldCacheFileMapping<E>* mappings = (dyldCacheFileMapping<E>*)&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<E>* images = (dyldCacheImageInfo<E>*)&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<E>* textImages = (dyldCacheImageTextInfo<E>*)&buffer[header->imagesTextOffset()];
-    uint32_t stringOffset = offset + (uint32_t)(sizeof(dyldCacheImageTextInfo<E>) * _dylibs.size());
-
-    // write text image array and image names pool at same time
-    for (auto& dylib : _dylibs) {
-        textImages->set_uuid(dylib->uuid.get());
-        textImages->set_loadAddress(_segmentMap[dylib][0].address);
-        textImages->set_textSegmentSize((uint32_t)_segmentMap[dylib].front().cacheSegSize);
-        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<uint64_t> segNewStartAddresses;
-    std::vector<uint64_t> segCacheFileOffsets;
-    std::vector<uint64_t> 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)
-{
-    bindAllImagesInCache(_dylibs, _segmentMap, _pointersForASLR);
-}
-
-void SharedCache::writeCacheSegments(void)
-{
-    uint8_t* cacheBytes = (uint8_t*)_buffer.get();
-    for (auto& dylib : _dylibs) {
-        const uint8_t* srcDylib = dylib->getBuffer();
-
-        for (auto& seg : _segmentMap[dylib]) {
-            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[seg.base->fileOffset], 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<CS_SuperBlob*>(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<CS_Scatter*>((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<LittleEndian>* header = (dyldCacheHeader<LittleEndian>*)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<Pointer32<LittleEndian>>(__VA_ARGS__);       \
-            break;                                              \
-        case CPU_TYPE_X86_64:                                   \
-        case CPU_TYPE_ARM64:                                    \
-            method<Pointer64<LittleEndian>>(__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/interlinked-dylibs/Trie.hpp b/interlinked-dylibs/Trie.hpp
deleted file mode 100644 (file)
index d77a21a..0000000
+++ /dev/null
@@ -1,498 +0,0 @@
-/* -*- 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 <typename V>
- 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<std::bidirectional_iterator_tag, const Entry>;
-
- const_iterator begin() const;
- const_iterator end() const;
-
- Trie(void);
- Trie(const uint8_t* start, const uint8_t* end);
- Trie(const std::vector<Entry>& entries);
-
- void emit(std::vector<uint8_t>& output);
- */
-
-
-#ifndef __TRIE__
-#define __TRIE__
-#define TRIE_DEBUG (0)
-
-#include <algorithm>
-#include <vector>
-#include <memory>
-#include <string>
-#include <map>
-#include <iterator>
-
-#include "MachOFileAbstraction.hpp"
-
-#if __cplusplus <= 201103L
-namespace std {
-       template<typename T, typename... Args>
-       std::unique_ptr<T> make_unique(Args&&... args)
-       {
-               return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
-       }
-}
-#endif
-
-namespace TrieUtils {
-
-static void append_uleb128(uint64_t value, std::vector<uint8_t>& 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<uint8_t>& 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 <typename V>
-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<Entry>& entries) : count(0), nodeCount(1) {
-               // make nodes for all exported symbols
-               for (auto& entry : entries) {
-                       addEntry(entry);
-               }
-       }
-
-       void emit(std::vector<uint8_t>& output) {
-               // create vector of nodes
-               std::vector<Node*> 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<Entry>& output)
-       {
-               // empty trie has no entries
-               if ( start == end )
-                       return false;
-               char cummulativeString[32768];
-               std::vector<EntryWithOffset> 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<std::string,std::unique_ptr<Node> > 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<uint8_t>& 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<Node> nodeC;
-                                       std::swap(nodeC, entry.second);
-                                       currentNode->fChildren.erase(entry.first);
-
-                                       //Build the new node and insert it
-                                       std::unique_ptr<Node> nodeB = std::make_unique<Node>();
-                                       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<Node>(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<EntryWithOffset>& 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<Node*>& 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<uint8_t>& 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<ExportInfo> 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<uint8_t>& 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<DylibIndex> DylibIndexTrie;
-
-
-#endif // __TRIE__
-
-
diff --git a/interlinked-dylibs/dyld_shared_cache_builder.mm b/interlinked-dylibs/dyld_shared_cache_builder.mm
deleted file mode 100644 (file)
index 0ce8429..0000000
+++ /dev/null
@@ -1,383 +0,0 @@
-/* -*- 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 <dispatch/dispatch.h>
-#include <Security/Security.h>
-#include <Security/SecCodeSigner.h>
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/mman.h>
-#include <sys/resource.h>
-#include <mach/mach.h>
-#include <mach/mach_time.h>
-#include <limits.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <math.h>
-#include <fcntl.h>
-#include <dlfcn.h>
-#include <signal.h>
-#include <errno.h>
-#include <sys/uio.h>
-#include <unistd.h>
-#include <sys/param.h>
-#include <sys/sysctl.h>
-#include <sys/resource.h>
-#include <dirent.h>
-#include <libgen.h>
-#include <pthread.h>
-#include <fts.h>
-
-#include <vector>
-#include <array>
-#include <set>
-#include <map>
-#include <algorithm>
-
-#include <spawn.h>
-
-#include <Bom/Bom.h>
-
-#include "mega-dylib-utils.h"
-
-#include "MultiCacheBuilder.h"
-
-#include "MachOProxy.h"
-#include "Manifest.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<std::string> &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<std::string> &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::string realPath(const std::string& path) {
-    char resolvedPath[PATH_MAX];
-    if (realpath( path.c_str(), &resolvedPath[0]) != nullptr)  {
-        return resolvedPath;
-    } else {
-        return "";
-    }
-}
-
-std::set<std::string> 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<std::string>    roots;
-               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 = realPath(argv[++i]);
-                                       processRoot(root, roots);
-                               } else if (strcmp(arg, "-copy_roots") == 0) {
-                                       copyRoots = true;
-                               } else if (strcmp(arg, "-dylib_cache") == 0) {
-                                       dylibCacheDir = realPath(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 = realPath(argv[++i]);
-                               } else if (strcmp(arg, "-release") == 0) {
-                                       release = argv[++i];
-                               } else if (strcmp(arg, "-results") == 0) {
-                                       resultPath = realPath(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) {
-            manifest.forEachConfiguration([](const std::string& configName) {
-                printf("%s\n", configName.c_str());
-            });
-            exit(0);
-               }
-
-        if (!manifest.filterForConfig(configuration)) {
-            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.calculateClosure(false);
-        manifest.checkLinks();
-
-        // FIXME: Plumb through no_development
-
-        std::shared_ptr<MultiCacheBuilder> builder = std::make_shared<MultiCacheBuilder>(manifest, false, false, true);
-        builder->buildCaches(dstRoot);
-        writeRootList(dstRoot, roots);
-
-        if (copyRoots) {
-            manifest.forEachConfiguration([&manifest](const std::string& configName) {
-                for (auto& arch : manifest.configuration(configName).architectures) {
-                    for (auto& dylib : arch.second.results.dylibs) {
-                        if (dylib.second.included) {
-                            MachOProxy* proxy = MachOProxy::forIdentifier(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/interlinked-dylibs/mega-dylib-utils.h b/interlinked-dylibs/mega-dylib-utils.h
deleted file mode 100644 (file)
index 16fc4ab..0000000
+++ /dev/null
@@ -1,316 +0,0 @@
-/* -*- 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 <sys/types.h>
-#include <sys/stat.h>
-#include <sys/mman.h>
-#include <limits.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <unistd.h>
-#include <stdlib.h>
-
-#include <dispatch/dispatch.h>
-
-#include <memory>
-#include <map>
-#include <set>
-#include <array>
-#include <vector>
-#include <string>
-#include <algorithm>
-#include <unordered_set>
-#include <unordered_map>
-
-#ifndef UUID
-#include <uuid/uuid.h>
-
-struct UUID {
-    UUID() {}
-    UUID(const UUID& other) { uuid_copy(&_bytes[0], &other._bytes[0]); }
-    UUID(const uuid_t other_uuid) { uuid_copy(&_bytes[0], other_uuid); }
-    bool operator<(const UUID& other) const { return uuid_compare(&_bytes[0], &other._bytes[0]) < 0; }
-    bool operator==(const UUID& other) const { return uuid_compare(&_bytes[0], &other._bytes[0]) == 0; }
-    bool operator!=(const UUID& other) const { return !(*this == other); }
-
-    size_t               hash() const {
-        size_t retval = 0;
-        for (auto i = 0; i < 16/sizeof(size_t); ++i) {
-            retval ^= ((size_t *)(&_bytes[0]))[i];
-        }
-        return retval;
-    }
-    const unsigned char* get() const { return &_bytes[0]; };
-private:
-    std::array<unsigned char, 16> _bytes;
-};
-
-struct ImageIdentifier {
-    ImageIdentifier() {}
-    ImageIdentifier(const UUID &U) : _uuid(U) {}
-    size_t hash() const { return _uuid.hash(); }
-    bool operator<(const ImageIdentifier& other) const { return _uuid < other._uuid; }
-    bool operator==(const ImageIdentifier& other) const { return _uuid == other._uuid; }
-    bool operator!=(const ImageIdentifier& other) const { return !(*this == other); }
-
-private:
-    UUID _uuid;
-};
-
-namespace std {
-template <>
-struct hash<UUID> {
-    size_t operator()(const UUID& x) const
-    {
-        return x.hash();
-    }
-};
-
-template <>
-struct hash<ImageIdentifier> {
-    size_t operator()(const ImageIdentifier& x) const
-    {
-        return x.hash();
-    }
-};
-}
-
-#endif
-
-#include "CacheFileAbstraction.hpp"
-
-#include "MachOFileAbstraction.hpp"
-
-#include "Manifest.h"
-
-struct MachOProxy;
-struct MachOProxySegment;
-struct SharedCache;
-
-struct FileCache {
-    FileCache(void);
-    std::tuple<uint8_t *, struct stat, bool> cacheLoad(const std::string path);
-    void preflightCache(const std::string& path);
-    void preflightCache(const std::unordered_set<std::string> &paths);
-private:
-    std::tuple<uint8_t *, struct stat, bool> fill(const std::string& path);
-
-    std::unordered_map<std::string, std::tuple<uint8_t *, struct stat, bool>> entries;
-    dispatch_queue_t cache_queue;
-};
-
-extern FileCache fileCache;
-
-typedef std::pair<Manifest*,std::set<std::pair<std::string, std::string>>> 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 MachOProxySegment* seg)
-            : base(seg)
-            , address(0)
-            , cacheFileOffset(0)
-            , cacheSegSize(0)
-        {
-        }
-
-        const MachOProxySegment*    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<void> 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<void> buffer() const;
-
-    void buildForDevelopment(const std::string& cachePath);
-    void buildForProduction(const std::string& cachePath);
-    bool writeCacheMapFile(const std::string&  mapPath);
-
-    typedef std::function<void(const void* machHeader, const char* installName, time_t lastModTime, ino_t inode,
-        const std::vector<MachOProxySegment>& segments)>
-        DylibHandler;
-    // Calls lambda once per image in the cache
-    void forEachImage(DylibHandler handler);
-
-    typedef std::function<void(void* content, uint64_t vmAddr, uint64_t size, uint32_t permissions)> 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<uint64_t>& branchPoolStartAddrs);
-    void optimizeLinkedit(bool dontMapLocalSymbols, bool addAcceleratorTables, const std::vector<uint64_t>& 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::vector<MachOProxy*> dylibs, const std::map<const MachOProxy*, std::vector<SegmentInfo>>& segmentMap, std::vector<void*>& 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<uint64_t>& segNewStartAddresses,
-                                           const std::vector<uint64_t>& segCacheFileOffsets,
-                                           const std::vector<uint64_t>& segCacheSizes, std::vector<void*>& 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);
-    void appendCodeSignature(const std::string& suffix);
-    template <typename P> void buildForDevelopment(const std::string& cachePath);
-    template <typename P> void buildForProduction(const std::string& cachePath);
-    template <typename P> void forEachImage(DylibHandler handler);
-    template <typename P> void forEachRegion(RegionHandler handler);
-    template <typename P> void setLinkeditsMappingEndFileOffset(uint64_t newFileSize);
-    template <typename P> void setAcceleratorInfoRange(uint64_t accelInfoAddr, uint32_t accelInfoSize);
-    template <typename P> void setUnmappedLocalsRange(uint64_t localSymbolsOffset, uint32_t unmappedSize);
-    template <typename P> void recomputeCacheUUID(void);
-    template <typename P> void bypassStubs(const std::vector<uint64_t>& branchPoolStartAddrs);
-    template <typename P> void optimizeLinkedit(bool dontMapLocalSymbols, bool addAcceleratorTables, const std::vector<uint64_t>& branchPoolOffsets);
-    template <typename P> void writeCacheHeader(void);
-    template <typename E> void writeSlideInfo(void);
-    template <typename P> void writeSlideInfoV2(uint64_t deltaMask, uint64_t valueAdd);
-
-    template <typename P> void findImplicitAliases(std::shared_ptr<MachOProxy> dylib);
-    template <typename P> void addPageStarts(uint8_t* pageContent, const bool bitmap[], const dyldCacheSlideInfo2<typename P::E>* info,
-                                             std::vector<uint16_t>& pageStarts, std::vector<uint16_t>& pageExtras);
-    template <typename P> bool makeRebaseChain(uint8_t* pageContent, uint16_t lastLocationOffset, uint16_t newOffset, const dyldCacheSlideInfo2<typename P::E>* info);
-                            void findDylibAndSegment(const void* contentPtr, std::string& dylibName, std::string& segName);
-
-    ArchPair _arch;
-    std::vector<MachOProxy *> _dylibs;
-    std::shared_ptr<void> _buffer;
-    std::map<const MachOProxy*, std::vector<SegmentInfo>> _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<std::string, uint32_t>   _dataDirtySegsOrder;
-    std::vector<void*>                          _pointersForASLR;
-    std::vector<uint64_t>                       _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 <class Set1, class Set2>
-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/interlinked-dylibs/multi_dyld_shared_cache_builder.mm b/interlinked-dylibs/multi_dyld_shared_cache_builder.mm
deleted file mode 100644 (file)
index b21264f..0000000
+++ /dev/null
@@ -1,270 +0,0 @@
-/* -*- 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 <dispatch/dispatch.h>
-#include <Security/Security.h>
-#include <Security/SecCodeSigner.h>
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/mman.h>
-#include <sys/resource.h>
-#include <mach/mach.h>
-#include <mach/mach_time.h>
-#include <limits.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <math.h>
-#include <fcntl.h>
-#include <dlfcn.h>
-#include <signal.h>
-#include <errno.h>
-#include <sys/uio.h>
-#include <unistd.h>
-#include <sys/param.h>
-#include <sys/sysctl.h>
-#include <sys/resource.h>
-#include <dirent.h>
-#include <libgen.h>
-#include <pthread.h>
-
-#include <vector>
-#include <array>
-#include <set>
-#include <map>
-#include <algorithm>
-
-#include "mega-dylib-utils.h"
-
-#include "MultiCacheBuilder.h"
-#include "Manifest.h"
-#include "MachOProxy.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<std::string> copied;
-    MachOProxy::runOnAllProxies(false, [&](MachOProxy* proxy) {
-        if (copied.count(proxy->path) > 0) {
-            return;
-        }
-        if (!includeExecutables && !proxy->isDylib())
-            return;
-        (void)copyFile(proxy->buildPath, normalize_absolute_file_path(dylibCachePath + "/Root/" + proxy->path));
-        copied.insert(proxy->path);
-    });
-
-    // HACK for 10.e
-    (void)symlink("libstdc++.6.0.9.dylib", (dylibCachePath + "/Root/usr/lib/libstdc++.6.dylib").c_str());
-    (void)symlink("libstdc++.6.0.9.dylib", (dylibCachePath + "/Root/usr/lib/libstdc++.dylib").c_str());
-    log("Artifact dylibs copied");
-}
-
-void addArtifactPaths(Manifest &manifest) {
-    manifest.setDylibOrderFile("./Metadata/dylibOrderFile.txt");
-    manifest.setDirtyDataOrderFile("./Metadata/dirtyDataOrderFile.txt");
-    manifest.setMetabomFile("./Metadata/metabom.bom");
-
-    for (auto& projects : manifest.projects()) {
-        manifest.addProjectSource(projects.first, "./Root", true);
-        }
-}
-
-int main (int argc, const char * argv[]) {
-       @autoreleasepool {
-        auto defaultCtx = std::make_shared<LoggingContext>("");
-        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.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);
-               }
-
-        auto dylibCacheCtx = std::make_shared<LoggingContext>("DylibCache");
-        setLoggingContext(dylibCacheCtx);
-
-        if (!skipWrites && !skipBuilds) {
-            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);
-        manifest.checkLinks();
-        std::shared_ptr<MultiCacheBuilder> builder = std::make_shared<MultiCacheBuilder>(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/interlinked-dylibs/update_dyld_shared_cache.mm b/interlinked-dylibs/update_dyld_shared_cache.mm
deleted file mode 100644 (file)
index bd7fbea..0000000
+++ /dev/null
@@ -1,375 +0,0 @@
-/* -*- 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 <sys/types.h>
-#include <sys/stat.h>
-#include <sys/mman.h>
-#include <mach/mach.h>
-#include <mach/mach_time.h>
-#include <limits.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <math.h>
-#include <fcntl.h>
-#include <dlfcn.h>
-#include <signal.h>
-#include <errno.h>
-#include <sys/uio.h>
-#include <unistd.h>
-#include <sys/param.h>
-#include <sys/sysctl.h>
-#include <sys/resource.h>
-#include <dirent.h>
-#include <rootless.h>
-
-extern "C" {
-#include <dscsym.h>
-}
-
-#include <vector>
-#include <set>
-#include <map>
-#include <iostream>
-#include <fstream>
-
-#include "mega-dylib-utils.h"
-#include "MultiCacheBuilder.h"
-#include "MachOProxy.h"
-#include "Manifest.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
-
-const std::string anchorsDirPath = "/private/var/db/dyld/shared_region_roots";
-
-bool parsePathsFile( const std::string& filePath, std::set<std::string>& 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<std::string>& 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<std::string>& 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<std::string>& 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<const char*>& overlayPaths,
-                                const std::string& path, std::string& foundPath, std::vector<std::string>& 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);
-}
-
-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<std::string>         archStrs;
-       std::vector<Manifest::Anchor> 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 ) {
-                // <rdar://problem/26182089> -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);
-    }
-
-    std::set<std::string> 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 manifest(archStrs, overlayPath, rootPath, paths);
-
-    manifest.setPlatform(platform);
-
-    // If the path we are writing to is trusted then our sources need to be trusted
-    // <rdar://problem/21166835> Can't update the update_dyld_shared_cache on a non-boot volume
-    bool requireDylibsBeRootlessProtected = isProtectedBySIP(cacheDir);
-    manifest.calculateClosure( requireDylibsBeRootlessProtected );
-
-    for (const std::string& archStr : archStrs) {
-        std::string cachePath = cacheDir + "/dyld_shared_cache_" + archStr;
-        if (!force && manifest.sameContentsAsCacheAtPath("localhost", archStr, cachePath)) {
-            manifest.remove("localhost", archStr);
-            verboseLog("%s is already up to date", cachePath.c_str());
-        }
-    }
-    
-    if (manifest.empty()) {
-        dumpLogAndExit(false);
-    }
-    
-    // build caches
-    std::shared_ptr<MultiCacheBuilder> builder = std::make_shared<MultiCacheBuilder>(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();
-}
index b2dddbce498d93df61555b3bccb1d10394d5eeb9..b1ac76c5ee7d62e3900904e7058cc060eabf7c10 100644 (file)
@@ -97,6 +97,7 @@ private:
 };
 
 
 };
 
 
+
 template <typename E>
 class dyldCacheFileMapping {
 public:                
 template <typename E>
 class dyldCacheFileMapping {
 public:                
@@ -292,6 +293,7 @@ private:
        dyld_cache_accelerator_dof                      fields;
 };
 
        dyld_cache_accelerator_dof                      fields;
 };
 
+
 template <typename E>
 class dyldCacheSlideInfo {
 public:                
 template <typename E>
 class dyldCacheSlideInfo {
 public:                
diff --git a/launch-cache/MachOBinder.hpp b/launch-cache/MachOBinder.hpp
deleted file mode 100644 (file)
index d87fe23..0000000
+++ /dev/null
@@ -1,931 +0,0 @@
-/* -*- 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 <sys/types.h>
-#include <sys/stat.h>
-#include <sys/mman.h>
-#include <mach/mach.h>
-#include <limits.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <unistd.h>
-#include <mach-o/loader.h>
-#include <mach-o/fat.h>
-
-#include <vector>
-#include <set>
-#include <unordered_map>
-#include <unordered_set>
-
-#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 <typename A>
-class Binder : public Rebaser<A>
-{
-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<const char*, class Binder<A>*, CStringHash, CStringEquals> Map;
-
-
-                                                                                               Binder(const MachOLayoutAbstraction&);
-       virtual                                                                         ~Binder() {}
-       
-       const char*                                                                     getDylibID() const;
-       void                                                                            setDependentBinders(const Map& map);
-       void                                                                            bind(std::vector<void*>&);
-       void                                                                            optimize();
-    void                                        addResolverClient(Binder<A>* 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<A>* binder; bool reExport; };
-       struct SymbolReExport { const char* exportName; int dylibOrdinal; const char* importName; };
-       typedef std::unordered_map<const char*, pint_t, CStringHash, CStringEquals> NameToAddrMap;
-       typedef std::unordered_set<const char*, CStringHash, CStringEquals> NameSet;
-       typedef std::unordered_map<const char*, std::set<Binder<A>*>, CStringHash, CStringEquals> ResolverClientsMap;
-
-       
-       static bool                                                                     isPublicLocation(const char* pth);
-       void                                                                            hoistPrivateRexports();
-       int                                                                                     ordinalOfDependentBinder(Binder<A>* dep);
-       void                                                                            doBindDyldInfo(std::vector<void*>& pointersInData);
-       void                                                                            doBindDyldLazyInfo(std::vector<void*>& 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<void*>& pointersInData);
-       bool                                                                            findExportedSymbolAddress(const char* name, pint_t* result, Binder<A>** foundIn, 
-                                                                                                                                                       bool* isResolverSymbol, bool* isAbsolute);
-       const char*                                                                     parentUmbrella();
-       pint_t                                                                          runtimeAddressFromNList(const macho_nlist<P>* 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<BinderAndReExportFlag>                      fDependentDylibs;
-       NameToAddrMap                                                           fHashTable;
-       NameSet                                                                         fSymbolResolvers;
-       NameSet                                                                         fAbsoluteSymbols;
-       std::vector<SymbolReExport>                                     fReExportedSymbols;
-       const macho_nlist<P>*                                           fSymbolTable;
-       const char*                                                                     fStrings;
-       const macho_dysymtab_command<P>*                        fDynamicInfo;
-       const macho_segment_command<P>*                         fFristWritableSegment;
-       const macho_dylib_command<P>*                           fDylibID;
-       const macho_dylib_command<P>*                           fParentUmbrella;
-       const macho_dyld_info_command<P>*                       fDyldInfo;
-       bool                                                                            fOriginallyPrebound;
-       bool                                                                            fReExportedSymbolsResolved;
-       ResolverClientsMap                                                      fResolverInfo;
-};
-
-template <> 
-uint32_t Binder<arm>::runtimeAddressFromNList(const macho_nlist<Pointer32<LittleEndian> >* sym) 
-{ 
-       if (sym->n_desc() & N_ARM_THUMB_DEF)
-               return sym->n_value() + 1; 
-       else
-               return sym->n_value(); 
-}
-
-template <typename A> 
-typename A::P::uint_t  Binder<A>::runtimeAddressFromNList(const macho_nlist<P>* sym) 
-{ 
-       return sym->n_value(); 
-}
-
-
-template <typename A>
-Binder<A>::Binder(const MachOLayoutAbstraction& layout)
-       : Rebaser<A>(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<P>*)this->fHeader)->set_flags(this->fHeader->flags() | MH_PREBOUND | MH_SPLIT_SEGS | 0x80000000);
-
-       // calculate fDynamicInfo, fStrings, fSymbolTable
-       const macho_symtab_command<P>* symtab;
-       const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)this->fHeader + sizeof(macho_header<P>));
-       const uint32_t cmd_count = this->fHeader->ncmds();
-       const macho_load_command<P>* cmd = cmds;
-       for (uint32_t i = 0; i < cmd_count; ++i) {
-               switch (cmd->cmd()) {
-                       case LC_SYMTAB:
-                               symtab = (macho_symtab_command<P>*)cmd;
-                               fSymbolTable = (macho_nlist<P>*)(&this->fLinkEditBase[symtab->symoff()]);
-                               fStrings = (const char*)&this->fLinkEditBase[symtab->stroff()];
-                               break;
-                       case LC_DYSYMTAB:
-                               fDynamicInfo = (macho_dysymtab_command<P>*)cmd;
-                               break;
-                       case LC_ID_DYLIB:
-                               ((macho_dylib_command<P>*)cmd)->set_timestamp(0);
-                               fDylibID = (macho_dylib_command<P>*)cmd;
-                               break;
-                       case LC_LOAD_DYLIB:
-                       case LC_LOAD_WEAK_DYLIB:
-                       case LC_REEXPORT_DYLIB:
-                       case LC_LOAD_UPWARD_DYLIB:
-                               ((macho_dylib_command<P>*)cmd)->set_timestamp(0);
-                               break;
-                       case LC_SUB_FRAMEWORK:
-                               fParentUmbrella = (macho_dylib_command<P>*)cmd;
-                               break;
-                       case LC_DYLD_INFO:
-                       case LC_DYLD_INFO_ONLY:
-                               fDyldInfo = (macho_dyld_info_command<P>*)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<P>*)(((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<mach_o::trie::Entry> 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<mach_o::trie::Entry>::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<x86>::pointerRelocSize()   { return 2; }
-template <> uint8_t    Binder<x86_64>::pointerRelocSize() { return 3; }
-template <> uint8_t    Binder<arm>::pointerRelocSize() { return 2; }
-template <> uint8_t    Binder<arm64>::pointerRelocSize() { return 3; }
-
-template <> uint8_t    Binder<x86>::pointerRelocType()   { return GENERIC_RELOC_VANILLA; }
-template <> uint8_t    Binder<x86_64>::pointerRelocType() { return X86_64_RELOC_UNSIGNED; }
-template <> uint8_t    Binder<arm>::pointerRelocType() { return ARM_RELOC_VANILLA; }
-template <> uint8_t    Binder<arm64>::pointerRelocType() { return ARM64_RELOC_UNSIGNED; }
-
-
-template <typename A>
-const char* Binder<A>::getDylibID() const
-{
-       if ( fDylibID != NULL )
-               return fDylibID->name();
-       else
-               return NULL;
-}
-
-template <typename A>
-const char* Binder<A>::parentUmbrella()
-{
-       if ( fParentUmbrella != NULL )
-               return fParentUmbrella->name();
-       else
-               return NULL;
-}
-
-
-template <typename A>
-bool Binder<A>::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 <typename A>
-void Binder<A>::setDependentBinders(const Map& map)
-{
-       // build vector of dependent dylibs
-       const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)this->fHeader + sizeof(macho_header<P>));
-       const uint32_t cmd_count = this->fHeader->ncmds();
-       const macho_load_command<P>* 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<P>*)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<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
-       }       
-}
-
-template <typename A>
-int Binder<A>::ordinalOfDependentBinder(Binder<A>* dep)
-{
-       for (int i=0; i < fDependentDylibs.size(); ++i) {
-               if ( fDependentDylibs[i].binder == dep ) 
-                       return i+1;
-       }
-       throw "dependend dylib not found";
-}
-
-template <typename A>
-void Binder<A>::hoistPrivateRexports()
-{
-       std::vector<Binder<A>*> privateReExportedDylibs;
-       for (typename std::vector<BinderAndReExportFlag>::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<mach_o::trie::Entry> 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<Binder<A>*>::iterator it = privateReExportedDylibs.begin(); it != privateReExportedDylibs.end(); ++it) {
-                       Binder<A>* binder = *it;
-                       int ordinal = ordinalOfDependentBinder(binder);
-                       const uint8_t* aDylibsExportsStart = binder->fLayout.getDyldInfoExports(); 
-                       const uint8_t* aDylibsExportsEnd = &aDylibsExportsStart[binder->fDyldInfo->export_size()];
-                       std::vector<mach_o::trie::Entry> 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<mach_o::trie::Entry>::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<uint8_t> 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<P>*)fDyldInfo)->set_export_off(0); // invalidate old trie
-               ((macho_dyld_info_command<P>*)fDyldInfo)->set_export_size(newExportsSize);
-       }
-}
-
-
-template <typename A>
-void Binder<A>::bind(std::vector<void*>& pointersInData)
-{
-       this->doBindDyldInfo(pointersInData);
-       this->doBindDyldLazyInfo(pointersInData);
-       // weak bind info is processed at launch time
-}
-
-template <typename A>
-void Binder<A>::bindDyldInfoAt(uint8_t segmentIndex, uint64_t segmentOffset, uint8_t type, int libraryOrdinal, 
-                                                       int64_t addend, const char* symbolName, bool lazyPointer, bool weakImport, std::vector<void*>& pointersInData)
-{
-       //printf("%d 0x%08llX type=%d, lib=%d, addend=%lld, symbol=%s\n", segmentIndex, segmentOffset, type, libraryOrdinal, addend, symbolName);
-       const std::vector<MachOLayoutAbstraction::Segment>& 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<A>* 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<A>* 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 <typename A>
-void Binder<A>::doBindDyldLazyInfo(std::vector<void*>& 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 <typename A>
-void Binder<A>::doBindDyldInfo(std::vector<void*>& 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 <typename A>
-bool Binder<A>::findExportedSymbolAddress(const char* name, pint_t* result, Binder<A>** 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<SymbolReExport>::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<A>* 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<BinderAndReExportFlag>::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 <typename A>
-void Binder<A>::addResolverClient(Binder<A>* clientDylib, const char* symbolName)
-{
-       fResolverInfo[symbolName].insert(clientDylib);
-}                                        
-
-
-template <typename A>
-typename A::P::uint_t Binder<A>::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<P>* const cmds = (macho_load_command<P>*)((uint8_t*)this->fHeader + sizeof(macho_header<P>));
-       const uint32_t cmd_count = this->fHeader->ncmds();
-       const macho_load_command<P>* cmd = cmds;
-       for (uint32_t i = 0; i < cmd_count; ++i) {
-               if ( cmd->cmd() == macho_segment_command<P>::CMD ) {
-                       const macho_segment_command<P>* seg = (macho_segment_command<P>*)cmd;
-                       const macho_section<P>* const sectionsStart = (macho_section<P>*)((uint8_t*)seg + sizeof(macho_segment_command<P>));
-                       const macho_section<P>* const sectionsEnd = &sectionsStart[seg->nsects()];
-                       for (const macho_section<P>* 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<P>* 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<P>*)(((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<SymbolReExport>::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<A>* 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<BinderAndReExportFlag>::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 <typename A>
-void Binder<A>::optimize()
-{
-       hoistPrivateRexports();
-       shareLazyPointersToResolvers();
-}
-
-template <typename A>
-void Binder<A>::shareLazyPointersToResolvers()
-{
-       for (const auto &entry : fResolverInfo) {
-               const char* resolverSymbolName = entry.first;
-        if ( pint_t lpVMAddr = findLazyPointerFor(resolverSymbolName) ) {
-                       for (Binder<A>* 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<arm>::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<x86_64>::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 <typename A>
-void Binder<A>::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 <typename A>
-void Binder<A>::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<P>* const cmds = (macho_load_command<P>*)((uint8_t*)this->fHeader + sizeof(macho_header<P>));
-       const uint32_t cmd_count = this->fHeader->ncmds();
-       const macho_load_command<P>* cmd = cmds;
-       for (uint32_t i = 0; i < cmd_count; ++i) {
-               if ( cmd->cmd() == macho_segment_command<P>::CMD ) {
-                       macho_segment_command<P>* seg = (macho_segment_command<P>*)cmd;
-                       macho_section<P>* const sectionsStart = (macho_section<P>*)((char*)seg + sizeof(macho_segment_command<P>));
-                       macho_section<P>* const sectionsEnd = &sectionsStart[seg->nsects()];
-                       for(macho_section<P>* 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<P>* 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<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
-       }
-}
-
-
-#endif // __MACHO_BINDER__
-
-
-
-
index f92a95ba5bdeee1d4bf15bf361b493a613bfb3ec..ebd298d46a876d870923ab5e6f4c0d0659b76094 100644 (file)
@@ -116,6 +116,10 @@ struct uuid_command {
 
 #define MH_HAS_OBJC                    0x40000000
 
 
 #define MH_HAS_OBJC                    0x40000000
 
+#ifndef CPU_SUBTYPE_ARM64_E
+       #define CPU_SUBTYPE_ARM64_E    2
+#endif
+
 #include "FileAbstraction.hpp"
 #include "Architectures.hpp"
 
 #include "FileAbstraction.hpp"
 #include "Architectures.hpp"
 
@@ -947,7 +951,7 @@ inline int64_t read_sleb128(const uint8_t*& p, const uint8_t* end)
                if (p == end)
                        throw "malformed sleb128";
                byte = *p++;
                if (p == end)
                        throw "malformed sleb128";
                byte = *p++;
-               result |= ((byte & 0x7f) << bit);
+               result |= (((int64_t)(byte & 0x7f)) << bit);
                bit += 7;
        } while (byte & 0x80);
        // sign extend negative numbers
                bit += 7;
        } while (byte & 0x80);
        // sign extend negative numbers
diff --git a/launch-cache/MachOLayout.hpp b/launch-cache/MachOLayout.hpp
deleted file mode 100644 (file)
index 49f593e..0000000
+++ /dev/null
@@ -1,772 +0,0 @@
-/* -*- 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 <sys/types.h>
-#include <sys/stat.h>
-#include <sys/errno.h>
-#include <sys/mman.h>
-#include <mach/mach.h>
-#include <limits.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <unistd.h>
-#include <mach-o/loader.h>
-#include <mach-o/fat.h>
-
-#include <vector>
-#include <set>
-#include <unordered_map>
-
-#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<Segment>&                           getSegments() = 0;
-       virtual const std::vector<Segment>&                     getSegments() const = 0;
-       virtual const Segment*                                          getSegment(const char* name) const = 0;
-       virtual const std::vector<Library>&                     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 <typename A>
-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<Segment>&                           getSegments()                   { return fSegments; }
-       virtual const std::vector<Segment>&                     getSegments() const             { return fSegments; }
-       virtual const Segment*                                          getSegment(const char* name) const;
-       virtual const std::vector<Library>&                     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<typename A::P>* segCmd) const;
-       uint64_t                                                                        segmentFileSize(const macho_segment_command<typename A::P>* segCmd) const;
-       uint64_t                                                                        segmentAlignment(const macho_segment_command<typename A::P>* segCmd) const;
-       uint64_t                                                                        sectionsSize(const macho_segment_command<typename A::P>* segCmd) const;
-       uint64_t                                                                        sectionsAlignment(const macho_segment_command<typename A::P>* 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<Segment>                                            fSegments;
-       std::vector<Library>                                            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<P>*           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<ArchPair>* onlyArchs=NULL);
-                                                                                               ~UniversalMachOLayout() {}
-
-       static const UniversalMachOLayout&                      find(const char* path, const std::set<ArchPair>* onlyArchs=NULL);
-       const MachOLayoutAbstraction*                           getSlice(ArchPair ap) const;
-       const std::vector<MachOLayoutAbstraction*>&     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<const char*, const UniversalMachOLayout*, CStringHash, CStringEquals> PathToNode;
-
-       static bool                                     requestedSlice(const std::set<ArchPair>* onlyArchs, cpu_type_t cpuType, cpu_subtype_t cpuSubType);
-
-       static PathToNode                                                       fgLayoutCache;
-       const char*                                                                     fPath;
-       std::vector<MachOLayoutAbstraction*>            fLayouts;
-};
-
-UniversalMachOLayout::PathToNode UniversalMachOLayout::fgLayoutCache;
-
-
-
-
-const MachOLayoutAbstraction* UniversalMachOLayout::getSlice(ArchPair ap) const
-{
-       // use matching cputype and cpusubtype
-       for(std::vector<MachOLayoutAbstraction*>::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<ArchPair>* 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<ArchPair>* onlyArchs, cpu_type_t cpuType, cpu_subtype_t cpuSubType)
-{
-       if ( onlyArchs == NULL )
-               return true;
-       // must match cputype and cpusubtype
-       for (std::set<ArchPair>::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<ArchPair>* 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<x86>(&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<x86_64>(&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<arm>(&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<arm64>(&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<x86>(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<x86_64>(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<arm>(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<arm64>(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 <typename A>
-uint64_t MachOLayout<A>::segmentSize(const macho_segment_command<typename A::P>* segCmd) const
-{
-       // <rdar://problem/13089366> segments may have 16KB alignment padding at end, if so we can remove that in cache
-       if ( segCmd->nsects() > 0 ) {
-               const macho_section<P>* const sectionsStart = (macho_section<P>*)((uint8_t*)segCmd + sizeof(macho_segment_command<P>));
-               const macho_section<P>* const lastSection = &sectionsStart[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 <typename A>
-uint64_t MachOLayout<A>::segmentAlignment(const macho_segment_command<typename A::P>* segCmd) const
-{
-       int p2align = 12;
-       if ( segCmd->nsects() > 0 ) {
-               const macho_section<P>* const sectionsStart = (macho_section<P>*)((uint8_t*)segCmd + sizeof(macho_segment_command<P>));
-               const macho_section<P>* const sectionsEnd = &sectionsStart[segCmd->nsects()-1];
-               for (const macho_section<P>*  sect=sectionsStart; sect < sectionsEnd; ++sect) {
-                       if ( sect->align() > p2align )
-                               p2align = sect->align();
-               }
-       }
-       return (1 << p2align);
-}
-
-template <typename A>
-uint64_t MachOLayout<A>::segmentFileSize(const macho_segment_command<typename A::P>* segCmd) const
-{
-       // <rdar://problem/13089366> 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<P>* const sectionsStart = (macho_section<P>*)((uint8_t*)segCmd + sizeof(macho_segment_command<P>));
-               const macho_section<P>* const sectionsEnd = &sectionsStart[segCmd->nsects()];
-               for (const macho_section<P>* 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 <typename A>
-uint64_t MachOLayout<A>::sectionsSize(const macho_segment_command<typename A::P>* segCmd) const
-{
-       if ( segCmd->nsects() > 0 ) {
-               const macho_section<P>* const sectionsStart = (macho_section<P>*)((uint8_t*)segCmd + sizeof(macho_segment_command<P>));
-               const macho_section<P>* const lastSection = &sectionsStart[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 <typename A>
-uint64_t MachOLayout<A>::sectionsAlignment(const macho_segment_command<typename A::P>* segCmd) const
-{
-       int p2align = 4;
-       if ( hasSplitSegInfoV2() && (segCmd->nsects() > 0) ) {
-               const macho_section<P>* const sectionsStart = (macho_section<P>*)((uint8_t*)segCmd + sizeof(macho_segment_command<P>));
-               const macho_section<P>* const sectionsEnd = &sectionsStart[segCmd->nsects()-1];
-               for (const macho_section<P>*  sect=sectionsStart; sect < sectionsEnd; ++sect) {
-                       if ( sect->align() > p2align )
-                               p2align = sect->align();
-               }
-       }
-       return (1 << p2align);
-}
-
-
-
-template <typename A>
-MachOLayout<A>::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<P>* mh = (const macho_header<P>*)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<P>* dyldInfo = NULL;
-       const macho_symtab_command<P>* symbolTableCmd = NULL;
-       const macho_dysymtab_command<P>* dynamicSymbolTableCmd = NULL;
-       const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)mh + sizeof(macho_header<P>));
-       const uint32_t cmd_count = mh->ncmds();
-       const macho_load_command<P>* cmd = cmds;
-       for (uint32_t i = 0; i < cmd_count; ++i) {
-               switch ( cmd->cmd() ) {
-                       case LC_ID_DYLIB:       
-                               {
-                                       macho_dylib_command<P>* dylib  = (macho_dylib_command<P>*)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<P>* dylib = (macho_dylib_command<P>*)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<P>*)cmd;
-                               break;
-                       case macho_segment_command<P>::CMD:
-                               {
-                                       const macho_segment_command<P>* segCmd = (macho_segment_command<P>*)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<P>*)cmd;
-                               break;
-                       case LC_DYSYMTAB:
-                               dynamicSymbolTableCmd = (macho_dysymtab_command<P>*)cmd;
-                               break;
-                       case LC_DYLD_INFO:
-                       case LC_DYLD_INFO_ONLY:
-                               fHasDyldInfo = true;
-                               dyldInfo = (struct macho_dyld_info_command<P>*)cmd;
-                               break;
-                       case LC_UUID:
-                               {
-                                       const macho_uuid_command<P>* uc = (macho_uuid_command<P>*)cmd;
-                                       memcpy(&fUUID, uc->uuid(), 16);
-                               }
-                               break;
-               }
-               cmd = (const macho_load_command<P>*)(((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<Segment>::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<P>* symbolTable = (macho_nlist<P>*)((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<x86>::arch()     { return CPU_TYPE_I386; }
-template <> cpu_type_t MachOLayout<x86_64>::arch()  { return CPU_TYPE_X86_64; }
-template <> cpu_type_t MachOLayout<arm>::arch()                { return CPU_TYPE_ARM; }
-template <> cpu_type_t MachOLayout<arm64>::arch()      { return CPU_TYPE_ARM64; }
-
-template <>
-bool MachOLayout<x86>::validReadWriteSeg(const Segment& seg) const
-{
-       return (strcmp(seg.name(), "__DATA") == 0) || (strcmp(seg.name(), "__OBJC") == 0);
-}
-
-template <typename A>
-bool MachOLayout<A>::validReadWriteSeg(const Segment& seg) const
-{
-       return (strcmp(seg.name(), "__DATA") == 0);
-}
-
-
-template <>
-bool MachOLayout<x86>::isSplitSeg() const
-{
-       return ( (this->getFlags() & MH_SPLIT_SEGS) != 0 );
-}
-
-template <>
-bool MachOLayout<arm>::isSplitSeg() const
-{
-       return ( (this->getFlags() & MH_SPLIT_SEGS) != 0 );
-}
-
-template <typename A>
-bool MachOLayout<A>::isSplitSeg() const
-{
-       return false;
-}
-
-template <typename A>
-const MachOLayoutAbstraction::Segment* MachOLayout<A>::getSegment(const char* name) const
-{
-       for(std::vector<Segment>::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/launch-cache/MachORebaser.hpp b/launch-cache/MachORebaser.hpp
deleted file mode 100644 (file)
index 464220a..0000000
+++ /dev/null
@@ -1,1124 +0,0 @@
-/* -*- 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 <sys/types.h>
-#include <sys/stat.h>
-#include <sys/mman.h>
-#include <mach/mach.h>
-#include <limits.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <unistd.h>
-#include <mach-o/loader.h>
-#include <mach-o/fat.h>
-#include <mach-o/reloc.h>
-#include <mach-o/x86_64/reloc.h>
-#include <mach-o/arm/reloc.h>
-#include <vector>
-#include <set>
-
-#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<void*>&) = 0;
-};
-
-
-template <typename A>
-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<void*>&);
-
-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<void*>& pointersInData);
-       void                                                                            adjustReferencesUsingInfoV2(std::vector<void*>& 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<void*>& pointersInData);
-       bool                                                                            adjustExportInfo();
-       void                                                                            doRebase(int segIndex, uint64_t segOffset, uint8_t type, std::vector<void*>& 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<P>* reloc);
-       bool                                                                            unequalSlides() const;
-
-protected:     
-       const macho_header<P>*                                          fHeader; 
-       uint8_t*                                                                        fLinkEditBase;                          // add file offset to this to get linkedit content
-       const MachOLayoutAbstraction&                           fLayout;
-private:
-       const macho_symtab_command<P>*                          fSymbolTable;
-       const macho_dysymtab_command<P>*                        fDynamicSymbolTable;
-       const macho_dyld_info_command<P>*                       fDyldInfo;
-       const macho_linkedit_data_command<P>*           fSplitSegInfo;
-       bool                                                                            fSplittingSegments;
-       bool                                                                            fHasSplitSegInfoV2;
-       std::vector<uint64_t>                                           fSectionOffsetsInSegment;
-};
-
-
-template <typename A>
-Rebaser<A>::Rebaser(const MachOLayoutAbstraction& layout)
- :     fLayout(layout), fLinkEditBase(0), fSymbolTable(NULL), fDynamicSymbolTable(NULL), 
-    fDyldInfo(NULL), fSplitSegInfo(NULL), fSplittingSegments(false), fHasSplitSegInfoV2(false)
-{
-       fHeader = (const macho_header<P>*)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<MachOLayoutAbstraction::Segment>& segments = fLayout.getSegments();
-       for(std::vector<MachOLayoutAbstraction::Segment>::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<P>* const cmds = (macho_load_command<P>*)((uint8_t*)fHeader + sizeof(macho_header<P>));
-       const uint32_t cmd_count = fHeader->ncmds();
-       const macho_load_command<P>* cmd = cmds;
-       for (uint32_t i = 0; i < cmd_count; ++i) {
-               switch (cmd->cmd()) {
-                       case LC_SYMTAB:
-                               fSymbolTable = (macho_symtab_command<P>*)cmd;
-                               break;
-                       case LC_DYSYMTAB:
-                               fDynamicSymbolTable = (macho_dysymtab_command<P>*)cmd;
-                               break;
-                       case LC_DYLD_INFO:
-                       case LC_DYLD_INFO_ONLY:
-                               fDyldInfo = (macho_dyld_info_command<P>*)cmd;
-                               break;
-                       case LC_SEGMENT_SPLIT_INFO:
-                               fSplitSegInfo = (macho_linkedit_data_command<P>*)cmd;
-                               break;
-                       case macho_segment_command<P>::CMD: {
-                               // update segment/section file offsets
-                               macho_segment_command<P>* segCmd = (macho_segment_command<P>*)cmd;
-                               macho_section<P>* const sectionsStart = (macho_section<P>*)((char*)segCmd + sizeof(macho_segment_command<P>));
-                               macho_section<P>* const sectionsEnd = &sectionsStart[segCmd->nsects()];
-                               for(macho_section<P>* sect = sectionsStart; sect < sectionsEnd; ++sect) {
-                                       fSectionOffsetsInSegment.push_back(sect->addr() - segCmd->vmaddr());
-                               }
-                       }
-               }
-               cmd = (const macho_load_command<P>*)(((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<x86>::getArchitecture()    const { return CPU_TYPE_I386; }
-template <> cpu_type_t Rebaser<x86_64>::getArchitecture() const { return CPU_TYPE_X86_64; }
-template <> cpu_type_t Rebaser<arm>::getArchitecture() const { return CPU_TYPE_ARM; }
-template <> cpu_type_t Rebaser<arm64>::getArchitecture() const { return CPU_TYPE_ARM64; }
-
-template <typename A>
-bool Rebaser<A>::unequalSlides() const
-{
-       const std::vector<MachOLayoutAbstraction::Segment>& segments = fLayout.getSegments();
-       uint64_t slide = segments[0].newAddress() - segments[0].address();
-       for(std::vector<MachOLayoutAbstraction::Segment>::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 <typename A>
-uint64_t Rebaser<A>::getBaseAddress() const
-{
-       return fLayout.getSegments()[0].address();
-}
-
-template <typename A>
-uint64_t Rebaser<A>::getVMSize() const
-{
-       uint64_t highestVMAddress = 0;
-       const std::vector<MachOLayoutAbstraction::Segment>& segments = fLayout.getSegments();
-       for(std::vector<MachOLayoutAbstraction::Segment>::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 <typename A>
-bool Rebaser<A>::rebase(std::vector<void*>& 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 <typename A>
-void Rebaser<A>::adjustLoadCommands()
-{
-       const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)fHeader + sizeof(macho_header<P>));
-       const uint32_t cmd_count = fHeader->ncmds();
-       const macho_load_command<P>* 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<P>* dylib  = (macho_dylib_command<P>*)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<P>* dylib  = (macho_dylib_command<P>*)cmd;
-                                       dylib->set_timestamp(2);
-                               }
-                               break;
-                       case macho_routines_command<P>::CMD:
-                               // update -init command
-                               {
-                                       struct macho_routines_command<P>* routines = (struct macho_routines_command<P>*)cmd;
-                                       routines->set_init_address(routines->init_address() + this->getSlideForVMAddress(routines->init_address()));
-                               }
-                               break;
-                       case macho_segment_command<P>::CMD:
-                               // update segment commands
-                               {
-                                       macho_segment_command<P>* seg = (macho_segment_command<P>*)cmd;
-                                       pint_t slide = this->getSlideForVMAddress(seg->vmaddr());
-                                       seg->set_vmaddr(seg->vmaddr() + slide);
-                                       macho_section<P>* const sectionsStart = (macho_section<P>*)((char*)seg + sizeof(macho_segment_command<P>));
-                                       macho_section<P>* const sectionsEnd = &sectionsStart[seg->nsects()];
-                                       for(macho_section<P>* sect = sectionsStart; sect < sectionsEnd; ++sect) {
-                                               sect->set_addr(sect->addr() + slide);
-                                       }
-                               }
-                               break;
-               }
-               cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
-       }
-}
-
-template <>
-uint64_t Rebaser<arm64>::maskedVMAddress(pint_t vmaddress)
-{
-       return (vmaddress & 0x0FFFFFFFFFFFFFFF);
-}
-
-template <typename A>
-typename A::P::uint_t Rebaser<A>::maskedVMAddress(pint_t vmaddress)
-{
-       return vmaddress;
-}
-
-
-template <typename A>
-typename A::P::uint_t Rebaser<A>::getSlideForVMAddress(pint_t vmaddress)
-{
-       pint_t vmaddr = this->maskedVMAddress(vmaddress);
-       const std::vector<MachOLayoutAbstraction::Segment>& segments = fLayout.getSegments();
-       for(std::vector<MachOLayoutAbstraction::Segment>::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>
-typename A::P::uint_t* Rebaser<A>::mappedAddressForVMAddress(pint_t vmaddress)
-{
-       pint_t vmaddr = this->maskedVMAddress(vmaddress);
-       const std::vector<MachOLayoutAbstraction::Segment>& segments = fLayout.getSegments();
-       for(std::vector<MachOLayoutAbstraction::Segment>::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>
-typename A::P::uint_t* Rebaser<A>::mappedAddressForNewAddress(pint_t vmaddress)
-{
-       const std::vector<MachOLayoutAbstraction::Segment>& segments = fLayout.getSegments();
-       for(std::vector<MachOLayoutAbstraction::Segment>::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>
-typename A::P::uint_t Rebaser<A>::getSlideForNewAddress(pint_t newAddress)
-{
-       const std::vector<MachOLayoutAbstraction::Segment>& segments = fLayout.getSegments();
-       for(std::vector<MachOLayoutAbstraction::Segment>::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 <typename A>
-void Rebaser<A>::adjustSymbolTable()
-{
-       macho_nlist<P>* symbolTable = (macho_nlist<P>*)(&fLinkEditBase[fSymbolTable->symoff()]);
-
-       // walk all exports and slide their n_value
-       macho_nlist<P>* lastExport = &symbolTable[fDynamicSymbolTable->iextdefsym()+fDynamicSymbolTable->nextdefsym()];
-       for (macho_nlist<P>* 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<P>*  lastLocal = &symbolTable[fDynamicSymbolTable->ilocalsym()+fDynamicSymbolTable->nlocalsym()];
-       for (macho_nlist<P>* 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 <typename A>
-bool Rebaser<A>::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<mach_o::trie::Entry> originalExports;
-       try {
-               parseTrie(start, end, originalExports);
-       }
-       catch (const char* msg) {
-               throwf("%s in %s", msg, fLayout.getFilePath());
-       }
-       
-       std::vector<mach_o::trie::Entry> newExports;
-       newExports.reserve(originalExports.size());
-       pint_t baseAddress = this->getBaseAddress();
-       pint_t baseAddressSlide = this->getSlideForVMAddress(baseAddress);
-       for (std::vector<mach_o::trie::Entry>::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<uint8_t> 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<P>*)fDyldInfo)->set_export_off(0); // invalidate old trie
-               ((macho_dyld_info_command<P>*)fDyldInfo)->set_export_size(newExportsSize);
-               return false;
-       }
-}
-
-
-
-template <typename A>
-void Rebaser<A>::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 <typename A>
-const uint8_t* Rebaser<A>::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 <typename A>
-void Rebaser<A>::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<MachOLayoutAbstraction::Segment>& segments = fLayout.getSegments();
-               const MachOLayoutAbstraction::Segment& codeSeg = segments[0];
-               for(std::vector<MachOLayoutAbstraction::Segment>::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 <typename A>
-void Rebaser<A>::doRebase(int segIndex, uint64_t segOffset, uint8_t type, std::vector<void*>& pointersInData)
-{
-       const std::vector<MachOLayoutAbstraction::Segment>& 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 <typename A>
-void Rebaser<A>::applyRebaseInfo(std::vector<void*>& 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<arm64>::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<void*>& 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<arm>::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<void*>& 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 <typename A>
-void Rebaser<A>::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<void*>& pointersInData)
-{
-       throw "v2 split seg info not supported yet";
-}
-
-
-template <typename A>
-void Rebaser<A>::adjustReferencesUsingInfoV2(std::vector<void*>& 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<uint64_t> sectionSlides;
-       std::vector<uint64_t> sectionNewAddress;
-       std::vector<uint8_t*> 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                 :== <count> FromToSection+
-       // FromToSection :== <from-sect-index> <to-sect-index> <count> ToOffset+
-       // ToOffset              :== <to-sect-offset-delta> <count> FromOffset+
-       // FromOffset    :== <kind> <count> <from-sect-offset-delta>
-       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__
-
-
-
-
index 30ba09435f40302be4aa00a06c5df6be30b40649..e55d74abd5f81a884b8293fccd1be38a32e8d76c 100644 (file)
@@ -497,7 +497,7 @@ size_t dylib_maker(const void* mapped_cache, std::vector<uint8_t> &dylib_data, c
        optimize_linkedit<A>(((macho_header<P>*)(base_ptr+offsetInFatFile)), textOffsetInCache, mapped_cache, &newSize);
        
        // update fat header with new file size
        optimize_linkedit<A>(((macho_header<P>*)(base_ptr+offsetInFatFile)), textOffsetInCache, mapped_cache, &newSize);
        
        // update fat header with new file size
-    dylib_data.resize(offsetInFatFile+newSize);
+    dylib_data.resize((size_t)(offsetInFatFile+newSize));
     base_ptr                                    = &dylib_data.front();
        FA->size                                    = OSSwapHostToBigInt32(newSize);
 #undef FH
     base_ptr                                    = &dylib_data.front();
        FA->size                                    = OSSwapHostToBigInt32(newSize);
 #undef FH
@@ -521,7 +521,7 @@ int dyld_shared_cache_extract_dylibs_progress(const char* shared_cache_file_path
                return -1;
        }
        
                return -1;
        }
        
-       void* mapped_cache = mmap(NULL, statbuf.st_size, PROT_READ, MAP_PRIVATE, cache_fd, 0);
+       void* mapped_cache = mmap(NULL, (size_t)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;
        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;
@@ -547,9 +547,11 @@ int dyld_shared_cache_extract_dylibs_progress(const char* shared_cache_file_path
                dylib_create_func = dylib_maker<arm>;
        else if ( strcmp((char*)mapped_cache, "dyld_v1   arm64") == 0 ) 
                dylib_create_func = dylib_maker<arm64>;
                dylib_create_func = dylib_maker<arm>;
        else if ( strcmp((char*)mapped_cache, "dyld_v1   arm64") == 0 ) 
                dylib_create_func = dylib_maker<arm64>;
+       else if ( strcmp((char*)mapped_cache, "dyld_v1  arm64e") == 0 )
+               dylib_create_func = dylib_maker<arm64>;
        else {
                fprintf(stderr, "Error: unrecognized dyld shared cache magic.\n");
        else {
                fprintf(stderr, "Error: unrecognized dyld shared cache magic.\n");
-        munmap(mapped_cache, statbuf.st_size);
+        munmap(mapped_cache, (size_t)statbuf.st_size);
                return -1;
        }
 
                return -1;
        }
 
@@ -561,7 +563,7 @@ int dyld_shared_cache_extract_dylibs_progress(const char* shared_cache_file_path
 
     if(result != 0) {
                fprintf(stderr, "Error: dyld_shared_cache_iterate_segments_with_slide failed.\n");
 
     if(result != 0) {
                fprintf(stderr, "Error: dyld_shared_cache_iterate_segments_with_slide failed.\n");
-        munmap(mapped_cache, statbuf.st_size);
+        munmap(mapped_cache, (size_t)statbuf.st_size);
                return result;
     }
 
                return result;
     }
 
@@ -602,7 +604,7 @@ int dyld_shared_cache_extract_dylibs_progress(const char* shared_cache_file_path
                 return;
             }
             
                 return;
             }
             
-            std::vector<uint8_t> *vec   = new std::vector<uint8_t>(statbuf.st_size);
+            std::vector<uint8_t> *vec   = new std::vector<uint8_t>((size_t)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);
             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);
@@ -635,7 +637,7 @@ int dyld_shared_cache_extract_dylibs_progress(const char* shared_cache_file_path
     dispatch_release(group);
     dispatch_release(writer_queue);
     
     dispatch_release(group);
     dispatch_release(writer_queue);
     
-    munmap(mapped_cache, statbuf.st_size);
+    munmap(mapped_cache, (size_t)statbuf.st_size);
        return result;
 }
 
        return result;
 }
 
index 2c7c21203e1b9661300573a5500d1068ad83566b..7aa84ca5b1090d0fa8331d2205069bded14fed49 100644 (file)
@@ -59,7 +59,7 @@ namespace dyld {
 
        // call the callback block on each segment in this image                                                          
        template <typename A>
 
        // call the callback block on each segment in this image                                                          
        template <typename A>
-       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, 
+       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, uint64_t cache_unslid_base_address,
                                                                                        void (^callback)(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo)) 
        {
                typedef typename A::P           P;      
                                                                                        void (^callback)(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo)) 
        {
                typedef typename A::P           P;      
@@ -104,13 +104,14 @@ namespace dyld {
                                        if ( (fileOffset+sizem) > (uint64_t)(cacheEnd-cache) )
                                                sizem = (cacheEnd-cache)-fileOffset;
                                }
                                        if ( (fileOffset+sizem) > (uint64_t)(cacheEnd-cache) )
                                                sizem = (cacheEnd-cache)-fileOffset;
                                }
-                               segInfo.version = 1;
+                               segInfo.version = 2;
                                segInfo.name = segCmd->segname();
                                segInfo.fileOffset = fileOffset;
                                segInfo.fileSize = sizem;
                                if ( segCmd->filesize() > segCmd->vmsize() )
                                        return -1;
                                segInfo.address = segCmd->vmaddr();
                                segInfo.name = segCmd->segname();
                                segInfo.fileOffset = fileOffset;
                                segInfo.fileSize = sizem;
                                if ( segCmd->filesize() > segCmd->vmsize() )
                                        return -1;
                                segInfo.address = segCmd->vmaddr();
+                segInfo.addressOffset = segInfo.address - cache_unslid_base_address;
                                callback(&dylibInfo, &segInfo);
                        }
                        cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
                                callback(&dylibInfo, &segInfo);
                        }
                        cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
@@ -131,6 +132,7 @@ namespace dyld {
                const dyldCacheHeader<E>*      header   = (dyldCacheHeader<E>*)cache;
                const dyldCacheImageInfo<E>*   dylibs   = (dyldCacheImageInfo<E>*)&cache[header->imagesOffset()];
                const dyldCacheFileMapping<E>* mappings = (dyldCacheFileMapping<E>*)&cache[header->mappingOffset()];
                const dyldCacheHeader<E>*      header   = (dyldCacheHeader<E>*)cache;
                const dyldCacheImageInfo<E>*   dylibs   = (dyldCacheImageInfo<E>*)&cache[header->imagesOffset()];
                const dyldCacheFileMapping<E>* mappings = (dyldCacheFileMapping<E>*)&cache[header->mappingOffset()];
+        uint64_t unslid_base_address = mappings[0].address();
                uint64_t greatestMappingOffset = 0;
                for (uint32_t i=0; i < header->mappingCount(); ++i) {
                        if ( (size != 0) && (mappings[i].file_offset() > size) )
                uint64_t greatestMappingOffset = 0;
                for (uint32_t i=0; i < header->mappingCount(); ++i) {
                        if ( (size != 0) && (mappings[i].file_offset() > size) )
@@ -168,7 +170,7 @@ namespace dyld {
                                return -1;
                        if ( firstSeg == NULL )
                                firstSeg = machHeader;
                                return -1;
                        if ( firstSeg == NULL )
                                firstSeg = machHeader;
-                       int result = walkSegments<A>(cache, cacheEnd, firstSeg, dylibPath, inode, modTime, machHeader, callback);
+                       int result = walkSegments<A>(cache, cacheEnd, firstSeg, dylibPath, inode, modTime, machHeader, unslid_base_address, callback);
                        if ( result != 0 )
                                return result;
                }
                        if ( result != 0 )
                                return result;
                }
@@ -201,6 +203,8 @@ extern int dyld_shared_cache_iterate(const void* shared_cache_file, uint32_t sha
                        return dyld::walkImages<arm>(cache, shared_cache_size, callback);
        else if ( strcmp((char*)cache, "dyld_v1   arm64") == 0 ) 
                        return dyld::walkImages<arm64>(cache, shared_cache_size, callback);
                        return dyld::walkImages<arm>(cache, shared_cache_size, callback);
        else if ( strcmp((char*)cache, "dyld_v1   arm64") == 0 ) 
                        return dyld::walkImages<arm64>(cache, shared_cache_size, callback);
+       else if ( strcmp((char*)cache, "dyld_v1  arm64e") == 0 ) 
+                       return dyld::walkImages<arm64>(cache, shared_cache_size, callback);
        else
                return -1;
 }
        else
                return -1;
 }
index 6c75d0e1ee5fe4910ed5f98e608bbea4b611da29..d0ea94d8067e08825501f9136f3bb4d4c1853c88 100644 (file)
@@ -40,11 +40,13 @@ typedef struct dyld_shared_cache_dylib_info dyld_shared_cache_dylib_info;
 
 struct dyld_shared_cache_segment_info {
        uint64_t                version;                // initial version 1
 
 struct dyld_shared_cache_segment_info {
        uint64_t                version;                // initial version 1
+       // following fields exist in 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
        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
+    // following fields exist in version 2
+       uint64_t                addressOffset;  // of segment from base of mapped cache
 };
 typedef struct dyld_shared_cache_segment_info dyld_shared_cache_segment_info;
 
 };
 typedef struct dyld_shared_cache_segment_info dyld_shared_cache_segment_info;
 
index fd9d1c2b776a25c9af5dbdb7f712c5821fbce45a..57b5d074afc4621d52271a6d957e89dcc4644fcf 100644 (file)
@@ -54,6 +54,7 @@ struct dyld_cache_header
        uint64_t        imagesTextCount;                // number of dyld_cache_image_text_info entries
 };
 
        uint64_t        imagesTextCount;                // number of dyld_cache_image_text_info entries
 };
 
+
 struct dyld_cache_mapping_info {
        uint64_t        address;
        uint64_t        size;
 struct dyld_cache_mapping_info {
        uint64_t        address;
        uint64_t        size;
@@ -131,7 +132,6 @@ struct dyld_cache_image_text_info
        uint32_t        pathOffset;                             // offset from start of cache file
 };
 
        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
 // 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
index 63d6b733f83ac278a349b536314ff0c65caaae5e..4c56c35cb77538a4f637c8c12ac0eedc351c308f 100644 (file)
@@ -136,7 +136,9 @@ static const char* default_shared_cache_path() {
        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";
        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__ 
+#elif __arm64e__
+       return IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME "arm64e";
+#elif __arm64__
        return IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME "arm64";
 #else
        #error unsupported architecture
        return IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME "arm64";
 #else
        #error unsupported architecture
@@ -249,7 +251,6 @@ void collect_size(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shar
        info.textSize = segInfo->fileSize;
        info.path = dylibInfo->path;
        results.textSegments.push_back(info);
        info.textSize = segInfo->fileSize;
        info.path = dylibInfo->path;
        results.textSegments.push_back(info);
-       size_t size = segInfo->fileSize;
 }
 
 
 }
 
 
@@ -475,7 +476,7 @@ int main (int argc, const char* argv[]) {
                fprintf(stderr, "Error: open() failed for shared cache file at %s, errno=%d\n", sharedCachePath, errno);
                exit(1);
        }
                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);
+       options.mappedCache = ::mmap(NULL, (size_t)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.mappedCache == MAP_FAILED) {
                fprintf(stderr, "Error: mmap() for shared cache at %s failed, errno=%d\n", sharedCachePath, errno);
                exit(1);
@@ -549,6 +550,39 @@ int main (int argc, const char* argv[]) {
                else {
                        printf("n/a\n");
                }
                else {
                        printf("n/a\n");
                }
+               if ( header->mappingOffset() >= 0xE0 ) {
+            // HACK until this uses new header
+            uint32_t platform = *((uint32_t*)(((char*)header) + 0xD8));
+            uint32_t simulator = *((uint32_t*)(((char*)header) + 0xDC));
+            switch (platform) {
+                case 1:
+                    printf("platform: macOS\n");
+                    break;
+                case 2:
+                    if ( simulator & 0x400 )
+                        printf("platform: iOS simulator\n");
+                    else
+                        printf("platform: iOS\n");
+                    break;
+                case 3:
+                    if ( simulator & 0x400 )
+                        printf("platform: tvOS simulator\n");
+                    else
+                        printf("platform: tvOS\n");
+                    break;
+                case 4:
+                    if ( simulator & 0x400 )
+                        printf("platform: watchOS simulator\n");
+                    else
+                        printf("platform: watchOS\n");
+                    break;
+                case 5:
+                    printf("platform: bridgeOS\n");
+                    break;
+                default:
+                    printf("platform: 0x%08X 0x%08X\n", platform, simulator);
+            }
+        }
                printf("image count: %u\n", header->imagesCount());
                if ( (header->mappingOffset() >= 0x78) && (header->branchPoolsOffset() != 0) ) {
                        printf("branch pool count:  %u\n", header->branchPoolsCount());
                printf("image count: %u\n", header->imagesCount());
                if ( (header->mappingOffset() >= 0x78) && (header->branchPoolsOffset() != 0) ) {
                        printf("branch pool count:  %u\n", header->branchPoolsCount());
@@ -712,7 +746,7 @@ int main (int argc, const char* argv[]) {
                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);
                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;
+               //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);
                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);
@@ -756,7 +790,7 @@ int main (int argc, const char* argv[]) {
                return result;
        }
        else {
                return result;
        }
        else {
-               segment_callback_t callback;
+               segment_callback_t callback = nullptr;
                if ( strcmp((char*)options.mappedCache, "dyld_v1    i386") == 0 ) {
                        switch ( options.mode ) {
                                case modeList:
                if ( strcmp((char*)options.mappedCache, "dyld_v1    i386") == 0 ) {
                        switch ( options.mode ) {
                                case modeList:
@@ -840,7 +874,8 @@ int main (int argc, const char* argv[]) {
                                        break;
                        }
                }               
                                        break;
                        }
                }               
-               else if ( strcmp((char*)options.mappedCache, "dyld_v1   arm64") == 0 ) {
+               else if ( (strcmp((char*)options.mappedCache, "dyld_v1   arm64") == 0)
+               || (strcmp((char*)options.mappedCache, "dyld_v1  arm64e") == 0) ) {
                        switch ( options.mode ) {
                                case modeList:
                                        callback = print_list<arm64>;
                        switch ( options.mode ) {
                                case modeList:
                                        callback = print_list<arm64>;
diff --git a/launch-cache/update_dyld_shared_cache_entitlements.plist b/launch-cache/update_dyld_shared_cache_entitlements.plist
deleted file mode 100644 (file)
index 5ed9d1c..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-    <dict>
-      <key>com.apple.rootless.storage.dyld</key>
-      <true/>
-    </dict>
-</plist>
index 89d9cdcea32d2c74edfb8c3dd7ebc4073b018908..13117d5064abcd750ebd526002e211825f2ece42 100644 (file)
@@ -618,14 +618,12 @@ void ImageLoader::recursiveLoadLibraries(const LinkContext& context, bool prefli
                        bool depLibRequired = false;
                        bool depLibCheckSumsMatch = false;
                        DependentLibraryInfo& requiredLibInfo = libraryInfos[i];
                        bool depLibRequired = false;
                        bool depLibCheckSumsMatch = false;
                        DependentLibraryInfo& requiredLibInfo = libraryInfos[i];
-#if DYLD_SHARED_CACHE_SUPPORT
                        if ( preflightOnly && context.inSharedCache(requiredLibInfo.name) ) {
                                // <rdar://problem/5910137> 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;
                        }
                        if ( preflightOnly && context.inSharedCache(requiredLibInfo.name) ) {
                                // <rdar://problem/5910137> 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);
                        try {
                                unsigned cacheIndex;
                                dependentLib = context.loadLibrary(requiredLibInfo.name, true, this->getPath(), &thisRPaths, cacheIndex);
@@ -1317,7 +1315,7 @@ uintptr_t ImageLoader::read_uleb128(const uint8_t*& p, const uint8_t* end)
                        bit += 7;
                }
        } while (*p++ & 0x80);
                        bit += 7;
                }
        } while (*p++ & 0x80);
-       return result;
+       return (uintptr_t)result;
 }
 
 
 }
 
 
@@ -1336,7 +1334,7 @@ intptr_t ImageLoader::read_sleb128(const uint8_t*& p, const uint8_t* end)
        // sign extend negative numbers
        if ( (byte & 0x40) != 0 )
                result |= (-1LL) << bit;
        // sign extend negative numbers
        if ( (byte & 0x40) != 0 )
                result |= (-1LL) << bit;
-       return result;
+       return (intptr_t)result;
 }
 
 
 }
 
 
index 334b9c7ecefe62451b31bf1840377d9173e91bfb..fd70e2d42c67caa2db33d42f43fcf63c797d585a 100644 (file)
        #define SPLIT_SEG_DYLIB_SUPPORT                 0
        #define PREBOUND_IMAGE_SUPPORT                  __arm__
        #define TEXT_RELOC_SUPPORT                              __i386__
        #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_OLD_CRT_INITIALIZATION  0
        #define SUPPORT_LC_DYLD_ENVIRONMENT             1
        #define SUPPORT_VERSIONED_PATHS                 1
        #define SPLIT_SEG_DYLIB_SUPPORT                 __i386__
        #define PREBOUND_IMAGE_SUPPORT                  __i386__
        #define TEXT_RELOC_SUPPORT                              __i386__
        #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_OLD_CRT_INITIALIZATION  __i386__
        #define SUPPORT_LC_DYLD_ENVIRONMENT             (__i386__ || __x86_64__)
        #define SUPPORT_VERSIONED_PATHS                 1
@@ -609,6 +607,7 @@ public:
        dyld_image_states                                       getState() { return (dyld_image_states)fState; }
 
        ino_t                                                           getInode() const { return fInode; }
        dyld_image_states                                       getState() { return (dyld_image_states)fState; }
 
        ino_t                                                           getInode() const { return fInode; }
+    dev_t                               getDevice() const { return fDevice; }
 
                                                                                // used to sort images bottom-up
        int                                                                     compare(const ImageLoader* right) const;
 
                                                                                // used to sort images bottom-up
        int                                                                     compare(const ImageLoader* right) const;
index 3369bb77d38ce086830d4e0509973f2146d218b7..feba249cb758c2e84d6b7ac69f59c6b0d4a3b2db 100644 (file)
@@ -52,6 +52,7 @@
 #include "ImageLoaderMachOClassic.h"
 #endif
 #include "mach-o/dyld_images.h"
 #include "ImageLoaderMachOClassic.h"
 #endif
 #include "mach-o/dyld_images.h"
+#include "Tracing.h"
 #include "dyld.h"
 
 // <rdar://problem/8718137> use stack guard random value to add padding between dylibs
 #include "dyld.h"
 
 // <rdar://problem/8718137> use stack guard random value to add padding between dylibs
@@ -69,6 +70,43 @@ extern "C" long __stack_chk_guard;
        #define LC_VERSION_MIN_WATCHOS 0x30
 #endif
 
        #define LC_VERSION_MIN_WATCHOS 0x30
 #endif
 
+#ifndef LC_BUILD_VERSION
+       #define LC_BUILD_VERSION 0x32 /* build for platform min OS version */
+
+       /*
+        * The build_version_command contains the min OS version on which this 
+        * binary was built to run for its platform.  The list of known platforms and
+        * tool values following it.
+        */
+       struct build_version_command {
+               uint32_t        cmd;            /* LC_BUILD_VERSION */
+               uint32_t        cmdsize;        /* sizeof(struct build_version_command) plus */
+                                                               /* ntools * sizeof(struct build_tool_version) */
+               uint32_t        platform;       /* platform */
+               uint32_t        minos;          /* X.Y.Z is encoded in nibbles xxxx.yy.zz */
+               uint32_t        sdk;            /* X.Y.Z is encoded in nibbles xxxx.yy.zz */
+               uint32_t        ntools;         /* number of tool entries following this */
+       };
+
+       struct build_tool_version {
+               uint32_t        tool;           /* enum for the tool */
+               uint32_t        version;        /* version number of the tool */
+       };
+
+       /* Known values for the platform field above. */
+       #define PLATFORM_MACOS          1
+       #define PLATFORM_IOS            2
+       #define PLATFORM_TVOS           3
+       #define PLATFORM_WATCHOS        4
+       #define PLATFORM_BRIDGEOS       5
+
+       /* Known values for the tool field above. */
+       #define TOOL_CLANG      1
+       #define TOOL_SWIFT      2
+       #define TOOL_LD         3
+#endif
+
+
 
 #if TARGET_IPHONE_SIMULATOR
        #define LIBSYSTEM_DYLIB_PATH "/usr/lib/libSystem.dylib"
 
 #if TARGET_IPHONE_SIMULATOR
        #define LIBSYSTEM_DYLIB_PATH "/usr/lib/libSystem.dylib"
@@ -224,7 +262,7 @@ void ImageLoaderMachO::sniffLoadCommands(const macho_header* mh, const char* pat
                                        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->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->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 ( (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 ) {
@@ -356,7 +394,7 @@ void ImageLoaderMachO::sniffLoadCommands(const macho_header* mh, const char* pat
                throw "load commands not in a segment";
        if ( linkeditSegCmd == NULL )
                throw "malformed mach-o image: missing __LINKEDIT segment";
                throw "load commands not in a segment";
        if ( linkeditSegCmd == NULL )
                throw "malformed mach-o image: missing __LINKEDIT segment";
-       if ( startOfFileSegCmd == NULL )
+       if ( !inCache && (startOfFileSegCmd == NULL) )
                throw "malformed mach-o image: missing __TEXT segment that maps start of file";
        // <rdar://problem/13145644> verify every segment does not overlap another segment
        if ( context.strictMachORequired ) {
                throw "malformed mach-o image: missing __TEXT segment that maps start of file";
        // <rdar://problem/13145644> verify every segment does not overlap another segment
        if ( context.strictMachORequired ) {
@@ -1258,6 +1296,7 @@ uint32_t ImageLoaderMachO::sdkVersion(const mach_header* mh)
        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;
        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;
+       const struct build_version_command* buildVersCmd;
        for (uint32_t i = 0; i < cmd_count; ++i) {
                switch ( cmd->cmd ) {
                        case LC_VERSION_MIN_MACOSX:
        for (uint32_t i = 0; i < cmd_count; ++i) {
                switch ( cmd->cmd ) {
                        case LC_VERSION_MIN_MACOSX:
@@ -1266,6 +1305,9 @@ uint32_t ImageLoaderMachO::sdkVersion(const mach_header* mh)
                        case LC_VERSION_MIN_WATCHOS:
                                versCmd = (version_min_command*)cmd;
                                return versCmd->sdk;
                        case LC_VERSION_MIN_WATCHOS:
                                versCmd = (version_min_command*)cmd;
                                return versCmd->sdk;
+                       case LC_BUILD_VERSION:
+                               buildVersCmd = (build_version_command*)cmd;
+                               return buildVersCmd->sdk;
                }
                cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
        }
                }
                cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
        }
@@ -1283,6 +1325,7 @@ uint32_t ImageLoaderMachO::minOSVersion(const mach_header* mh)
        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;
        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;
+       const struct build_version_command* buildVersCmd;
        for (uint32_t i = 0; i < cmd_count; ++i) {
                switch ( cmd->cmd ) {
                        case LC_VERSION_MIN_MACOSX:
        for (uint32_t i = 0; i < cmd_count; ++i) {
                switch ( cmd->cmd ) {
                        case LC_VERSION_MIN_MACOSX:
@@ -1291,6 +1334,9 @@ uint32_t ImageLoaderMachO::minOSVersion(const mach_header* mh)
                        case LC_VERSION_MIN_WATCHOS:
                                versCmd = (version_min_command*)cmd;
                                return versCmd->version;
                        case LC_VERSION_MIN_WATCHOS:
                                versCmd = (version_min_command*)cmd;
                                return versCmd->version;
+                       case LC_BUILD_VERSION:
+                               buildVersCmd = (build_version_command*)cmd;
+                               return buildVersCmd->minos;
                }
                cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
        }
                }
                cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
        }
@@ -1794,7 +1840,7 @@ intptr_t ImageLoaderMachO::computeSlide(const mach_header* mh)
        for (uint32_t i = 0; i < cmd_count; ++i) {
                if ( cmd->cmd == LC_SEGMENT_COMMAND ) {
                        const macho_segment_command* seg = (macho_segment_command*)cmd;
        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) )
+                       if ( strcmp(seg->segname, "__TEXT") == 0 )
                                return (char*)mh - (char*)(seg->vmaddr);
                }
                cmd = (const load_command*)(((char*)cmd)+cmd->cmdsize);
                                return (char*)mh - (char*)(seg->vmaddr);
                }
                cmd = (const load_command*)(((char*)cmd)+cmd->cmdsize);
@@ -2004,7 +2050,7 @@ struct DATAdyld {
 
 // These are defined in dyldStartup.s
 extern "C" void stub_binding_helper();
 
 // These are defined in dyldStartup.s
 extern "C" void stub_binding_helper();
-
+extern "C" int _dyld_func_lookup(const char* name, void** address);
 
 void ImageLoaderMachO::setupLazyPointerHandler(const LinkContext& context)
 {
 
 void ImageLoaderMachO::setupLazyPointerHandler(const LinkContext& context)
 {
@@ -2160,7 +2206,9 @@ void ImageLoaderMachO::doImageInit(const LinkContext& context)
                                        }
                                        if ( context.verboseInit )
                                                dyld::log("dyld: calling -init function %p in %s\n", func, 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);
+                    dyld3::kdebug_trace_dyld_duration(DBG_DYLD_TIMING_STATIC_INITIALIZER, (uint64_t)func, 0, ^{
+                        func(context.argc, context.argv, context.envp, context.apple, &context.programVars);
+                    });
                                        break;
                        }
                        cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
                                        break;
                        }
                        cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
@@ -2202,7 +2250,9 @@ void ImageLoaderMachO::doModInitFunctions(const LinkContext& context)
                                                        if ( context.verboseInit )
                                                                dyld::log("dyld: calling initializer function %p in %s\n", func, this->getPath());
                                                        bool haveLibSystemHelpersBefore = (dyld::gLibSystemHelpers != NULL);
                                                        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);
+                            dyld3::kdebug_trace_dyld_duration(DBG_DYLD_TIMING_STATIC_INITIALIZER, (uint64_t)func, 0, ^{
+                                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
                                                        bool haveLibSystemHelpersAfter = (dyld::gLibSystemHelpers != NULL);
                                                        if ( !haveLibSystemHelpersBefore && haveLibSystemHelpersAfter ) {
                                                                // now safe to use malloc() and other calls in libSystem.dylib
@@ -2422,7 +2472,7 @@ void ImageLoaderMachO::mapSegments(int fd, uint64_t offsetInFat, uint64_t lenInF
        }
        // map in all segments
        for(unsigned int i=0, e=segmentCount(); i < e; ++i) {
        }
        // map in all segments
        for(unsigned int i=0, e=segmentCount(); i < e; ++i) {
-               vm_offset_t fileOffset = segFileOffset(i) + offsetInFat;
+               vm_offset_t fileOffset = (vm_offset_t)(segFileOffset(i) + offsetInFat);
                vm_size_t size = segFileSize(i);
                uintptr_t requestedLoadAddress = segPreferredLoadAddress(i) + slide;
                int protection = 0;
                vm_size_t size = segFileSize(i);
                uintptr_t requestedLoadAddress = segPreferredLoadAddress(i) + slide;
                int protection = 0;
@@ -2569,8 +2619,9 @@ const char* ImageLoaderMachO::findClosestSymbol(const mach_header* mh, const voi
                                        unslidLinkEditBase = (uint8_t*)(seg->vmaddr - seg->fileoff);
                                        linkEditBaseFound = true;
                                }
                                        unslidLinkEditBase = (uint8_t*)(seg->vmaddr - seg->fileoff);
                                        linkEditBaseFound = true;
                                }
-                               else if ( (seg->fileoff == 0) && (seg->filesize != 0) )
+                               else if ( strcmp(seg->segname, "__TEXT") == 0 ) {
                                        slide = (uintptr_t)mh - seg->vmaddr;
                                        slide = (uintptr_t)mh - seg->vmaddr;
+                }
                                break;
                        case LC_SYMTAB:
                                symtab = (symtab_command*)cmd;
                                break;
                        case LC_SYMTAB:
                                symtab = (symtab_command*)cmd;
index fa71b097f281b3a50c59a45601b89f9a30e11ddf..86723e72d04d94c9c70dfaaba8cfab93c991d0d2 100644 (file)
@@ -436,14 +436,14 @@ ImageLoaderMachOClassic::mapSplitSegDylibOutsideSharedRegion(int fd,
        while ( ! foundRoom ) {
                foundRoom = true;
                for(unsigned int i=0; i < regionCount; ++i) {
        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 ;
+                       vm_address_t addr = (vm_address_t)(nextAltLoadAddress + regions[i].sfm_address - regions[0].sfm_address);
+                       vm_size_t size = (vm_size_t)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) {
                        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 ;
+                                       addr = (vm_address_t)(nextAltLoadAddress + regions[j].sfm_address - regions[0].sfm_address);
+                                       size = (vm_size_t)(regions[j].sfm_size);
                                        (void)vm_deallocate(mach_task_self(), addr, size);
                                }
                                nextAltLoadAddress += 0x00100000;  // skip ahead 1MB and try again
                                        (void)vm_deallocate(mach_task_self(), addr, size);
                                }
                                nextAltLoadAddress += 0x00100000;  // skip ahead 1MB and try again
@@ -459,7 +459,7 @@ ImageLoaderMachOClassic::mapSplitSegDylibOutsideSharedRegion(int fd,
        }
        
        // map in each region
        }
        
        // map in each region
-       uintptr_t slide = nextAltLoadAddress - regions[0].sfm_address;
+       uintptr_t slide = (uintptr_t)(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) ) {
        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) ) {
@@ -467,7 +467,7 @@ ImageLoaderMachOClassic::mapSplitSegDylibOutsideSharedRegion(int fd,
                }
                else {
                        void* mmapAddress = (void*)(uintptr_t)(regions[i].sfm_address + slide);
                }
                else {
                        void* mmapAddress = (void*)(uintptr_t)(regions[i].sfm_address + slide);
-                       size_t size = regions[i].sfm_size;
+                       size_t size = (size_t)regions[i].sfm_size;
                        int protection = 0;
                        if ( regions[i].sfm_init_prot & VM_PROT_EXECUTE )
                                protection   |= PROT_EXEC;
                        int protection = 0;
                        if ( regions[i].sfm_init_prot & VM_PROT_EXECUTE )
                                protection   |= PROT_EXEC;
@@ -728,7 +728,7 @@ void ImageLoaderMachOClassic::resetPreboundLazyPointers(const LinkContext& conte
 {
        // loop through all local (internal) relocation records looking for pre-bound-lazy-pointer values
        const uintptr_t relocBase = this->getRelocBase();
 {
        // 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 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) {
        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) {
index fb3c04e25f723cd43503e27e8322c422dba08f4a..020ec51fcd87f62f20e1d9eb2f6bf68b4f24c07a 100644 (file)
@@ -35,6 +35,7 @@
 #include <sys/fcntl.h>
 #include <sys/stat.h> 
 #include <sys/param.h>
 #include <sys/fcntl.h>
 #include <sys/stat.h> 
 #include <sys/param.h>
+#include <sys/sysctl.h>
 #include <mach/mach.h>
 #include <mach/thread_status.h>
 #include <mach-o/loader.h> 
 #include <mach/mach.h>
 #include <mach/thread_status.h>
 #include <mach-o/loader.h> 
        struct macho_routines_command   : public routines_command  {};  
 #endif
 
        struct macho_routines_command   : public routines_command  {};  
 #endif
 
+#if __arm__ || __arm64__
+bool ImageLoaderMachOCompressed::sVmAccountingDisabled  = false;
+bool ImageLoaderMachOCompressed::sVmAccountingSuspended = false;
+#endif
+
+
 
 // create image for main executable
 ImageLoaderMachOCompressed* ImageLoaderMachOCompressed::instantiateMainExecutable(const macho_header* mh, uintptr_t slide, const char* path, 
 
 // create image for main executable
 ImageLoaderMachOCompressed* ImageLoaderMachOCompressed::instantiateMainExecutable(const macho_header* mh, uintptr_t slide, const char* path, 
@@ -150,6 +157,10 @@ ImageLoaderMachOCompressed* ImageLoaderMachOCompressed::instantiateFromFile(cons
                // make sure path is stable before recording in dyld_all_image_infos
                image->setMapped(context);
 
                // make sure path is stable before recording in dyld_all_image_infos
                image->setMapped(context);
 
+               // dylibs with thread local variables cannot be unloaded because there is no way to clean up all threads
+               if ( image->machHeader()->flags & MH_HAS_TLV_DESCRIPTORS )
+                       image->setNeverUnload();
+
                // 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()) {
                // 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()) {
@@ -838,8 +849,45 @@ void ImageLoaderMachOCompressed::doBindJustLazies(const LinkContext& context)
        eachLazyBind(context, &ImageLoaderMachOCompressed::bindAt);
 }
 
        eachLazyBind(context, &ImageLoaderMachOCompressed::bindAt);
 }
 
+#if __arm__ || __arm64__
+int ImageLoaderMachOCompressed::vmAccountingSetSuspended(bool suspend, const LinkContext& context)
+{
+    if ( context.verboseBind )
+        dyld::log("vm.footprint_suspend=%d\n", suspend);
+    int newValue = suspend ? 1 : 0;
+    int oldValue = 0;
+    size_t newlen = sizeof(newValue);
+    size_t oldlen = sizeof(oldValue);
+    return sysctlbyname("vm.footprint_suspend", &oldValue, &oldlen, &newValue, newlen);
+}
+#endif
+
 void ImageLoaderMachOCompressed::eachBind(const LinkContext& context, bind_handler handler)
 {
 void ImageLoaderMachOCompressed::eachBind(const LinkContext& context, bind_handler handler)
 {
+#if __arm__ || __arm64__
+    // <rdar://problem/29099600> dyld should tell the kernel when it is doing root fix-ups
+    if ( !sVmAccountingDisabled ) {
+        if ( fInSharedCache ) {
+            if ( !sVmAccountingSuspended ) {
+                int ret = vmAccountingSetSuspended(true, context);
+                if ( context.verboseBind && (ret != 0) )
+                    dyld::log("vm.footprint_suspend => %d, errno=%d\n", ret, errno);
+                if ( ret == 0 )
+                    sVmAccountingSuspended = true;
+                else
+                    sVmAccountingDisabled = true;
+            }
+        }
+        else if ( sVmAccountingSuspended ) {
+            int ret = vmAccountingSetSuspended(false, context);
+            if ( ret == 0 )
+                sVmAccountingSuspended = false;
+            else if ( errno == ENOENT )
+                sVmAccountingDisabled = true;
+        }
+    }
+#endif
+
        try {
                uint8_t type = 0;
                int segmentIndex = -1;
        try {
                uint8_t type = 0;
                int segmentIndex = -1;
index 013bb907b3a70a51997d0b29d65c0da581c3b38a..d0423875a002bf01b02d5406c0724d120b4d6746 100644 (file)
@@ -136,8 +136,14 @@ private:
     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);
     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;
        const struct dyld_info_command*                 fDyldInfo;
+
+#if __arm__ || __arm64__
+    static int                          vmAccountingSetSuspended(bool suspend, const LinkContext& context);
+    static bool                         sVmAccountingDisabled;  // sysctl not availble
+    static bool                         sVmAccountingSuspended; // kernel is currently ignoring COWs
+#endif
 };
 
 
 };
 
 
index 2e45f216e1ebdf239df295a1b40917e8932c92c4..a54625d2ab457436daa707c2697dda3560e62f60 100644 (file)
 #include "ImageLoaderMachO.h"
 #include "mach-o/dyld_images.h"
 #include "dyldLibSystemInterface.h"
 #include "ImageLoaderMachO.h"
 #include "mach-o/dyld_images.h"
 #include "dyldLibSystemInterface.h"
+#include "Tracing.h"
 #include "dyld.h"
 
 // from dyld_gdb.cpp 
 extern void addImagesToAllImages(uint32_t infoCount, const dyld_image_info info[]);
 
 #include "dyld.h"
 
 // from dyld_gdb.cpp 
 extern void addImagesToAllImages(uint32_t infoCount, const dyld_image_info info[]);
 
+extern "C" int _dyld_func_lookup(const char* name, void** address);
+
 
 #ifndef EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE
        #define EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE                       0x02
 
 #ifndef EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE
        #define EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE                       0x02
@@ -77,9 +80,9 @@ extern void addImagesToAllImages(uint32_t infoCount, const dyld_image_info info[
 #if SUPPORT_ACCELERATE_TABLES
  
 
 #if SUPPORT_ACCELERATE_TABLES
  
 
-ImageLoaderMegaDylib* ImageLoaderMegaDylib::makeImageLoaderMegaDylib(const dyld_cache_header* header, long slide, const LinkContext& context)
+ImageLoaderMegaDylib* ImageLoaderMegaDylib::makeImageLoaderMegaDylib(const dyld_cache_header* header, long slide, const macho_header* mainMH, const LinkContext& context)
 {
 {
-       return new ImageLoaderMegaDylib(header, slide, context);
+       return new ImageLoaderMegaDylib(header, slide, mainMH, context);
 }
 
 struct DATAdyld {
 }
 
 struct DATAdyld {
@@ -91,7 +94,7 @@ struct DATAdyld {
 
 
 
 
 
 
-ImageLoaderMegaDylib::ImageLoaderMegaDylib(const dyld_cache_header* header, long slide, const LinkContext& context)
+ImageLoaderMegaDylib::ImageLoaderMegaDylib(const dyld_cache_header* header, long slide, const macho_header* mainMH, const LinkContext& context)
        : ImageLoader("dyld shared cache", 0), _header(header), _linkEditBias(NULL), _slide(slide),
         _lockArray(NULL), _lockArrayInUseCount(0)
 {
        : ImageLoader("dyld shared cache", 0), _header(header), _linkEditBias(NULL), _slide(slide),
         _lockArray(NULL), _lockArrayInUseCount(0)
 {
@@ -123,7 +126,7 @@ ImageLoaderMegaDylib::ImageLoaderMegaDylib(const dyld_cache_header* header, long
        DATAdyld* dyldSection = (DATAdyld*)(accHeader->dyldSectionAddr + slide);
        dyldSection->dyldLazyBinder = NULL; // not used by libdyld.dylib
        dyldSection->dyldFuncLookup = (void*)&_dyld_func_lookup;
        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();
+       dyldSection->vars.mh = mainMH;
        context.setNewProgramVars(dyldSection->vars);
 }
 
        context.setNewProgramVars(dyldSection->vars);
 }
 
@@ -269,6 +272,7 @@ unsigned ImageLoaderMegaDylib::findImageIndex(const LinkContext& context, const
        if ( strncmp(path, "@rpath/", 7) == 0 ) {
                std::vector<const char*> rpathsFromMainExecutable;
                context.mainExecutable->getRPaths(context, rpathsFromMainExecutable);
        if ( strncmp(path, "@rpath/", 7) == 0 ) {
                std::vector<const char*> rpathsFromMainExecutable;
                context.mainExecutable->getRPaths(context, rpathsFromMainExecutable);
+               rpathsFromMainExecutable.push_back("/System/Library/Frameworks/");
                const char* trailingPath = &path[7];
                for (const char* anRPath : rpathsFromMainExecutable) {
                        if ( anRPath[0] != '/' )
                const char* trailingPath = &path[7];
                for (const char* anRPath : rpathsFromMainExecutable) {
                        if ( anRPath[0] != '/' )
@@ -283,17 +287,17 @@ unsigned ImageLoaderMegaDylib::findImageIndex(const LinkContext& context, const
                        }
                }
        }
                        }
                }
        }
-
-       // handle symlinks embedded in load commands
-       char resolvedPath[PATH_MAX];
-       realpath(path, resolvedPath);
-       int realpathErrno = errno;
-       // If realpath() resolves to a path which does not exist on disk, errno is set to ENOENT
-       if ( (realpathErrno == ENOENT) || (realpathErrno == 0) ) {
-               if ( strcmp(resolvedPath, path) != 0 )
-                       return findImageIndex(context, resolvedPath);
-       }
-
+    else {
+        // handle symlinks embedded in load commands
+        char resolvedPath[PATH_MAX];
+        realpath(path, resolvedPath);
+        int realpathErrno = errno;
+        // If realpath() resolves to a path which does not exist on disk, errno is set to ENOENT
+        if ( (realpathErrno == ENOENT) || (realpathErrno == 0) ) {
+            if ( strcmp(resolvedPath, path) != 0 )
+                return findImageIndex(context, resolvedPath);
+        }
+    }
 
        dyld::throwf("no cache image with name (%s)", path);
 }
 
        dyld::throwf("no cache image with name (%s)", path);
 }
@@ -839,7 +843,7 @@ void ImageLoaderMegaDylib::recursiveSpinLockRelease(unsigned int imageIndex, mac
 
 
 void ImageLoaderMegaDylib::recursiveInitialization(const LinkContext& context, mach_port_t thisThread, unsigned int imageIndex,
 
 
 void ImageLoaderMegaDylib::recursiveInitialization(const LinkContext& context, mach_port_t thisThread, unsigned int imageIndex,
-                                                                                                       InitializerTimingList& timingInfo)
+                                                                                                       InitializerTimingList& timingInfo, UpwardIndexes& upInits)
 {
        // Don't do any locking until libSystem.dylib is initialized, so we can malloc() the lock array
        bool useLock = dyld::gProcessInfo->libSystemInitialized;
 {
        // Don't do any locking until libSystem.dylib is initialized, so we can malloc() the lock array
        bool useLock = dyld::gProcessInfo->libSystemInitialized;
@@ -858,7 +862,9 @@ void ImageLoaderMegaDylib::recursiveInitialization(const LinkContext& context, m
                        unsigned subDepIndex = _dependenciesArray[i];
                        // ignore upward links
                        if ( (subDepIndex & 0x8000) == 0 )
                        unsigned subDepIndex = _dependenciesArray[i];
                        // ignore upward links
                        if ( (subDepIndex & 0x8000) == 0 )
-                               recursiveInitialization(context, thisThread, subDepIndex, timingInfo);
+                               recursiveInitialization(context, thisThread, subDepIndex, timingInfo, upInits);
+            else
+                upInits.images[upInits.count++] = (subDepIndex & 0x7FFF);
                }
 
                // notify objc about this image
                }
 
                // notify objc about this image
@@ -875,7 +881,9 @@ void ImageLoaderMegaDylib::recursiveInitialization(const LinkContext& context, m
                                if ( context.verboseInit )
                                        dyld::log("dyld: calling initializer function %p in %s\n", func, getIndexedPath(imageIndex));
                                bool haveLibSystemHelpersBefore = (dyld::gLibSystemHelpers != NULL);
                                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);
+                               dyld3::kdebug_trace_dyld_duration(DBG_DYLD_TIMING_STATIC_INITIALIZER, (uint64_t)func, 0, ^{
+                                       func(context.argc, context.argv, context.envp, context.apple, &context.programVars);
+                               });
                                bool haveLibSystemHelpersAfter = (dyld::gLibSystemHelpers != NULL);
                                ranSomeInitializers = true;
                                if ( !haveLibSystemHelpersBefore && haveLibSystemHelpersAfter ) {
                                bool haveLibSystemHelpersAfter = (dyld::gLibSystemHelpers != NULL);
                                ranSomeInitializers = true;
                                if ( !haveLibSystemHelpersBefore && haveLibSystemHelpersAfter ) {
@@ -904,11 +912,20 @@ void ImageLoaderMegaDylib::recursiveInitialization(const LinkContext& context, m
 
 
 void ImageLoaderMegaDylib::recursiveInitialization(const LinkContext& context, mach_port_t thisThread, const char* pathToInitialize,
 
 
 void ImageLoaderMegaDylib::recursiveInitialization(const LinkContext& context, mach_port_t thisThread, const char* pathToInitialize,
-                                                                                                       InitializerTimingList& timingInfo, UninitedUpwards& uninitUps)
+                                                                                                       InitializerTimingList& timingInfo, UninitedUpwards&)
 {
        unsigned imageIndex;
        if ( hasDylib(pathToInitialize, &imageIndex) ) {
 {
        unsigned imageIndex;
        if ( hasDylib(pathToInitialize, &imageIndex) ) {
-               this->recursiveInitialization(context, thisThread, imageIndex, timingInfo);
+               UpwardIndexes upsBuffer[256];
+               UpwardIndexes& ups = upsBuffer[0];
+               ups.count = 0;
+               this->recursiveInitialization(context, thisThread, imageIndex, timingInfo, ups);
+               for (int i=0; i < ups.count; ++i) {
+                       UpwardIndexes upsBuffer2[256];
+                       UpwardIndexes& ignoreUp = upsBuffer2[0];
+                       ignoreUp.count = 0;
+                       this->recursiveInitialization(context, thisThread, ups.images[i], timingInfo, ignoreUp);
+               }
        }
 }
 
        }
 }
 
@@ -947,9 +964,8 @@ unsigned ImageLoaderMegaDylib::appendImagesToNotify(dyld_image_states state, boo
        uint8_t targetCacheState = dyldStateToCacheState(state);
        if ( targetCacheState == kStateUnused )
                return 0;
        uint8_t targetCacheState = dyldStateToCacheState(state);
        if ( targetCacheState == kStateUnused )
                return 0;
-
        unsigned usedCount = 0;
        unsigned usedCount = 0;
-       for (int i=_imageCount-1; i > 0; --i) {
+       for (int i=_imageCount-1; i >= 0; --i) {
                uint16_t index = _bottomUpArray[i];
                uint8_t imageState = _stateFlags[index];
                if ( imageState == kStateFlagWeakBound )
                uint16_t index = _bottomUpArray[i];
                uint8_t imageState = _stateFlags[index];
                if ( imageState == kStateFlagWeakBound )
@@ -994,7 +1010,17 @@ bool ImageLoaderMegaDylib::dlopenFromCache(const LinkContext& context, const cha
                InitializerTimingList timingInfo[_initializerCount];
                timingInfo[0].count = 0;
                mach_port_t thisThread  = mach_thread_self();
                InitializerTimingList timingInfo[_initializerCount];
                timingInfo[0].count = 0;
                mach_port_t thisThread  = mach_thread_self();
-               this->recursiveInitialization(context, thisThread, imageIndex, timingInfo[0]);
+               UpwardIndexes upsBuffer[256];  // room for 511 dangling upward links
+               UpwardIndexes& ups = upsBuffer[0];
+               ups.count = 0;
+               this->recursiveInitialization(context, thisThread, imageIndex, timingInfo[0], ups);
+               // make sure any upward linked dylibs were initialized
+               for (int i=0; i < ups.count; ++i) {
+                       UpwardIndexes upsBuffer2[256];
+                       UpwardIndexes& ignoreUp = upsBuffer2[0];
+                       ignoreUp.count = 0;
+                       this->recursiveInitialization(context, thisThread, ups.images[i], timingInfo[0], ignoreUp);
+               }
                mach_port_deallocate(mach_task_self(), thisThread);
                context.notifyBatch(dyld_image_state_initialized, false);
        }
                mach_port_deallocate(mach_task_self(), thisThread);
                context.notifyBatch(dyld_image_state_initialized, false);
        }
index 425ec8e7b17d76c713833ff9a427114288c1f2d8..6046da63c653c46b71c36174769b12ea06deec42 100644 (file)
@@ -39,7 +39,7 @@
 //
 class ImageLoaderMegaDylib : public ImageLoader {
 public:
 //
 class ImageLoaderMegaDylib : public ImageLoader {
 public:
-       static ImageLoaderMegaDylib*            makeImageLoaderMegaDylib(const dyld_cache_header*, long slide, const LinkContext&);
+       static ImageLoaderMegaDylib*            makeImageLoaderMegaDylib(const dyld_cache_header*, long slide, const macho_header* mainMH, const LinkContext&);
 
 
        virtual                                                         ~ImageLoaderMegaDylib();
 
 
        virtual                                                         ~ImageLoaderMegaDylib();
@@ -185,7 +185,13 @@ protected:
        virtual void                                            setWeakSymbolsBound(unsigned index);
 
 private:
        virtual void                                            setWeakSymbolsBound(unsigned index);
 
 private:
-                                                                               ImageLoaderMegaDylib(const dyld_cache_header*, long slide, const LinkContext&);
+                                                                               ImageLoaderMegaDylib(const dyld_cache_header*, long slide, const macho_header* mainMH, const LinkContext&);
+
+    struct UpwardIndexes
+       {
+               uint16_t         count;
+               uint16_t     images[1];
+       };
 
        const macho_header*                                     getIndexedMachHeader(unsigned index) const;
        const uint8_t*                                          getIndexedTrie(unsigned index, uint32_t& trieSize) const;
 
        const macho_header*                                     getIndexedMachHeader(unsigned index) const;
        const uint8_t*                                          getIndexedTrie(unsigned index, uint32_t& trieSize) const;
@@ -213,7 +219,7 @@ private:
                                                                                                                        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,
                                                                                                                        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);
+                                                                                                                               InitializerTimingList& timingInfo, UpwardIndexes&);
        void                                                            recursiveSpinLockAcquire(unsigned int imageIndex, mach_port_t thisThread);
        void                                                            recursiveSpinLockRelease(unsigned int imageIndex, mach_port_t thisThread);
 
        void                                                            recursiveSpinLockAcquire(unsigned int imageIndex, mach_port_t thisThread);
        void                                                            recursiveSpinLockRelease(unsigned int imageIndex, mach_port_t thisThread);
 
index 4aff26a8ce56ef723a5ef973c55ecc47dcc51ced..c98a9e356125434442d5923e9d67108bd08fedd6 100644 (file)
@@ -40,6 +40,7 @@
 #include <sys/un.h>
 #include <sys/syslog.h>
 #include <sys/uio.h>
 #include <sys/un.h>
 #include <sys/syslog.h>
 #include <sys/uio.h>
+#include <mach/mach.h>
 #include <mach-o/fat.h>
 #include <mach-o/loader.h> 
 #include <mach-o/ldsyms.h> 
 #include <mach-o/fat.h>
 #include <mach-o/loader.h> 
 #include <mach-o/ldsyms.h> 
 #include <sandbox.h>
 #include <sandbox/private.h>
 
 #include <sandbox.h>
 #include <sandbox/private.h>
 
+extern "C" int __fork();
+
 #include <array>
 #include <array>
+#include <algorithm>
+#include <vector>
 
 #ifndef CPU_SUBTYPE_ARM_V5TEJ
        #define CPU_SUBTYPE_ARM_V5TEJ           ((cpu_subtype_t) 7)
 
 #ifndef CPU_SUBTYPE_ARM_V5TEJ
        #define CPU_SUBTYPE_ARM_V5TEJ           ((cpu_subtype_t) 7)
        #define CPU_SUBTYPE_X86_64_H            ((cpu_subtype_t) 8) 
 #endif 
 
        #define CPU_SUBTYPE_X86_64_H            ((cpu_subtype_t) 8) 
 #endif 
 
+#ifndef CPU_SUBTYPE_ARM64_E
+       #define CPU_SUBTYPE_ARM64_E    2
+#endif
+
 #ifndef VM_PROT_SLIDE   
     #define VM_PROT_SLIDE 0x20
 #endif
 
 #ifndef VM_PROT_SLIDE   
     #define VM_PROT_SLIDE 0x20
 #endif
 
-#include <vector>
-#include <algorithm>
-
 #include "mach-o/dyld_gdb.h"
 
 #include "dyld.h"
 #include "ImageLoader.h"
 #include "ImageLoaderMachO.h"
 #include "dyldLibSystemInterface.h"
 #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"
 #include "dyld_cache_format.h"
-#endif
 #include "dyld_process_info_internal.h"
 #include <coreSymbolicationDyldSupport.h>
 #if TARGET_IPHONE_SIMULATOR
 #include "dyld_process_info_internal.h"
 #include <coreSymbolicationDyldSupport.h>
 #if TARGET_IPHONE_SIMULATOR
        #include "dyldSyscallInterface.h"
 #endif
 
        #include "dyldSyscallInterface.h"
 #endif
 
+#include "LaunchCache.h"
+#include "libdyldEntryVector.h"
+#include "MachOParser.h"
+#include "Loading.h"
+#include "DyldSharedCache.h"
+#include "SharedCacheRuntime.h"
+#include "StringUtils.h"
+#include "Tracing.h"
+#include "DyldCacheParser.h"
+
+extern "C" {
+    #include "closuredProtocol.h"
+}
+
 
 // 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
 
 // 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)
+#define CPU_SUBTYPES_SUPPORTED  ((__arm__ || __arm64__ || __x86_64__) && !TARGET_IPHONE_SIMULATOR)
 
 #if __LP64__
        #define LC_SEGMENT_COMMAND              LC_SEGMENT_64
 
 #if __LP64__
        #define LC_SEGMENT_COMMAND              LC_SEGMENT_64
@@ -213,8 +231,7 @@ struct EnvironmentVariables {
        bool                                            DYLD_PRINT_ENV;
        bool                                            DYLD_DISABLE_DOFS;
        bool                                            DYLD_PRINT_CS_NOTIFICATIONS;
        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_SHARED_CACHE_DIR           ==> sSharedCacheOverrideDir
                                                        //      DYLD_ROOT_PATH                                  ==> gLinkContext.rootPaths
                                                        //      DYLD_IMAGE_SUFFIX                               ==> gLinkContext.imageSuffix
                                                        //      DYLD_PRINT_OPTS                                 ==> gLinkContext.verboseOpts
                                                        //      DYLD_ROOT_PATH                                  ==> gLinkContext.rootPaths
                                                        //      DYLD_IMAGE_SUFFIX                               ==> gLinkContext.imageSuffix
                                                        //      DYLD_PRINT_OPTS                                 ==> gLinkContext.verboseOpts
@@ -248,6 +265,7 @@ enum EnvVarMode { envNone, envPrintOnly, envAll };
 static const char*                                     sExecPath = NULL;
 static const char*                                     sExecShortName = NULL;
 static const macho_header*                     sMainExecutableMachHeader = NULL;
 static const char*                                     sExecPath = NULL;
 static const char*                                     sExecShortName = NULL;
 static const macho_header*                     sMainExecutableMachHeader = NULL;
+static uintptr_t                                       sMainExecutableSlide = 0;
 #if CPU_SUBTYPES_SUPPORTED
 static cpu_type_t                                      sHostCPU;
 static cpu_subtype_t                           sHostCPUsubtype;
 #if CPU_SUBTYPES_SUPPORTED
 static cpu_type_t                                      sHostCPU;
 static cpu_subtype_t                           sHostCPUsubtype;
@@ -269,26 +287,17 @@ 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 };
 #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 };
+static const char*                                     sRestrictedFrameworkFallbackPaths[] = { "/System/Library/Frameworks", NULL };
+static const char*                                     sRestrictedLibraryFallbackPaths[] = { "/usr/lib", NULL };
 #else
 static const char*                                     sFrameworkFallbackPaths[] = { "/System/Library/Frameworks", NULL };
 static const char*                                     sLibraryFallbackPaths[] = { "/usr/local/lib", "/usr/lib", NULL };
 #endif
 #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
 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
+static dyld3::SharedCacheLoadInfo      sSharedCacheLoadInfo;
+static const char*                                     sSharedCacheOverrideDir;
+       bool                                                    gSharedCacheOverridden = false;
 ImageLoader::LinkContext                       gLinkContext;
 bool                                                           gLogAPIs = false;
 #if SUPPORT_ACCELERATE_TABLES
 ImageLoader::LinkContext                       gLinkContext;
 bool                                                           gLogAPIs = false;
 #if SUPPORT_ACCELERATE_TABLES
@@ -303,7 +312,7 @@ static std::vector<DylibOverride>   sDylibOverrides;
 static int                                                     sLogSocket = -1;
 #endif
 static bool                                                    sFrameworksFoundAsDylibs = false;
 static int                                                     sLogSocket = -1;
 #endif
 static bool                                                    sFrameworksFoundAsDylibs = false;
-#if __x86_64__ && DYLD_SHARED_CACHE_SUPPORT
+#if __x86_64__ && !TARGET_IPHONE_SIMULATOR
 static bool                                                    sHaswell = false;
 #endif
 static std::vector<ImageLoader::DynamicReference> sDynamicReferences;
 static bool                                                    sHaswell = false;
 #endif
 static std::vector<ImageLoader::DynamicReference> sDynamicReferences;
@@ -322,12 +331,15 @@ static bool                                                       sForceStderr = false;
 #endif
 
 
 #endif
 
 
-
 #if SUPPORT_ACCELERATE_TABLES
 static ImageLoaderMegaDylib*           sAllCacheImagesProxy = NULL;
 static bool                                                    sDisableAcceleratorTables = false;
 #endif
 
 #if SUPPORT_ACCELERATE_TABLES
 static ImageLoaderMegaDylib*           sAllCacheImagesProxy = NULL;
 static bool                                                    sDisableAcceleratorTables = false;
 #endif
 
+bool                                                           gUseDyld3 = false;
+static bool                                                    sSkipMain = false;
+static bool                                                    sEnableClosures = false;
+
 //
 // 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
 //
 // 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
@@ -557,7 +569,8 @@ void warn(const char* format, ...)
        va_end(list);
 }
 
        va_end(list);
 }
 
-
+#else
+       extern void vlog(const char* format, va_list list);
 #endif // !TARGET_IPHONE_SIMULATOR     
 
 
 #endif // !TARGET_IPHONE_SIMULATOR     
 
 
@@ -755,16 +768,19 @@ static void notifySingleFromCache(dyld_image_states state, const mach_header* mh
 #endif
 
 static mach_port_t sNotifyReplyPorts[DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT];
 #endif
 
 static mach_port_t sNotifyReplyPorts[DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT];
-
+static bool        sZombieNotifiers[DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT];
 
 static void notifyMonitoringDyld(bool unloading, unsigned portSlot, unsigned imageCount, const dyld_image_info infos[])
 {
 
 static void notifyMonitoringDyld(bool unloading, unsigned portSlot, unsigned imageCount, const dyld_image_info infos[])
 {
+       if ( sZombieNotifiers[portSlot] )
+               return;
+
        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 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
+       unsigned totalSize = (sizeof(dyld_process_info_notify_header) + MAX_TRAILER_SIZE + 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.
        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.
@@ -822,7 +838,7 @@ static void notifyMonitoringDyld(bool unloading, unsigned portSlot, unsigned ima
        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);
        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);
+       kern_return_t sendResult = mach_msg(h, MACH_SEND_MSG | MACH_RCV_MSG | MACH_RCV_TIMEOUT, h->msgh_size, h->msgh_size, sNotifyReplyPorts[portSlot], 5000, 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("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
@@ -832,28 +848,35 @@ static void notifyMonitoringDyld(bool unloading, unsigned portSlot, unsigned ima
                dyld::gProcessInfo->notifyPorts[portSlot] = 0;
                sNotifyReplyPorts[portSlot] = 0;
        }
                dyld::gProcessInfo->notifyPorts[portSlot] = 0;
                sNotifyReplyPorts[portSlot] = 0;
        }
+       else if ( sendResult == MACH_RCV_TIMED_OUT ) {
+               // client took too long, ignore him from now on
+               sZombieNotifiers[portSlot] = true;
+               mach_port_deallocate(mach_task_self(), sNotifyReplyPorts[portSlot]);
+               sNotifyReplyPorts[portSlot] = 0;
+       }
 }
 
 static void notifyMonitoringDyldMain()
 {
        for (int slot=0; slot < DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT; ++slot) {
 }
 
 static void notifyMonitoringDyldMain()
 {
        for (int slot=0; slot < DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT; ++slot) {
-               if ( dyld::gProcessInfo->notifyPorts[slot] != 0 ) {
+               if ( (dyld::gProcessInfo->notifyPorts[slot] != 0 ) && !sZombieNotifiers[slot] ) {
                        if ( sNotifyReplyPorts[slot] == 0 ) {
                                if ( !mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &sNotifyReplyPorts[slot]) )
                                        mach_port_insert_right(mach_task_self(), sNotifyReplyPorts[slot], sNotifyReplyPorts[slot], MACH_MSG_TYPE_MAKE_SEND);
                                //dyld::log("allocated reply port %d\n", sNotifyReplyPorts[slot]);
                        }
                        //dyld::log("found port to send to\n");
                        if ( sNotifyReplyPorts[slot] == 0 ) {
                                if ( !mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &sNotifyReplyPorts[slot]) )
                                        mach_port_insert_right(mach_task_self(), sNotifyReplyPorts[slot], sNotifyReplyPorts[slot], MACH_MSG_TYPE_MAKE_SEND);
                                //dyld::log("allocated reply port %d\n", sNotifyReplyPorts[slot]);
                        }
                        //dyld::log("found port to send to\n");
-                       mach_msg_header_t h;
-                       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                       = DYLD_PROCESS_INFO_NOTIFY_MAIN_ID;
-                       h.msgh_local_port       = sNotifyReplyPorts[slot];
-                       h.msgh_remote_port      = dyld::gProcessInfo->notifyPorts[slot];
-                       h.msgh_reserved         = 0;
-                       h.msgh_size                     = (mach_msg_size_t)sizeof(mach_msg_header_t);
-                       //dyld::log("sending to port[%d]=%d, size=%d, reply port=%d, id=0x%X\n", slot, dyld::gProcessInfo->notifyPorts[slot], h.msgh_size, sNotifyReplyPorts[slot], 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[slot], 100, MACH_PORT_NULL);
-                       //dyld::log("send result = 0x%X, msg_id=%d, msg_size=%d\n", sendResult, h.msgh_id, h.msgh_size);
+                       uint8_t messageBuffer[sizeof(mach_msg_header_t) + MAX_TRAILER_SIZE];
+                       mach_msg_header_t* h = (mach_msg_header_t*)messageBuffer;
+                       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              = DYLD_PROCESS_INFO_NOTIFY_MAIN_ID;
+                       h->msgh_local_port      = sNotifyReplyPorts[slot];
+                       h->msgh_remote_port     = dyld::gProcessInfo->notifyPorts[slot];
+                       h->msgh_reserved        = 0;
+                       h->msgh_size            = (mach_msg_size_t)sizeof(messageBuffer);
+                       //dyld::log("sending to port[%d]=%d, size=%d, reply port=%d, id=0x%X\n", slot, dyld::gProcessInfo->notifyPorts[slot], h->msgh_size, sNotifyReplyPorts[slot], h->msgh_id);
+                       kern_return_t sendResult = mach_msg(h, MACH_SEND_MSG | MACH_RCV_MSG | MACH_RCV_TIMEOUT, h->msgh_size, h->msgh_size, sNotifyReplyPorts[slot], 5000, 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[slot], sNotifyReplyPorts[slot]);
                        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[slot], sNotifyReplyPorts[slot]);
@@ -862,44 +885,24 @@ static void notifyMonitoringDyldMain()
                                dyld::gProcessInfo->notifyPorts[slot] = 0;
                                sNotifyReplyPorts[slot] = 0;
                        }
                                dyld::gProcessInfo->notifyPorts[slot] = 0;
                                sNotifyReplyPorts[slot] = 0;
                        }
+                       else if ( sendResult == MACH_RCV_TIMED_OUT ) {
+                               // client took too long, ignore him from now on
+                               sZombieNotifiers[slot] = true;
+                               mach_port_deallocate(mach_task_self(), sNotifyReplyPorts[slot]);
+                               sNotifyReplyPorts[slot] = 0;
+                       }
                }
        }
 }
 
                }
        }
 }
 
-#define MAX_KERNEL_IMAGES_PER_CALL (100)
-
-static void flushKernelNotifications(bool loading, bool force, std::array<dyld_kernel_image_info_t,MAX_KERNEL_IMAGES_PER_CALL>& kernelInfos, uint32_t &kernelInfoCount) {
-       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;
-       }
-}
-
-static
-void queueKernelNotification(const ImageLoader& image, bool loading, std::array<dyld_kernel_image_info_t,MAX_KERNEL_IMAGES_PER_CALL>& kernelInfos, uint32_t &kernelInfoCount) {
+void notifyKernel(const ImageLoader& image, bool loading) {
        if ( !image.inSharedCache() ) {
        if ( !image.inSharedCache() ) {
+               uint32_t baseCode = loading ? DBG_DYLD_UUID_MAP_A : DBG_DYLD_UUID_UNMAP_A;
+               uuid_t uuid;
                ino_t inode = image.getInode();
                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++;
+               image.getUUID(uuid);
+               dyld3::kdebug_trace_dyld_image(baseCode, (const uuid_t *)&uuid, *(fsobj_id_t*)&inode, {{ image.getDevice(), 0 }}, image.machHeader());
        }
        }
-       flushKernelNotifications(loading, false, kernelInfos, kernelInfoCount);
-}
-
-void notifyKernel(const ImageLoader& image, bool loading) {
-       std::array<dyld_kernel_image_info_t,MAX_KERNEL_IMAGES_PER_CALL> 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)
 }
 
 static void notifySingle(dyld_image_states state, const ImageLoader* image, ImageLoader::InitializerTimingList* timingInfo)
@@ -967,6 +970,7 @@ static void notifySingle(dyld_image_states state, const ImageLoader* image, Imag
                                //dyld::log("deallocated reply port %d\n", sNotifyReplyPorts[slot]);
                                mach_port_deallocate(mach_task_self(), sNotifyReplyPorts[slot]);
                                sNotifyReplyPorts[slot] = 0;
                                //dyld::log("deallocated reply port %d\n", sNotifyReplyPorts[slot]);
                                mach_port_deallocate(mach_task_self(), sNotifyReplyPorts[slot]);
                                sNotifyReplyPorts[slot] = 0;
+                               sZombieNotifiers[slot] = false;
                        }
                }
        }
                        }
                }
        }
@@ -1022,9 +1026,6 @@ static int imageSorter(const void* l, const void* r)
 static void notifyBatchPartial(dyld_image_states state, bool orLater, dyld_image_state_change_handler onlyHandler, bool preflightOnly, bool onlyObjCMappedNotification)
 {
        std::vector<dyld_image_state_change_handler>* handlers = stateToHandlers(state, sBatchHandlers);
 static void notifyBatchPartial(dyld_image_states state, bool orLater, dyld_image_state_change_handler onlyHandler, bool preflightOnly, bool onlyObjCMappedNotification)
 {
        std::vector<dyld_image_state_change_handler>* handlers = stateToHandlers(state, sBatchHandlers);
-       std::array<dyld_kernel_image_info_t,MAX_KERNEL_IMAGES_PER_CALL> 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();
        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();
@@ -1056,12 +1057,11 @@ static void notifyBatchPartial(dyld_image_states state, bool orLater, dyld_image
                                p->imageFileModDate = image->lastModified();
                                // get these registered with the kernel as early as possible
                                if ( state == dyld_image_state_dependents_mapped)
                                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);
+                                       notifyKernel(*image, true);
                                // special case for add_image hook
                                if ( state == dyld_image_state_bound )
                                        notifyAddImageCallbacks(image);
                        }
                                // 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 ) {
                }
 #if SUPPORT_ACCELERATE_TABLES
                if ( sAllCacheImagesProxy != NULL ) {
@@ -1070,7 +1070,7 @@ static void notifyBatchPartial(dyld_image_states state, bool orLater, dyld_image
                        if ( state == dyld_image_state_bound ) {
                                for (ImageCallback callback : sAddImageCallbacks) {
                                        for (unsigned i=0; i < cacheCount; ++i)
                        if ( state == dyld_image_state_bound ) {
                                for (ImageCallback callback : sAddImageCallbacks) {
                                        for (unsigned i=0; i < cacheCount; ++i)
-                                               (*callback)(infos[imageCount+i].imageLoadAddress, sSharedCacheSlide);
+                                               (*callback)(infos[imageCount+i].imageLoadAddress, sSharedCacheLoadInfo.slide);
                                }
                        }
                        imageCount += cacheCount;
                                }
                        }
                        imageCount += cacheCount;
@@ -1792,6 +1792,8 @@ static void paths_dump(const char **paths)
 }
 #endif
 
 }
 #endif
 
+
+
 static void printOptions(const char* argv[])
 {
        uint32_t i = 0;
 static void printOptions(const char* argv[])
 {
        uint32_t i = 0;
@@ -1952,14 +1954,13 @@ void processDyldEnvironmentVariable(const char* key, const char* value, const ch
                        dyld::warn("unknown option to DYLD_SHARED_REGION.  Valid options are: use, private, avoid\n");
                }
        }
                        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) && !sSafeMode  ) {
        else if ( (strcmp(key, "DYLD_SHARED_CACHE_DIR") == 0) && !sSafeMode  ) {
-        sSharedCacheDir = value;
+               sSharedCacheOverrideDir = value;
        }
        }
-       else if ( (strcmp(key, "DYLD_SHARED_CACHE_DONT_VALIDATE") == 0) && !sSafeMode  ) {
-               sSharedCacheIgnoreInodeAndTimeStamp = true;
+       else if ( strcmp(key, "DYLD_USE_CLOSURES") == 0 ) {
+               if ( dyld3::loader::internalInstall() )
+                       sEnableClosures = true;
        }
        }
-#endif
        else if ( strcmp(key, "DYLD_IGNORE_PREBINDING") == 0 ) {
                if ( strcmp(value, "all") == 0 ) {
                        gLinkContext.prebindUsage = ImageLoader::kUseNoPrebinding;
        else if ( strcmp(key, "DYLD_IGNORE_PREBINDING") == 0 ) {
                if ( strcmp(value, "all") == 0 ) {
                        gLinkContext.prebindUsage = ImageLoader::kUseNoPrebinding;
@@ -2002,6 +2003,10 @@ void processDyldEnvironmentVariable(const char* key, const char* value, const ch
                        dyld::log("dyld: could not open DYLD_PRINT_TO_FILE='%s', errno=%d\n", value, errno);
                }
        }
                        dyld::log("dyld: could not open DYLD_PRINT_TO_FILE='%s', errno=%d\n", value, errno);
                }
        }
+       else if ( (strcmp(key, "DYLD_SKIP_MAIN") == 0)) {
+               if ( dyld3::loader::internalInstall() )
+                       sSkipMain = true;
+       }
 #endif
        else {
                dyld::warn("unknown environment variable: %s\n", key);
 #endif
        else {
                dyld::warn("unknown environment variable: %s\n", key);
@@ -2101,10 +2106,17 @@ static void pruneEnvironmentVariables(const char* envp[], const char*** applep)
        checkLoadCommandEnvironmentVariables();
 #endif
 
        checkLoadCommandEnvironmentVariables();
 #endif
 
+    // Are we testing dyld on an internal config?
+    if ( _simple_getenv(envp, "DYLD_SKIP_MAIN") != NULL ) {
+               if ( dyld3::loader::internalInstall() )
+            sSkipMain = true;
+    }
+
        // delete all DYLD_* and LD_LIBRARY_PATH environment variables
        int removedCount = 0;
        const char** d = envp;
        for(const char** s = envp; *s != NULL; s++) {
        // 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;
                }
            if ( (strncmp(*s, "DYLD_", 5) != 0) && (strncmp(*s, "LD_LIBRARY_PATH=", 16) != 0) ) {
                        *d++ = *s;
                }
@@ -2212,7 +2224,7 @@ static void checkEnvironmentVariables(const char* envp[])
 #endif
 }
 
 #endif
 }
 
-#if __x86_64__ && DYLD_SHARED_CACHE_SUPPORT
+#if __x86_64__ && !TARGET_IPHONE_SIMULATOR
 static bool isGCProgram(const macho_header* mh, uintptr_t slide)
 {
        const uint32_t cmd_count = mh->ncmds;
 static bool isGCProgram(const macho_header* mh, uintptr_t slide)
 {
        const uint32_t cmd_count = mh->ncmds;
@@ -2260,6 +2272,12 @@ static void getHostInfo(const macho_header* mainExecutableMH, uintptr_t mainExec
 #elif __ARM_ARCH_7S__
        sHostCPU                = CPU_TYPE_ARM;
        sHostCPUsubtype = CPU_SUBTYPE_ARM_V7S;
 #elif __ARM_ARCH_7S__
        sHostCPU                = CPU_TYPE_ARM;
        sHostCPUsubtype = CPU_SUBTYPE_ARM_V7S;
+#elif __arm64e__
+       sHostCPU                = CPU_TYPE_ARM64;
+       sHostCPUsubtype = CPU_SUBTYPE_ARM64_E;
+#elif __arm64__
+       sHostCPU                = CPU_TYPE_ARM64;
+       sHostCPUsubtype = CPU_SUBTYPE_ARM64_V8;
 #else
        struct host_basic_info info;
        mach_msg_type_number_t count = HOST_BASIC_INFO_COUNT;
 #else
        struct host_basic_info info;
        mach_msg_type_number_t count = HOST_BASIC_INFO_COUNT;
@@ -2271,7 +2289,10 @@ static void getHostInfo(const macho_header* mainExecutableMH, uintptr_t mainExec
        sHostCPUsubtype = info.cpu_subtype;
        mach_port_deallocate(mach_task_self(), hostPort);
   #if __x86_64__
        sHostCPUsubtype = info.cpu_subtype;
        mach_port_deallocate(mach_task_self(), hostPort);
   #if __x86_64__
-       #if DYLD_SHARED_CACHE_SUPPORT
+         // host_info returns CPU_TYPE_I386 even for x86_64.  Override that here so that
+         // we don't need to mask the cpu type later.
+         sHostCPU = CPU_TYPE_X86_64;
+       #if !TARGET_IPHONE_SIMULATOR
          sHaswell = (sHostCPUsubtype == CPU_SUBTYPE_X86_64_H);
          // <rdar://problem/18528074> x86_64h: Fall back to the x86_64 slice if an app requires GC.
          if ( sHaswell ) {
          sHaswell = (sHostCPUsubtype == CPU_SUBTYPE_X86_64_H);
          // <rdar://problem/18528074> x86_64h: Fall back to the x86_64 slice if an app requires GC.
          if ( sHaswell ) {
@@ -2288,12 +2309,21 @@ static void getHostInfo(const macho_header* mainExecutableMH, uintptr_t mainExec
 #endif
 }
 
 #endif
 }
 
-static void checkSharedRegionDisable()
+static void checkSharedRegionDisable(const mach_header* mainExecutableMH)
 {
 #if __MAC_OS_X_VERSION_MIN_REQUIRED
 {
 #if __MAC_OS_X_VERSION_MIN_REQUIRED
-       // if main executable has segments that overlap the shared region, 
+       // if main executable has segments that overlap the shared region,
        // then disable using 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)) ) {
+       dyld3::MachOParser parser(mainExecutableMH);
+       uintptr_t slide = parser.getSlide();
+       dyld3::launch_cache::MemoryRange sharedRegion = { (void*)(long)(SHARED_REGION_BASE),  SHARED_REGION_SIZE };
+       __block bool disable = false;
+       parser.forEachSegment(^(const char *segName, uint32_t fileOffset, uint32_t fileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, bool &stop) {
+               dyld3::launch_cache::MemoryRange segRegion = { (void*)(long)(vmAddr+slide),  vmSize };
+               if ( segRegion.intersects(sharedRegion) )
+                       disable = true;
+       });
+       if ( disable ) {
                gLinkContext.sharedRegionMode = ImageLoader::kDontUseSharedRegion;
                if ( gLinkContext.verboseMapping )
                        dyld::warn("disabling shared region because main executable overlaps\n");
                gLinkContext.sharedRegionMode = ImageLoader::kDontUseSharedRegion;
                if ( gLinkContext.verboseMapping )
                        dyld::warn("disabling shared region because main executable overlaps\n");
@@ -2305,7 +2335,7 @@ static void checkSharedRegionDisable()
        }
 #endif
 #endif
        }
 #endif
 #endif
-       // iPhoneOS cannot run without shared region
+       // iOS cannot run without shared region
 }
 
 bool validImage(const ImageLoader* possibleImage)
 }
 
 bool validImage(const ImageLoader* possibleImage)
@@ -2503,6 +2533,21 @@ static const cpu_subtype_t kARM[kARM_RowCount][9] = {
 };
 #endif
 
 };
 #endif
 
+#if __arm64__
+//
+//     ARM64 sub-type lists
+//
+const int kARM64_RowCount = 2;
+static const cpu_subtype_t kARM64[kARM64_RowCount][4] = {
+
+       // armv64e can run: 64e, 64
+       {  CPU_SUBTYPE_ARM64_E, CPU_SUBTYPE_ARM64_V8, CPU_SUBTYPE_ARM64_ALL, CPU_SUBTYPE_END_OF_LIST },
+
+       // armv64 can run: 64
+       {  CPU_SUBTYPE_ARM64_V8, CPU_SUBTYPE_ARM64_ALL, CPU_SUBTYPE_END_OF_LIST },
+};
+#endif
+
 #if __x86_64__
 //      
 //     x86_64 sub-type lists
 #if __x86_64__
 //      
 //     x86_64 sub-type lists
@@ -2511,10 +2556,10 @@ 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
 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_LIB64|CPU_SUBTYPE_X86_64_H, CPU_SUBTYPE_LIB64|CPU_SUBTYPE_X86_64_ALL, CPU_SUBTYPE_X86_64_ALL,  CPU_SUBTYPE_END_OF_LIST },
+       { 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
 
        // x86_64 can run: x86_64(lib) and x86_64
-       { CPU_SUBTYPE_X86_64_ALL, CPU_SUBTYPE_LIB64|CPU_SUBTYPE_X86_64_ALL, CPU_SUBTYPE_END_OF_LIST },
+       { CPU_SUBTYPE_X86_64_ALL, (cpu_subtype_t)(CPU_SUBTYPE_LIB64|CPU_SUBTYPE_X86_64_ALL), CPU_SUBTYPE_END_OF_LIST },
 
 };
 #endif
 
 };
 #endif
@@ -2532,6 +2577,14 @@ static const cpu_subtype_t* findCPUSubtypeList(cpu_type_t cpu, cpu_subtype_t sub
                        }
                        break;
 #endif
                        }
                        break;
 #endif
+#if __arm64__
+               case CPU_TYPE_ARM64:
+                       for (int i=0; i < kARM64_RowCount ; ++i) {
+                               if ( kARM64[i][0] == subtype )
+                                       return kARM64[i];
+                       }
+                       break;
+#endif
 #if __x86_64__
                case CPU_TYPE_X86_64:
                        for (int i=0; i < kX86_64_RowCount ; ++i) {
 #if __x86_64__
                case CPU_TYPE_X86_64:
                        for (int i=0; i < kX86_64_RowCount ; ++i) {
@@ -2595,6 +2648,15 @@ static bool fatFindRunsOnAllCPUs(cpu_type_t cpu, const fat_header* fh, uint64_t*
                                        }
                                        break;
 #endif
                                        }
                                        break;
 #endif
+#if __arm64__
+                               case CPU_TYPE_ARM64:
+                                       if ( (cpu_subtype_t)OSSwapBigToHostInt32(archs[i].cpusubtype) == CPU_SUBTYPE_ARM64_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 ) {
 #if __x86_64__
                                case CPU_TYPE_X86_64:
                                        if ( (cpu_subtype_t)OSSwapBigToHostInt32(archs[i].cpusubtype) == CPU_SUBTYPE_X86_64_ALL ) {
@@ -2687,7 +2749,7 @@ static bool fatFindBest(const fat_header* fh, uint64_t* offset, uint64_t* len)
        const cpu_type_t cpu = sMainExecutableMachHeader->cputype;
 
        // We only know the subtype to use if the main executable cpu type matches the host
        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 ) {
+       if ( cpu == sHostCPU ) {
                // get preference ordered list of subtypes
                const cpu_subtype_t* subTypePreferenceList = findCPUSubtypeList(cpu, sHostCPUsubtype);
        
                // get preference ordered list of subtypes
                const cpu_subtype_t* subTypePreferenceList = findCPUSubtypeList(cpu, sHostCPUsubtype);
        
@@ -2733,7 +2795,7 @@ bool isCompatibleMachO(const uint8_t* firstPage, const char* path)
        const mach_header* mh = (mach_header*)firstPage;
        if ( mh->magic == sMainExecutableMachHeader->magic ) {
                if ( mh->cputype == sMainExecutableMachHeader->cputype ) {
        const mach_header* mh = (mach_header*)firstPage;
        if ( mh->magic == sMainExecutableMachHeader->magic ) {
                if ( mh->cputype == sMainExecutableMachHeader->cputype ) {
-                       if ( (mh->cputype & CPU_TYPE_MASK) == sHostCPU ) {
+                       if ( mh->cputype == sHostCPU ) {
                                // get preference ordered list of subtypes that this machine can use
                                const cpu_subtype_t* subTypePreferenceList = findCPUSubtypeList(mh->cputype, sHostCPUsubtype);
                                if ( subTypePreferenceList != NULL ) {
                                // get preference ordered list of subtypes that this machine can use
                                const cpu_subtype_t* subTypePreferenceList = findCPUSubtypeList(mh->cputype, sHostCPUsubtype);
                                if ( subTypePreferenceList != NULL ) {
@@ -2789,104 +2851,47 @@ static ImageLoaderMachO* instantiateFromLoadedImage(const macho_header* mh, uint
        throw "main executable not a known format";
 }
 
        throw "main executable not a known format";
 }
 
-#if DYLD_SHARED_CACHE_SUPPORT
-
-#if __IPHONE_OS_VERSION_MIN_REQUIRED
+#if SUPPORT_ACCELERATE_TABLES
 static bool dylibsCanOverrideCache()
 {
 static bool dylibsCanOverrideCache()
 {
-       uint32_t devFlags = *((uint32_t*)_COMM_PAGE_DEV_FIRM);
-       if ( (devFlags & 1) == 0 )
+       if ( !dyld3::loader::internalInstall() )
                return false;
                return false;
-       return ( (sSharedCache != NULL) && (sSharedCache->cacheType == kDyldSharedCacheTypeDevelopment) );
+       return ( (sSharedCacheLoadInfo.loadAddress != nullptr) && (sSharedCacheLoadInfo.loadAddress->header.cacheType == kDyldSharedCacheTypeDevelopment) );
 }
 #endif
 
 }
 #endif
 
-static bool findInSharedCacheImage(const char* path, bool searchByPath, const struct stat* stat_buf, const macho_header** mh, const char** pathInCache, long* slide)
+const void* imMemorySharedCacheHeader()
 {
 {
-       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
+       return sSharedCacheLoadInfo.loadAddress;
+}
 
 
-               // 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) )  {
-                                       // <rdar://problem/22056997> 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
-               }       
+
+const char* getStandardSharedCacheFilePath()
+{
+       if ( sSharedCacheLoadInfo.loadAddress != nullptr )
+               return sSharedCacheLoadInfo.path;
+       else
+               return nullptr;
+}
+
+
+static bool findInSharedCacheImage(const char* path, bool searchByPath, const struct stat* stat_buf, const macho_header** mh, const char** pathInCache, long* slide)
+{
+       dyld3::SharedCacheFindDylibResults results;
+       if ( dyld3::findInSharedCacheImage(sSharedCacheLoadInfo, path, &results) ) {
+               *mh                      = (macho_header*)results.mhInCache;
+               *pathInCache = results.pathInCache;
+               *slide       = results.slideInCache;
+               return true;
        }
        return false;
 }
 
 bool inSharedCache(const char* path)
 {
        }
        return false;
 }
 
 bool inSharedCache(const char* path)
 {
-       const macho_header* mhInCache;
-       const char*                     pathInCache;
-       long                            slide;
-       return findInSharedCacheImage(path, true, NULL, &mhInCache, &pathInCache, &slide);
+       return dyld3::pathIsInSharedCacheImage(sSharedCacheLoadInfo, path);
 }
 
 }
 
-#endif
 
 static ImageLoader* checkandAddImage(ImageLoader* image, const LoadContext& context)
 {
 
 static ImageLoader* checkandAddImage(ImageLoader* image, const LoadContext& context)
 {
@@ -2978,7 +2983,7 @@ static ImageLoader* loadPhase6(int fd, const struct stat& stat_buf, const char*
        
        // min mach-o file is 4K
        if ( fileLength < 4096 ) {
        
        // min mach-o file is 4K
        if ( fileLength < 4096 ) {
-               if ( pread(fd, firstPages, fileLength, 0) != (ssize_t)fileLength )
+               if ( pread(fd, firstPages, (size_t)fileLength, 0) != (ssize_t)fileLength )
                        throwf("pread of short file failed: %d", errno);
                shortPage = true;
        } 
                        throwf("pread of short file failed: %d", errno);
                shortPage = true;
        } 
@@ -3104,143 +3109,92 @@ static ImageLoader* loadPhase5open(const char* path, const LoadContext& context,
 }
 
 
 }
 
 
-#if __MAC_OS_X_VERSION_MIN_REQUIRED    
-static ImageLoader* loadPhase5load(const char* path, const char* orgPath, const LoadContext& context, unsigned& cacheIndex, std::vector<const char*>* 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<const char*>* 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<const char*>* exceptions)
 {
        //dyld::log("%s(%s, %p)\n", __func__ , path, exceptions);
 
 // try to open file
 static ImageLoader* loadPhase5load(const char* path, const char* orgPath, const LoadContext& context, unsigned& cacheIndex, std::vector<const char*>* 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 SUPPORT_ACCELERATE_TABLES
        if ( sAllCacheImagesProxy != NULL ) {
                if ( sAllCacheImagesProxy->hasDylib(path, &cacheIndex) )
                        return sAllCacheImagesProxy;
        }
        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) ) {
+#endif
+#if TARGET_IPHONE_SIMULATOR
+       // in simulators, 'path' has DYLD_ROOT_PATH prepended, but cache index does not have the prefix, so use orgPath
+       const char* pathToFindInCache = orgPath;
+#else
+       const char* pathToFindInCache = path;
+#endif
+       uint statErrNo;
+       struct stat statBuf;
+       bool didStat = false;
+       bool existsOnDisk;
+       dyld3::SharedCacheFindDylibResults shareCacheResults;
+       if ( dyld3::findInSharedCacheImage(sSharedCacheLoadInfo, pathToFindInCache, &shareCacheResults) ) {
                // see if this image in the cache was already loaded via a different path
                for (std::vector<ImageLoader*>::iterator it=sAllImages.begin(); it != sAllImages.end(); ++it) {
                        ImageLoader* anImage = *it;
                // see if this image in the cache was already loaded via a different path
                for (std::vector<ImageLoader*>::iterator it=sAllImages.begin(); it != sAllImages.end(); ++it) {
                        ImageLoader* anImage = *it;
-                       if ( (const macho_header*)anImage->machHeader() == mhInCache )
+                       if ( (const mach_header*)anImage->machHeader() == shareCacheResults.mhInCache )
                                return anImage;
                }
                                return anImage;
                }
-               // do nothing if not already loaded and if RTLD_NOLOAD 
+               // if RTLD_NOLOAD, do nothing if not already loaded
                if ( context.dontLoad )
                        return NULL;
                if ( context.dontLoad )
                        return NULL;
-               // nope, so instantiate a new image from dyld shared cache
-               // <rdar://problem/7014995> 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);
+               bool useCache = false;
+               if ( shareCacheResults.imageData == nullptr ) {
+                       // HACK to support old caches
+                       existsOnDisk = ( my_stat(path, &statBuf) == 0 );
+                       didStat = true;
+                       statErrNo = errno;
+                       useCache = !existsOnDisk;
+               }
+               else {
+                       // <rdar://problem/7014995> zero out stat buffer so mtime, etc are zero for items from the shared cache
+                       bzero(&statBuf, sizeof(statBuf));
+                       dyld3::launch_cache::Image image(shareCacheResults.imageData);
+                       if ( image.overridableDylib() ) {
+                               existsOnDisk = ( my_stat(path, &statBuf) == 0 );
+                               didStat = true;
+                               statErrNo = errno;
+                               if ( sSharedCacheLoadInfo.loadAddress->header.dylibsExpectedOnDisk ) {
+                                       if ( (image.fileModTime() == statBuf.st_mtime) && (image.fileINode() == statBuf.st_ino) )
+                                               useCache = true;
+                               }
+                               else {
+                                       if ( !existsOnDisk )
+                                               useCache = true;
+                               }
+                       }
+                       else {
+                               useCache = true;
+                       }
+               }
+               if ( useCache ) {
+                       ImageLoader* imageLoader = ImageLoaderMachO::instantiateFromCache((macho_header*)shareCacheResults.mhInCache, shareCacheResults.pathInCache, shareCacheResults.slideInCache, statBuf, gLinkContext);
+                       return checkandAddImage(imageLoader, 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;
+
+       // not in cache or cache not usable
+       if ( !didStat ) {
+               existsOnDisk = ( my_stat(path, &statBuf) == 0 );
+               statErrNo = errno;
        }
        }
-#else
-       image = loadPhase5stat(path, context, &stat_buf, &statErrNo, &imageFound, exceptions);
-       if ( imageFound )
-               return image;
-#endif
+       if ( existsOnDisk ) {
+               // in case image was renamed or found via symlinks, check for inode match
+               ImageLoader* imageLoader = findLoadedImage(statBuf);
+               if ( imageLoader != NULL )
+                       return imageLoader;
+               // do nothing if not already loaded and if RTLD_NOLOAD 
+               if ( context.dontLoad )
+                       return NULL;
+               // try opening file
+               imageLoader = loadPhase5open(path, context, statBuf, exceptions);
+               if ( imageLoader != NULL )
+                       return imageLoader;
+       }
+
        // just return NULL if file not found, but record any other errors
        if ( (statErrNo != ENOENT) && (statErrNo != 0) ) {
                if ( (statErrNo == EPERM) && sandboxBlockedStat(path) )
        // just return NULL if file not found, but record any other errors
        if ( (statErrNo != ENOENT) && (statErrNo != 0) ) {
                if ( (statErrNo == EPERM) && sandboxBlockedStat(path) )
@@ -3250,8 +3204,6 @@ static ImageLoader* loadPhase5load(const char* path, const char* orgPath, const
        }
        return NULL;
 }
        }
        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)
 
 // look for path match with existing loaded images
 static ImageLoader* loadPhase5check(const char* path, const char* orgPath, const LoadContext& context)
@@ -3581,15 +3533,13 @@ static ImageLoader* loadPhase0(const char* path, const char* orgPath, const Load
        return loadPhase1(path, orgPath, context, cacheIndex, exceptions);
 }
 
        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
+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;
+}
 
 //
 // Given all the DYLD_ environment variables, the general case for loading libraries
 
 //
 // Given all the DYLD_ environment variables, the general case for loading libraries
@@ -3641,13 +3591,11 @@ ImageLoader* load(const char* path, const LoadContext& context, unsigned& cacheI
                for (std::vector<const char*>::iterator it = exceptions.begin(); it != exceptions.end(); ++it) {
                        free((void*)(*it));
                }
                for (std::vector<const char*>::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;
                }
                // 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 ) {
                return image;
        }
        else if ( exceptions.size() == 0 ) {
@@ -3676,728 +3624,84 @@ ImageLoader* load(const char* path, const LoadContext& context, unsigned& cacheI
 
 
 
 
 
 
-#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"
+static void mapSharedCache()
+{
+       dyld3::SharedCacheOptions opts;
+       opts.cacheDirOverride   = sSharedCacheOverrideDir;
+       opts.forcePrivate               = (gLinkContext.sharedRegionMode == ImageLoader::kUsePrivateSharedRegion);
+#if __x86_64__ && !TARGET_IPHONE_SIMULATOR
+       opts.useHaswell                 = sHaswell;
+#else
+       opts.useHaswell                 = false;
 #endif
 #endif
+       opts.verbose                    = gLinkContext.verboseMapping;
+       loadDyldCache(opts, &sSharedCacheLoadInfo);
 
 
+       // update global state
+       if ( sSharedCacheLoadInfo.loadAddress != nullptr ) {
+               dyld::gProcessInfo->processDetachedFromSharedRegion = opts.forcePrivate;
+               dyld::gProcessInfo->sharedCacheSlide                = sSharedCacheLoadInfo.slide;
+               dyld::gProcessInfo->sharedCacheBaseAddress          = (unsigned long)sSharedCacheLoadInfo.loadAddress;
+               sSharedCacheLoadInfo.loadAddress->getUUID(dyld::gProcessInfo->sharedCacheUUID);
+               dyld3::kdebug_trace_dyld_image(DBG_DYLD_UUID_SHARED_CACHE_A, (const uuid_t *)&dyld::gProcessInfo->sharedCacheUUID[0], {0,0}, {{ 0, 0 }}, (const mach_header *)sSharedCacheLoadInfo.loadAddress);
+       }
 
 
-static int __attribute__((noinline)) _shared_region_check_np(uint64_t* start_address)
-{
-       if ( gLinkContext.sharedRegionMode == ImageLoader::kUseSharedRegion ) 
-               return syscall(294, start_address);
-       return -1;
+//#if __IPHONE_OS_VERSION_MIN_REQUIRED && !TARGET_IPHONE_SIMULATOR
+// RAM disk booting does not have shared cache yet
+// Don't make lack of a shared cache fatal in that case
+//     if ( sSharedCacheLoadInfo.loadAddress == nullptr ) {
+//             if ( sSharedCacheLoadInfo.errorMessage != nullptr )
+//                     halt(sSharedCacheLoadInfo.errorMessage);
+//             else
+//                     halt("error loading dyld shared cache");
+//     }
+//#endif
 }
 
 
 }
 
 
-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;
-       }
+// 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);
 }
 
 
 }
 
 
-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)
+ImageLoader* loadFromMemory(const uint8_t* mem, uint64_t len, const char* moduleName)
 {
 {
-       // 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);
-               // <rdar://problem/12891874> 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<<k) ) {
-                                                       uintptr_t* p = (uintptr_t*)(page + j*8*4 + k*4);
-                                                       uintptr_t value = *p;
-                                                       //dyld::log("        *%p was 0x%lX will be 0x%lX\n", p, value, value+sSharedCacheSlide);
-                                                       *p = value + slide;
-                                               }
-                                       }
-                               }
-                       }
-               }
-       }
-
-       // succesfully mapped shared cache for just this process
-       gLinkContext.sharedRegionMode = ImageLoader::kUsePrivateSharedRegion;
-       
-       return 0;
-}
-
-
-const void*    imMemorySharedCacheHeader()
-{
-       return sSharedCache;
-}
-
-const char* getStandardSharedCacheFilePath()
-{
-#if __IPHONE_OS_VERSION_MIN_REQUIRED
-       return IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME;
-#else
-  #if __x86_64__
-       if ( sHaswell ) {
-               const char* path2 = MACOSX_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME_H;
-               struct stat statBuf;
-               if ( my_stat(path2, &statBuf) == 0 )
-                       return path2;
-       }
-  #endif
-       return MACOSX_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME;
-#endif
-}
-
-int openSharedCacheFile()
-{
-       char path[MAXPATHLEN];
-       strlcpy(path, sSharedCacheDir, MAXPATHLEN);
-       strlcat(path, "/", MAXPATHLEN);
-#if __x86_64__
-       if ( sHaswell ) {
-               strlcat(path, DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME_H, MAXPATHLEN);
-               int fd = my_open(path, O_RDONLY, 0);
-               if ( fd != -1 ) {
-                       if ( gLinkContext.verboseMapping ) 
-                               dyld::log("dyld: Mapping%s shared cache from %s\n", (gLinkContext.sharedRegionMode == ImageLoader::kUsePrivateSharedRegion) ? " private": "", path);
-                       return fd;
-               }
-               strlcpy(path, sSharedCacheDir, MAXPATHLEN);
-       }
-#endif
-       strlcat(path, DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME, MAXPATHLEN);
-#if __IPHONE_OS_VERSION_MIN_REQUIRED
-       struct stat enableStatBuf;
-       struct stat devCacheStatBuf;
-       struct stat prodCacheStatBuf;
-       if ( ((my_stat(IPHONE_DYLD_SHARED_CACHE_DIR "enable-dylibs-to-override-cache", &enableStatBuf) == 0)
-                       && (enableStatBuf.st_size < ENABLE_DYLIBS_TO_OVERRIDE_CACHE_SIZE)
-                       && (my_stat(IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME DYLD_SHARED_CACHE_DEVELOPMENT_EXT, &devCacheStatBuf) == 0))
-               || (my_stat(IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME, &prodCacheStatBuf) != 0))
-               strlcat(path, DYLD_SHARED_CACHE_DEVELOPMENT_EXT, MAXPATHLEN);
-#endif
-       if ( gLinkContext.verboseMapping )
-               dyld::log("dyld: Mapping%s shared cache from %s\n", (gLinkContext.sharedRegionMode == ImageLoader::kUsePrivateSharedRegion) ? " private": "", path);
-       return my_open(path, O_RDONLY, 0);
-}
-
-
-static void getCacheBounds(uint32_t mappingsCount, const shared_file_mapping_np mappings[], uint64_t& lowAddress, uint64_t& highAddress)
-{
-       lowAddress = 0;
-       highAddress = 0;
-       for(uint32_t i=0; i < mappingsCount; ++i) {
-               if ( lowAddress == 0 ) {
-                       lowAddress = mappings[i].sfm_address;
-                       highAddress = mappings[i].sfm_address + mappings[i].sfm_size;
-               }
-               else {
-                       if ( mappings[i].sfm_address < lowAddress )
-                               lowAddress = mappings[i].sfm_address;
-                       if ( (mappings[i].sfm_address + mappings[i].sfm_size) > 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__
-       // <rdar://problem/20848977> 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;
-                       }
-               }
-               dyld::gProcessInfo->sharedCacheBaseAddress = cacheBaseAddress;
-               // 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::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;
-                       task_register_dyld_shared_cache_image_info(mach_task_self(), kernelCacheInfo, false, false);
-               }
-       }
-       else {
-#if __i386__ || __x86_64__
-               // <rdar://problem/5925940> 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
-                                                       // <rdar://problem/23188073> 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 ) {
-                                                       // <rdar://problem/8611968> 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;
-                                                                       }
-                                                               }
-                                                               task_register_dyld_shared_cache_image_info(mach_task_self(), kernelCacheInfo, false, privateSharedCache);
-                                                       }
-                                               }
-                                               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";
-               }
+       // 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
        }
 
        // try each loader
@@ -4441,7 +3745,7 @@ void registerAddCallback(ImageCallback func)
                dyld_image_info infos[allImagesCount()+1];
                unsigned cacheCount = sAllCacheImagesProxy->appendImagesToNotify(dyld_image_state_bound, true, infos);
                for (unsigned i=0; i < cacheCount; ++i) {
                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);
+                       (*func)(infos[i].imageLoadAddress, sSharedCacheLoadInfo.slide);
                }
        }
 #endif
                }
        }
 #endif
@@ -4790,19 +4094,23 @@ void registerObjCNotifiers(_dyld_objc_notify_mapped mapped, _dyld_objc_notify_in
        catch (const char* msg) {
                // ignore request to abort during registration
        }
        catch (const char* msg) {
                // ignore request to abort during registration
        }
+
+       // <rdar://problem/32209809> call 'init' function on all images already init'ed (below libSystem)
+       for (std::vector<ImageLoader*>::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) {
+               ImageLoader* image = *it;
+               if ( (image->getState() == dyld_image_state_initialized) && image->notifyObjC() ) {
+                       (*sNotifyObjCInit)(image->getRealPath(), image->machHeader());
+               }
+       }
 }
 
 bool sharedCacheUUID(uuid_t uuid)
 {
 }
 
 bool sharedCacheUUID(uuid_t uuid)
 {
-#if DYLD_SHARED_CACHE_SUPPORT
-       if ( sSharedCache == NULL )
+       if ( sSharedCacheLoadInfo.loadAddress == nullptr )
                return false;
 
                return false;
 
-       memcpy(uuid, sSharedCache->uuid, 16);
+       sSharedCacheLoadInfo.loadAddress->getUUID(uuid);
        return true;
        return true;
-#else
-       return false;
-#endif
 }
 
 #if SUPPORT_ACCELERATE_TABLES
 }
 
 #if SUPPORT_ACCELERATE_TABLES
@@ -4939,9 +4247,7 @@ static void setContext(const macho_header* mainExecutableMH, int argc, const cha
        gLinkContext.printAllDepths                     = &printAllDepths;
        gLinkContext.imageCount                         = &imageCount;
        gLinkContext.setNewProgramVars          = &setNewProgramVars;
        gLinkContext.printAllDepths                     = &printAllDepths;
        gLinkContext.imageCount                         = &imageCount;
        gLinkContext.setNewProgramVars          = &setNewProgramVars;
-#if DYLD_SHARED_CACHE_SUPPORT
        gLinkContext.inSharedCache                      = &inSharedCache;
        gLinkContext.inSharedCache                      = &inSharedCache;
-#endif
        gLinkContext.setErrorStrings            = &setErrorStrings;
 #if SUPPORT_OLD_CRT_INITIALIZATION
        gLinkContext.setRunInitialzersOldWay= &setRunInitialzersOldWay;
        gLinkContext.setErrorStrings            = &setErrorStrings;
 #if SUPPORT_OLD_CRT_INITIALIZATION
        gLinkContext.setRunInitialzersOldWay= &setRunInitialzersOldWay;
@@ -4970,7 +4276,7 @@ static void setContext(const macho_header* mainExecutableMH, int argc, const cha
        gLinkContext.dynamicInterposeCount      = 0;
        gLinkContext.prebindUsage                       = ImageLoader::kUseAllPrebinding;
 #if TARGET_IPHONE_SIMULATOR
        gLinkContext.dynamicInterposeCount      = 0;
        gLinkContext.prebindUsage                       = ImageLoader::kUseAllPrebinding;
 #if TARGET_IPHONE_SIMULATOR
-       gLinkContext.sharedRegionMode           = ImageLoader::kDontUseSharedRegion;
+       gLinkContext.sharedRegionMode           = ImageLoader::kUsePrivateSharedRegion;
 #else
        gLinkContext.sharedRegionMode           = ImageLoader::kUseSharedRegion;
 #endif
 #else
        gLinkContext.sharedRegionMode           = ImageLoader::kUseSharedRegion;
 #endif
@@ -4983,6 +4289,7 @@ static void setContext(const macho_header* mainExecutableMH, int argc, const cha
 // Its presences means that the binary wants to have DYLD ignore
 // DYLD_ environment variables.
 //
 // Its presences means that the binary wants to have DYLD ignore
 // DYLD_ environment variables.
 //
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
 static bool hasRestrictedSegment(const macho_header* mh)
 {
        const uint32_t cmd_count = mh->ncmds;
 static bool hasRestrictedSegment(const macho_header* mh)
 {
        const uint32_t cmd_count = mh->ncmds;
@@ -5011,8 +4318,9 @@ static bool hasRestrictedSegment(const macho_header* mh)
                
        return false;
 }
                
        return false;
 }
+#endif
 
 
-#if __IPHONE_OS_VERSION_MIN_REQUIRED
+#if __IPHONE_OS_VERSION_MIN_REQUIRED && !TARGET_IPHONE_SIMULATOR
 static bool isFairPlayEncrypted(const macho_header* mh)
 {
        const uint32_t cmd_count = mh->ncmds;
 static bool isFairPlayEncrypted(const macho_header* mh)
 {
        const uint32_t cmd_count = mh->ncmds;
@@ -5070,7 +4378,6 @@ static bool getDylibVersionAndInstallname(const char* dylibPath, uint32_t* versi
        uint8_t firstPage[4096];
        const macho_header* mh = (macho_header*)firstPage;
        if ( !readFirstPage(dylibPath, firstPage) ) {
        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;
                // If file cannot be read, check to see if path is in shared cache
                const macho_header* mhInCache;
                const char*                     pathInCache;
@@ -5078,9 +4385,6 @@ static bool getDylibVersionAndInstallname(const char* dylibPath, uint32_t* versi
                if ( !findInSharedCacheImage(dylibPath, true, NULL, &mhInCache, &pathInCache, &slideInCache) )
                        return false;
                mh = mhInCache;
                if ( !findInSharedCacheImage(dylibPath, true, NULL, &mhInCache, &pathInCache, &slideInCache) )
                        return false;
                mh = mhInCache;
-       #else
-               return false;
-       #endif
        }
 
        // check mach-o header
        }
 
        // check mach-o header
@@ -5351,13 +4655,13 @@ static void loadInsertedDylib(const char* path)
 //
 static void configureProcessRestrictions(const macho_header* mainExecutableMH)
 {
 //
 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 TARGET_IPHONE_SIMULATOR
        sEnvMode = envAll;
        gLinkContext.requireCodeSignature = true;
 #elif __IPHONE_OS_VERSION_MIN_REQUIRED
        sEnvMode = envNone;
        gLinkContext.requireCodeSignature = true;
+    uint32_t flags;
        if ( csops(0, CS_OPS_STATUS, &flags, sizeof(flags)) != -1 ) {
                if ( flags & CS_ENFORCEMENT ) {
                        if ( flags & CS_GET_TASK_ALLOW ) {
        if ( csops(0, CS_OPS_STATUS, &flags, sizeof(flags)) != -1 ) {
                if ( flags & CS_ENFORCEMENT ) {
                        if ( flags & CS_GET_TASK_ALLOW ) {
@@ -5392,6 +4696,7 @@ static void configureProcessRestrictions(const macho_header* mainExecutableMH)
                gLinkContext.processIsRestricted = true;
        }
        bool usingSIP = (csr_check(CSR_ALLOW_TASK_FOR_PID) != 0);
                gLinkContext.processIsRestricted = true;
        }
        bool usingSIP = (csr_check(CSR_ALLOW_TASK_FOR_PID) != 0);
+    uint32_t flags;
        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) && usingSIP ) {
        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) && usingSIP ) {
@@ -5441,9 +4746,14 @@ static void addDyldImageToUUIDList()
        }
 }
 
        }
 }
 
-void notifyKernelAboutDyld()
+void notifyKernelAboutImage(const struct macho_header* mh, const char* fileInfo)
 {
 {
-       const struct macho_header* mh = (macho_header*)&__dso_handle;
+       const char *endptr = nullptr;
+       uint64_t fsid_scalar = hexToUInt64(fileInfo, &endptr);
+       uint64_t fsobj_id_scalar = 0;
+       if (endptr != nullptr) {
+               fsobj_id_scalar = hexToUInt64(endptr+1, &endptr);
+       }
        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 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;
@@ -5452,14 +4762,7 @@ void notifyKernelAboutDyld()
                        case LC_UUID: {
                                // Add dyld to the kernel image info
                                uuid_command* uc = (uuid_command*)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;
-                               task_register_dyld_image_infos(mach_task_self(), &kernelInfo, 1);
+                               dyld3::kdebug_trace_dyld_image(DBG_DYLD_UUID_MAP_A, (const uuid_t *)&uc->uuid[0], *reinterpret_cast<fsobj_id_t *>(&fsobj_id_scalar), *reinterpret_cast<fsid_t *>(&fsid_scalar), (const mach_header *)mh);
                                return;
                        }
                }
                                return;
                        }
                }
@@ -5473,7 +4776,7 @@ 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 = {
 typedef int (*ioctl_proc_t)(int, unsigned long, void*);
 static void* getProcessInfo() { return dyld::gProcessInfo; }
 static SyscallHelpers sSysCalls = {
-               7,
+               8,
                // added in version 1
                (open_proc_t)&open, 
                &close, 
                // added in version 1
                (open_proc_t)&open, 
                &close, 
@@ -5527,7 +4830,12 @@ static SyscallHelpers sSysCalls = {
                &task_get_dyld_image_infos,
                &task_register_dyld_shared_cache_image_info,
                &task_register_dyld_set_dyld_state,
                &task_get_dyld_image_infos,
                &task_register_dyld_shared_cache_image_info,
                &task_register_dyld_set_dyld_state,
-               &task_register_dyld_get_process_state
+               &task_register_dyld_get_process_state,
+               // Added in version 8
+               &task_info,
+               &thread_info,
+               &kdebug_is_enabled,
+               &kdebug_trace
 };
 
 __attribute__((noinline))
 };
 
 __attribute__((noinline))
@@ -5571,7 +4879,7 @@ static const char* useSimulatorDyld(int fd, const macho_header* mainExecutableMH
                        return "pread(dyld_sim) failed";
        }
        else if ( !isCompatibleMachO(firstPage, dyldPath) ) {
                        return "pread(dyld_sim) failed";
        }
        else if ( !isCompatibleMachO(firstPage, dyldPath) ) {
-               return "dyld_sim not compatible mach-o";
+               return "dyld_sim is not compatible with the loaded process, likely due to architecture mismatch";
        }
        
        // calculate total size of dyld segments
        }
        
        // calculate total size of dyld segments
@@ -5694,7 +5002,6 @@ static const char* useSimulatorDyld(int fd, const macho_header* mainExecutableMH
        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);
 
        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));
        // 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));
@@ -5743,6 +5050,563 @@ static const char* useSimulatorDyld(int fd, const macho_header* mainExecutableMH
 }
 #endif
 
 }
 #endif
 
+// 
+// If the DYLD_SKIP_MAIN environment is set to 1, dyld will return the 
+// address of this function instead of main() in the target program which 
+// __dyld_start jumps to. Useful for qualifying dyld itself.
+//
+int
+fake_main()
+{
+       return 0;
+}
+
+
+
+
+static bool envVarMatches(dyld3::launch_cache::Closure mainClosure, const char* envp[], const char* varName)
+{
+       __block const char* valueFromClosure = nullptr;
+       mainClosure.forEachEnvVar(^(const char* keyEqualValue, bool& stop) {
+               size_t keyLen = strlen(varName);
+               if ( (strncmp(varName, keyEqualValue, keyLen) == 0) && (keyEqualValue[keyLen] == '=') ) {
+                       valueFromClosure = &keyEqualValue[keyLen+1];
+                       stop = true;
+               }
+       });
+
+       const char* valueFromEnv = _simple_getenv(envp, varName);
+
+       bool inClosure = (valueFromClosure != nullptr);
+       bool inEnv     = (valueFromEnv != nullptr);
+       if ( inClosure != inEnv )
+               return false;
+       if ( !inClosure && !inEnv )
+               return true;
+       return ( strcmp(valueFromClosure, valueFromEnv) == 0 );
+}
+
+static const char* const sEnvVarsToCheck[] = {
+       "DYLD_LIBRARY_PATH",
+       "DYLD_FRAMEWORK_PATH",
+       "DYLD_FALLBACK_LIBRARY_PATH",
+       "DYLD_FALLBACK_FRAMEWORK_PATH",
+       "DYLD_INSERT_LIBRARIES",
+       "DYLD_IMAGE_SUFFIX",
+       "DYLD_VERSIONED_FRAMEWORK_PATH",
+       "DYLD_VERSIONED_LIBRARY_PATH",
+       "DYLD_ROOT_PATH"
+};
+
+static bool envVarsMatch(dyld3::launch_cache::Closure mainClosure, const char* envp[])
+{
+       for (const char* envVar : sEnvVarsToCheck) {
+               if ( !envVarMatches(mainClosure, envp, envVar) ) {
+                       if ( gLinkContext.verboseWarnings )
+                               dyld::log("dyld: closure %p not used because %s changed\n", mainClosure.binaryData(), envVar);
+                       return false;
+               }
+       }
+
+       return true;
+}
+
+static bool closureValid(const dyld3::launch_cache::BinaryClosureData* mainClosureData, const mach_header* mainExecutableMH, const uint8_t* mainExecutableCDHash, bool closureInCache, const char* envp[])
+{
+       const dyld3::launch_cache::Closure    mainClosure(mainClosureData);
+       const dyld3::launch_cache::ImageGroup mainGroup = mainClosure.group();
+
+       // verify current dyld cache is same as expected
+       if ( sSharedCacheLoadInfo.loadAddress == nullptr ) {
+               if ( gLinkContext.verboseWarnings )
+                       dyld::log("dyld: closure %p dyld cache not loaded\n", mainClosureData);
+               return false;
+       }
+       if ( !closureInCache ) {
+               // closures in cache don't have cache's UUID
+               uuid_t cacheUUID;
+               sSharedCacheLoadInfo.loadAddress->getUUID(cacheUUID);
+               if ( memcmp(mainClosure.dyldCacheUUID(), cacheUUID, sizeof(uuid_t)) != 0 ) {
+                       if ( gLinkContext.verboseWarnings )
+                               dyld::log("dyld: closure %p not used because built against different dyld cache\n", mainClosureData);
+                       return false;
+               }
+       }
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
+       else {
+               // HACK until closured for dlopen can run against live cache file
+               int fd = my_open(sSharedCacheLoadInfo.path, O_RDONLY, 0);
+               if ( fd != -1 ) {
+                       dyld_cache_header fileHeader;
+                       if ( pread(fd, &fileHeader, sizeof(fileHeader), 0) == sizeof(fileHeader) ) {
+                               uuid_t cacheUUID;
+                               sSharedCacheLoadInfo.loadAddress->getUUID(cacheUUID);
+                               if ( memcmp(fileHeader.uuid, cacheUUID, sizeof(uuid_t)) != 0 ) {
+                                       if ( gLinkContext.verboseWarnings )
+                                               dyld::log("dyld: closure %p not used because current cache on disk is not they one being used\n", mainClosureData);
+                                       ::close(fd);
+                                       return false;
+                               }
+                       }
+                       ::close(fd);
+               }
+       }
+#endif
+
+       // verify main executable file has not changed since closure was built
+       const dyld3::launch_cache::Image mainImage = mainGroup.image(mainClosure.mainExecutableImageIndex());
+    if ( mainImage.validateUsingModTimeAndInode() ) {
+        struct stat statBuf;
+        if ( ::stat(mainImage.path(), &statBuf) != 0 ) {
+                       if ( gLinkContext.verboseWarnings )
+                               dyld::log("dyld: closure %p not used because stat() failed on main executable\n", mainClosureData);
+            return false;
+        }
+        else if ( (statBuf.st_mtime != mainImage.fileModTime()) || (statBuf.st_ino != mainImage.fileINode()) ) {
+                       if ( gLinkContext.verboseWarnings )
+                               dyld::log("dyld: closure %p not used because mtime/inode changed since closure was built\n", mainClosureData);
+            return false;
+        }
+    }
+
+       // verify cdHash of main executable is same as recorded in closure
+       if ( mainImage.validateUsingCdHash() ) {
+               if ( mainExecutableCDHash == nullptr ) {
+                       if ( gLinkContext.verboseWarnings )
+                               dyld::log("dyld: closure %p not used because main executable is not code signed but was expected to be\n", mainClosureData);
+            return false;
+               }
+               if ( memcmp(mainExecutableCDHash, mainClosure.cdHash(), 20) != 0 ) {
+                       if ( gLinkContext.verboseWarnings )
+                               dyld::log("dyld: closure %p not used because main executable cd-hash changed since closure was built\n", mainClosureData);
+            return false;
+               }
+       }
+       
+       // verify UUID of main executable is same as recorded in closure
+       const uuid_t* closureMainUUID = mainImage.uuid();
+       dyld3::MachOParser parser(mainExecutableMH);
+       uuid_t actualUUID;
+       parser.getUuid(actualUUID);
+       if ( memcmp(actualUUID, closureMainUUID, sizeof(uuid_t)) != 0 ) {
+               if ( gLinkContext.verboseWarnings )
+                       dyld::log("dyld: closure %p not used because UUID of executable changed since closure was built\n", mainClosureData);
+               return false;
+       }
+
+       // verify DYLD_* env vars are same as when closure was built
+       if ( !envVarsMatch(mainClosure, envp) ) {
+               return false;
+       }
+
+       // verify files that are supposed to be missing actually are missing
+       __block bool foundFileThatInvalidatesClosure = false;
+       mainClosure.forEachMustBeMissingFile(^(const char* path, bool& stop) {
+               struct stat statBuf;
+               if ( ::stat(path, &statBuf) == 0 ) {
+                       stop = true;
+                       foundFileThatInvalidatesClosure = true;
+                       if ( gLinkContext.verboseWarnings )
+                               dyld::log("dyld: closure %p not used because found unexpected file '%s'\n", mainClosureData, path);
+               }
+       });
+
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
+       // verify no key frameworks have been overridden since cache was built
+       if ( dyld3::loader::internalInstall() ) {
+               dyld3::loader::forEachLineInFile("/AppleInternal/Library/Preferences/dyld-potential-framework-overrides", ^(const char* path, bool& stop) {
+                       dyld3::SharedCacheFindDylibResults shareCacheResults;
+                       if ( dyld3::findInSharedCacheImage(sSharedCacheLoadInfo, path, &shareCacheResults) ) {
+                               dyld3::launch_cache::Image image(shareCacheResults.imageData);
+                               struct stat statBuf;
+                               if ( ::stat(path, &statBuf) == 0 ) {
+                                       if ( (image.fileModTime() != statBuf.st_mtime) || (image.fileINode() != statBuf.st_ino)) {
+                                               if ( gLinkContext.verboseWarnings )
+                                                       dyld::log("dyld: closure %p not used because framework has changed: '%s'\n", mainClosureData, path);
+                                               foundFileThatInvalidatesClosure = true;
+                                               stop = true;
+                                       }
+                               }
+                       }
+               });
+       }
+#endif
+
+       return !foundFileThatInvalidatesClosure;
+}
+
+static bool nolog(const char* format, ...)
+{
+       return false;
+}
+
+static bool dolog(const char* format, ...)
+{
+       va_list list;
+       va_start(list, format);
+       vlog(format, list);
+       va_end(list);
+       return true;
+}
+
+static bool launchWithClosure(const dyld3::launch_cache::BinaryClosureData* mainClosureData,
+                                                         const DyldSharedCache* dyldCache,
+                                                         const mach_header* mainExecutableMH, uintptr_t mainExecutableSlide,
+                                                         int argc, const char* argv[], const char* envp[], const char* apple[],
+                                                         uintptr_t* entry, uintptr_t* startGlue)
+{
+       dyld3::launch_cache::Closure                    mainClosure(mainClosureData);
+       const dyld3::launch_cache::ImageGroup   mainGroup           = mainClosure.group();
+       const uint32_t                                                  mainExecutableIndex = mainClosure.mainExecutableImageIndex();
+       const dyld3::launch_cache::Image        mainImage           = mainGroup.image(mainExecutableIndex);
+       const uint32_t                                                  loadedImageCount    = mainClosure.initialImageCount();
+
+       // construct array of groups
+    dyld3::DyldCacheParser cacheParser(dyldCache, false);
+       STACK_ALLOC_DYNARRAY(const dyld3::launch_cache::binary_format::ImageGroup*, 3, theGroups);
+       theGroups[0] = cacheParser.cachedDylibsGroup();
+       theGroups[1] = cacheParser.otherDylibsGroup();
+       theGroups[2] = mainClosure.group().binaryData();
+       
+       // construct array of all Image*, starting with any inserted dylibs, then main executable
+       const dyld3::launch_cache::BinaryImageData*                     images[loadedImageCount];
+       dyld3::launch_cache::SlowLoadSet imageSet(&images[0], &images[loadedImageCount]);
+       for (uint32_t i=0; i <= mainExecutableIndex; ++i) {
+               imageSet.add(mainGroup.image(i).binaryData());
+       }
+       // add all dependents of main executable
+       if ( !mainImage.recurseAllDependentImages(theGroups, imageSet, nullptr) ) {
+               dyld::log("initial image list overflow, expected only %d\n", loadedImageCount);
+               return false;
+       }
+       // add dependents of any inserted dylibs
+       for (uint32_t i=0; i < mainExecutableIndex; ++i) {
+               if ( !mainGroup.image(i).recurseAllDependentImages(theGroups, imageSet, nullptr) ) {
+                       dyld::log("initial image list overflow in inserted libraries, expected only %d\n", loadedImageCount);
+                       return false;
+               }
+       }
+       const uint32_t actualImageCount = (uint32_t)imageSet.count();
+       // construct array of allImages
+       STACK_ALLOC_DYNARRAY(dyld3::loader::ImageInfo, actualImageCount, allImages);
+       for (int i=0; i < actualImageCount; ++i) {
+               dyld3::launch_cache::Image      img(images[i]);
+               dyld3::launch_cache::ImageGroup grp = img.group();
+               allImages[i].imageData              = img.binaryData();
+               allImages[i].loadAddress            = nullptr;
+               allImages[i].groupNum               = grp.groupNum();
+               allImages[i].indexInGroup           = grp.indexInGroup(img.binaryData());
+               allImages[i].previouslyFixedUp      = false;
+               allImages[i].justMapped             = false;
+               allImages[i].justUsedFromDyldCache  = false;
+               allImages[i].neverUnload            = false;
+       }
+       // prefill address of main executable to mark it is already loaded
+       allImages[mainExecutableIndex].loadAddress = mainExecutableMH;
+
+       // map new images and apply all fixups
+       Diagnostics diag;
+       mapAndFixupImages(diag, allImages, (const uint8_t*)dyldCache, (gLinkContext.verboseLoading ? &dolog : &nolog),
+                                                                                                                                 (gLinkContext.verboseMapping ? &dolog : &nolog),
+                                                                                                                                 (gLinkContext.verboseBind    ? &dolog : &nolog),
+                                                                                                                                 (gLinkContext.verboseDOF     ?  &dolog : &nolog));
+       if ( diag.hasError() ) {
+               if ( gLinkContext.verboseWarnings )
+                       dyld::log("dyld: %s\n", diag.errorMessage());
+               return false;
+       }
+
+       //dyld::log("loaded image list:\n");
+       //for (int i=0; i < allImages.count(); ++i) {
+       //      dyld3::launch_cache::Image img(allImages[i].imageData);
+       //      dyld::log("binImage[%d]=%p, mh=%p, path=%s\n", i, allImages[i].imageData, allImages[i].loadAddress, img.path());
+       //}
+
+       // find special images
+       const dyld3::launch_cache::BinaryImageData*     libSystemImage = mainClosure.libSystem(theGroups);
+       const dyld3::launch_cache::BinaryImageData*     libDyldImage   = mainClosure.libDyld(theGroups);
+       const mach_header*                                                      libdyldMH = nullptr;
+       const mach_header*                                                      libSystemMH = nullptr;
+       for (int i=0; i < allImages.count(); ++i) {
+               if ( allImages[i].imageData == libSystemImage )
+                       libSystemMH = allImages[i].loadAddress;
+               else if ( allImages[i].imageData == libDyldImage )
+                       libdyldMH = allImages[i].loadAddress;
+       }
+
+       // send info on all images to libdyld.dylb
+       const dyld3::LibDyldEntryVector* libDyldEntry = (dyld3::LibDyldEntryVector*)((uint8_t*)libdyldMH + mainClosure.libdyldVectorOffset());
+       libDyldEntry->setVars(mainExecutableMH, argc, argv, envp, apple);
+       libDyldEntry->setHaltFunction(&halt);
+       if ( libDyldEntry->vectorVersion > 2 )
+               libDyldEntry->setChildForkFunction(&_dyld_fork_child);
+#if !TARGET_IPHONE_SIMULATOR
+       if ( libDyldEntry->vectorVersion > 3 )
+               libDyldEntry->setLogFunction(&dyld::vlog);
+#endif
+       libDyldEntry->setOldAllImageInfo(gProcessInfo);
+       libDyldEntry->setInitialImageList(mainClosureData, dyldCache, sSharedCacheLoadInfo.path, allImages, libSystemMH, libSystemImage);
+       // run initializers
+       CRSetCrashLogMessage("dyld3: launch, running initializers");
+       libDyldEntry->runInitialzersBottomUp((mach_header*)mainExecutableMH);
+       //dyld::log("returned from runInitialzersBottomUp()\n");
+
+       dyld3::kdebug_trace_dyld_signpost(DBG_DYLD_SIGNPOST_START_MAIN, 0, 0);
+       if ( mainClosure.mainExecutableUsesCRT() ) {
+               // old style app linked with crt1.o
+               // entry is "start" function in program
+               *startGlue = 0;
+               *entry = (uintptr_t)mainExecutableMH + mainClosure.mainExecutableEntryOffset();
+       }
+       else {
+               // modern app with LC_MAIN
+               // set startGlue to "start" function in libdyld.dylib
+               // set entry to "main" function in program
+               *startGlue = (uintptr_t)(libDyldEntry->startFunc);
+               *entry =(uintptr_t)mainExecutableMH + mainClosure.mainExecutableEntryOffset();
+       }
+       CRSetCrashLogMessage(NULL);
+       return true;
+}
+
+static void putHexNibble(uint8_t value, char*& p)
+{
+       if ( value < 10 )
+               *p++ = '0' + value;
+       else
+               *p++ = 'A' + value - 10;
+}
+
+static void putHexByte(uint8_t value, char*& p)
+{
+       value &= 0xFF;
+       putHexNibble(value >> 4,   p);
+       putHexNibble(value & 0x0F, p);
+}
+
+static void makeHexLong(unsigned long value, char* p)
+{
+       *p++ = '0';
+       *p++ = 'x';
+#if __LP64__
+       putHexByte(value >> 56, p);
+       putHexByte(value >> 48, p);
+       putHexByte(value >> 40, p);
+       putHexByte(value >> 32, p);
+#endif
+       putHexByte(value >> 24, p);
+       putHexByte(value >> 16, p);
+       putHexByte(value >> 8, p);
+       putHexByte(value, p);
+       *p = '\0';
+}
+
+static void makeUUID(uint8_t uuid[16], char* p)
+{
+       putHexByte(uuid[0], p);
+       putHexByte(uuid[1], p);
+       putHexByte(uuid[2], p);
+       putHexByte(uuid[3], p);
+       *p++ = '-';
+       putHexByte(uuid[4], p);
+       putHexByte(uuid[5], p);
+       *p++ = '-';
+       putHexByte(uuid[6], p);
+       putHexByte(uuid[7], p);
+       *p++ = '-';
+       putHexByte(uuid[8], p);
+       putHexByte(uuid[9], p);
+       *p++ = '-';
+       putHexByte(uuid[10], p);
+       putHexByte(uuid[11], p);
+       putHexByte(uuid[12], p);
+       putHexByte(uuid[13], p);
+       putHexByte(uuid[14], p);
+       putHexByte(uuid[15], p);
+       *p = '\0';
+}
+
+#if !TARGET_IPHONE_SIMULATOR
+static const dyld3::launch_cache::BinaryClosureData* callClosureDaemon(const char* mainExecPath, const char* envp[])
+{
+       // temp, until we can get a bootstrap_lookup that works from dyld
+#if 1
+       // Create a pipe
+    int sockets[2];
+    if ( ::pipe(sockets) < 0 ) {
+        dyld::log("error opening stream socket pair to closured\n");
+        return NULL;
+    }
+       //dyld::log("created sockets %d and %d\n", sockets[0], sockets[1]);
+       // use fork/exec to launch closured
+    int child = ::__fork();
+    if ( child == -1 ) {
+               dyld::log("error forking, errno=%d\n", errno);
+               return NULL;
+    }
+    if ( child ) {
+               // parent side
+               //dyld::log("parent side pid=%d\n", getpid());
+               ::close(sockets[1]);
+               SocketBasedClousureHeader header;
+               long amount = ::read(sockets[0], &header, sizeof(SocketBasedClousureHeader));
+               if ( amount != sizeof(SocketBasedClousureHeader) ) {
+                       dyld::log("error reading, errno=%d\n", errno);
+                       return NULL;
+               }
+               vm_address_t bufferAddress = 0;
+               if ( ::vm_allocate(mach_task_self(), &bufferAddress, header.length, VM_FLAGS_ANYWHERE) != 0 ) {
+                       dyld::log("error allocating buffer\n");
+                       return NULL;
+               }
+               amount = ::read(sockets[0], (void*)bufferAddress, header.length);
+               close(sockets[0]);
+               if ( amount != header.length ) {
+                       dyld::log("dyld: error reading buffer header from closured, amount=%ld, errno=%d\n", amount, errno);
+                       return NULL;
+               }
+               if ( header.success ) {
+                       // make buffer read-only
+                       vm_protect(mach_task_self(), bufferAddress, header.length, VM_PROT_READ, VM_PROT_READ);
+                       return (const dyld3::launch_cache::BinaryClosureData*)bufferAddress;
+               }
+               else {
+                       // buffer contains error message as to why closure could not be built
+                       dyld::log("%s", (char*)bufferAddress);
+                       ::vm_deallocate(mach_task_self(), bufferAddress, header.length);
+                       return NULL;
+               }
+    }
+    else {
+               // child side
+               //dyld::log("child side pid=%d\n", getpid());
+               close(sockets[0]);
+               const char* closuredPath = "/usr/libexec/closured";
+               char pipeStr[8];
+               pipeStr[0] = '0' + sockets[1];
+               pipeStr[1] = '\0';
+               const char* argv[32];
+               char cacheUuidString[64];
+               char cacheAddrString[64];
+               char cacheSizeString[64];
+               int i = 0;
+               uuid_t cacheUUID;
+               sSharedCacheLoadInfo.loadAddress->getUUID(cacheUUID);
+               makeHexLong((long)sSharedCacheLoadInfo.loadAddress, cacheAddrString);
+               makeHexLong((long)sSharedCacheLoadInfo.loadAddress->mappedSize(), cacheSizeString);
+               makeUUID(cacheUUID, cacheUuidString);
+               argv[i++] = closuredPath;
+               argv[i++] = "-create_closure";
+               argv[i++] = mainExecPath;
+               argv[i++] = "-pipefd";
+               argv[i++] = pipeStr;
+               argv[i++] = "-cache_uuid";
+               argv[i++] = cacheUuidString;
+               argv[i++] = "-cache_address";
+               argv[i++] = cacheAddrString;
+               argv[i++] = "-cache_size";
+               argv[i++] = cacheSizeString;
+               for (const char**p=envp; *p != NULL; ++p) {
+                       const char* envToCheck = *p;
+                       for (const char* dyldEnvVar : sEnvVarsToCheck) {
+                               size_t dyldEnvVarLen = strlen(dyldEnvVar);
+                               if ( (strncmp(dyldEnvVar, envToCheck, dyldEnvVarLen) == 0) && (envToCheck[dyldEnvVarLen] == '=') ) {
+                                       argv[i++] = "-env";
+                                       argv[i++] = envToCheck;
+                               }
+                       }
+               }
+        argv[i] = nullptr;
+               //dyld::log("closured args:\n");
+               //for (int j=0; argv[j] != nullptr; ++j)
+               //      dyld::log("  argv[%d]=%s\n", j, argv[j]);
+        execve(closuredPath, (char**)argv, nullptr);
+               dyld::log("exec() of closured failed, errno=%d\n", errno);
+    }
+       return NULL;
+
+#else
+       // get port to closured
+    mach_port_t serverPort = dyld3::loader::lookupClosuredPort();
+       if ( serverPort == MACH_PORT_NULL )
+               return NULL;
+
+       // build env var list
+    char envBuffer[2048];
+       char* s = envBuffer;
+       for (const char* envVar : sEnvVarsToCheck) {
+               if ( const char* valueFromEnv = _simple_getenv(envp, envVar) ) {
+                       strcpy(s, envVar);
+                       strcat(s, "=");
+                       strcat(s, valueFromEnv);
+                       s += strlen(s)+1;
+               }
+       }
+       *s++ = '\0';
+
+       // get uuid of main executable
+       dyld3::MachOParser mainParser((mach_header*)sMainExecutableMachHeader);
+    uuid_t mainUuid;
+       mainParser.getUuid(mainUuid);
+
+       // message closured to build closure
+    bool success = false;
+    vm_offset_t reply = 0;
+    uint32_t  replySize = 0;
+    if ( closured_CreateLaunchClosure(serverPort, sExecPath, sSharedCachePath, mainUuid, envBuffer, &success, &reply, &replySize) != KERN_SUCCESS )
+               return NULL;
+
+       // release server port
+    mach_port_deallocate(mach_task_self(), serverPort);
+
+       if ( success )
+               return (const dyld3::launch_cache::BinaryClosureData*)reply;
+
+       dyld::log("closure failed to build: %s\n", (char*)reply);
+       return NULL;
+#endif
+}
+#endif // !TARGET_IPHONE_SIMULATOR
+
+
+#if !__MAC_OS_X_VERSION_MIN_REQUIRED
+static const char* sWhiteListDirs[] = {
+       "/bin/",
+       "/sbin/",
+       "/usr/bin/"
+};
+#endif
+
+static bool inWhiteList(const char* execPath)
+{
+    // First test to see if we forced in dyld2 via a kernel boot-arg
+    if ( dyld3::loader::bootArgsContains("force_dyld2=1") )
+               return false;
+
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
+
+       // rdar://problem/32701418: Don't use dyld3 for i386 for now.
+#if __i386__
+       return false;
+#else
+
+
+       return true;
+#endif // #if __i386__
+
+#else
+       // <rdar://problem/33171968> enable dyld3 mode for all OS programs when using customer dyld cache (no roots)
+       if ( (sSharedCacheLoadInfo.loadAddress != nullptr) && (sSharedCacheLoadInfo.loadAddress->header.cacheType == kDyldSharedCacheTypeProduction) )
+               return true;
+
+       for (const char* dir : sWhiteListDirs) {
+               if ( strncmp(dir, sExecPath, strlen(dir)) == 0 ) {
+                       return true;
+               }
+       }
+       return dyld3::loader::bootArgsContains("force_dyld3=1");
+#endif
+}
 
 //
 // Entry point for dyld.  The kernel loads dyld and jumps to __dyld_start which
 
 //
 // Entry point for dyld.  The kernel loads dyld and jumps to __dyld_start which
@@ -5755,14 +5619,28 @@ _main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide,
                int argc, const char* argv[], const char* envp[], const char* apple[], 
                uintptr_t* startGlue)
 {
                int argc, const char* argv[], const char* envp[], const char* apple[], 
                uintptr_t* startGlue)
 {
+       dyld3::kdebug_trace_dyld_signpost(DBG_DYLD_SIGNPOST_START_DYLD, 0, 0);
+
+       // Grab the cdHash of the main executable from the environment
+       uint8_t mainExecutableCDHashBuffer[20];
+       const uint8_t* mainExecutableCDHash = nullptr;
+       if ( hexToBytes(_simple_getenv(apple, "executable_cdhash"), 40, mainExecutableCDHashBuffer) )
+               mainExecutableCDHash = mainExecutableCDHashBuffer;
+
+       // Trace dyld's load
+       notifyKernelAboutImage((macho_header*)&__dso_handle, _simple_getenv(apple, "dyld_file"));
+#if !TARGET_IPHONE_SIMULATOR
+       // Trace the main executable's load
+       notifyKernelAboutImage(mainExecutableMH, _simple_getenv(apple, "executable_file"));
+#endif
+
        uintptr_t result = 0;
        sMainExecutableMachHeader = mainExecutableMH;
        uintptr_t result = 0;
        sMainExecutableMachHeader = mainExecutableMH;
+       sMainExecutableSlide = mainExecutableSlide;
 #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 ) {
 #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]; 
 
                // look to see if simulator has its own dyld
                char simDyldPath[PATH_MAX]; 
@@ -5800,6 +5678,7 @@ _main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide,
                        sExecPath = s;
                }
        }
                        sExecPath = s;
                }
        }
+
        // Remember short name of process for later logging
        sExecShortName = ::strrchr(sExecPath, '/');
        if ( sExecShortName != NULL )
        // Remember short name of process for later logging
        sExecShortName = ::strrchr(sExecPath, '/');
        if ( sExecShortName != NULL )
@@ -5826,6 +5705,96 @@ _main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide,
        if ( sEnv.DYLD_PRINT_ENV ) 
                printEnvironmentVariables(envp);
        getHostInfo(mainExecutableMH, mainExecutableSlide);
        if ( sEnv.DYLD_PRINT_ENV ) 
                printEnvironmentVariables(envp);
        getHostInfo(mainExecutableMH, mainExecutableSlide);
+
+       // load shared cache
+       checkSharedRegionDisable((mach_header*)mainExecutableMH);
+#if TARGET_IPHONE_SIMULATOR
+       // <HACK> until <rdar://30773711> is fixed
+       gLinkContext.sharedRegionMode = ImageLoader::kUsePrivateSharedRegion;
+       // </HACK>
+#endif
+       if ( gLinkContext.sharedRegionMode != ImageLoader::kDontUseSharedRegion ) {
+               mapSharedCache();
+       }
+
+       if ( (sEnableClosures || inWhiteList(sExecPath)) && (sSharedCacheLoadInfo.loadAddress != nullptr) ) {
+               if ( sSharedCacheLoadInfo.loadAddress->header.formatVersion == dyld3::launch_cache::binary_format::kFormatVersion ) {
+                       const dyld3::launch_cache::BinaryClosureData* mainClosureData;
+                       // check for closure in cache first
+                       dyld3::DyldCacheParser cacheParser(sSharedCacheLoadInfo.loadAddress, false);
+                       mainClosureData = cacheParser.findClosure(sExecPath);
+       #if __IPHONE_OS_VERSION_MIN_REQUIRED
+                       if ( mainClosureData == nullptr ) {
+                               // see if this is an OS app that was moved
+                               if ( strncmp(sExecPath, "/var/containers/Bundle/Application/", 35) == 0 ) {
+                                       dyld3::MachOParser mainParser((mach_header*)mainExecutableMH);
+                                       uint32_t textOffset;
+                                       uint32_t textSize;
+                                       if ( !mainParser.isFairPlayEncrypted(textOffset, textSize) ) {
+                                               __block bool hasEmbeddedDylibs = false;
+                                               mainParser.forEachDependentDylib(^(const char* loadPath, bool, bool, bool, uint32_t, uint32_t, bool& stop) {
+                                                       if ( loadPath[0] == '@' ) {
+                                                               hasEmbeddedDylibs = true;
+                                                               stop = true;
+                                                       }
+                                               });
+                                               if ( !hasEmbeddedDylibs ) {
+                                                       char altPath[1024];
+                                                       const char* lastSlash = strrchr(sExecPath, '/');
+                                                       if ( lastSlash != nullptr ) {
+                                                               strlcpy(altPath, "/private/var/staged_system_apps", sizeof(altPath));
+                                                               strlcat(altPath, lastSlash, sizeof(altPath));
+                                                               strlcat(altPath, ".app", sizeof(altPath));
+                                                               strlcat(altPath, lastSlash, sizeof(altPath));
+                                                               if ( gLinkContext.verboseWarnings )
+                                                                       dyld::log("try path: %s\n", altPath);
+                                                               mainClosureData = cacheParser.findClosure(altPath);
+                                                       }
+                                               }
+                                       }
+                               }
+                       }
+       #endif
+                       if ( gLinkContext.verboseWarnings && (mainClosureData != nullptr) )
+                               dyld::log("dyld: found closure %p in dyld shared cache\n", mainClosureData);
+       #if !TARGET_IPHONE_SIMULATOR
+                       if ( (mainClosureData == nullptr) || !closureValid(mainClosureData, (mach_header*)mainExecutableMH, mainExecutableCDHash, true, envp) ) {
+                               mainClosureData = nullptr;
+                               if ( sEnableClosures ) {
+                                       // if forcing closures, and no closure in cache, or it is invalid, then RPC to closured
+                                       mainClosureData = callClosureDaemon(sExecPath, envp);
+                                       if ( gLinkContext.verboseWarnings )
+                                               dyld::log("dyld: closured return %p for %s\n", mainClosureData, sExecPath);
+                                       if ( (mainClosureData != nullptr) && !closureValid(mainClosureData, (mach_header*)mainExecutableMH, mainExecutableCDHash, false, envp) ) {
+                                               // some how freshly generated closure is invalid...
+                                               mainClosureData = nullptr;
+                                       }
+                               }
+                       }
+       #endif
+                       // try using launch closure
+                       if ( mainClosureData != nullptr ) {
+                               CRSetCrashLogMessage("dyld3: launch started");
+                               if ( launchWithClosure(mainClosureData, sSharedCacheLoadInfo.loadAddress, (mach_header*)mainExecutableMH, mainExecutableSlide,
+                                                                          argc, argv, envp, apple, &result, startGlue) ) {
+                                       if (sSkipMain)
+                                               result = (uintptr_t)&fake_main;
+                                       return result;
+                               }
+                               else {
+                                       if ( gLinkContext.verboseWarnings )
+                                               dyld::log("dyld: unable to use closure %p\n", mainClosureData);
+                               }
+                       }
+               }
+               else {
+                       if ( gLinkContext.verboseWarnings )
+                               dyld::log("dyld: not using closure because shared cache format version does not match dyld's\n");
+               }
+               // could not use closure info, launch old way
+       }
+
+
        // install gdb notifier
        stateToHandlers(dyld_image_state_dependents_mapped, sBatchHandlers)->push_back(notifyGDB);
        stateToHandlers(dyld_image_state_mapped, sSingleHandlers)->push_back(updateAllImages);
        // install gdb notifier
        stateToHandlers(dyld_image_state_dependents_mapped, sBatchHandlers)->push_back(notifyGDB);
        stateToHandlers(dyld_image_state_mapped, sSingleHandlers)->push_back(updateAllImages);
@@ -5847,10 +5816,14 @@ _main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide,
        try {
                // add dyld itself to UUID list
                addDyldImageToUUIDList();
        try {
                // add dyld itself to UUID list
                addDyldImageToUUIDList();
-               notifyKernelAboutDyld();
 
 #if SUPPORT_ACCELERATE_TABLES
                bool mainExcutableAlreadyRebased = false;
 
 #if SUPPORT_ACCELERATE_TABLES
                bool mainExcutableAlreadyRebased = false;
+               if ( (sSharedCacheLoadInfo.loadAddress != nullptr) && !dylibsCanOverrideCache() && !sDisableAcceleratorTables && (sSharedCacheLoadInfo.loadAddress->header.accelerateInfoAddr != 0) ) {
+                       struct stat statBuf;
+                       if ( ::stat(IPHONE_DYLD_SHARED_CACHE_DIR "no-dyld2-accelerator-tables", &statBuf) != 0 )
+                               sAllCacheImagesProxy = ImageLoaderMegaDylib::makeImageLoaderMegaDylib(&sSharedCacheLoadInfo.loadAddress->header, sSharedCacheLoadInfo.slide, mainExecutableMH, gLinkContext);
+               }
 
 reloadAllImages:
 #endif
 
 reloadAllImages:
 #endif
@@ -5900,23 +5873,6 @@ reloadAllImages:
                gLinkContext.strictMachORequired = true;
        #endif
 
                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;
-                       task_register_dyld_shared_cache_image_info(mach_task_self(), kernelCacheInfo, true, false);
-               }
-       #endif
-
        #if SUPPORT_ACCELERATE_TABLES
                sAllImages.reserve((sAllCacheImagesProxy != NULL) ? 16 : INITIAL_IMAGE_COUNT);
        #else
        #if SUPPORT_ACCELERATE_TABLES
                sAllImages.reserve((sAllCacheImagesProxy != NULL) ? 16 : INITIAL_IMAGE_COUNT);
        #else
@@ -6036,16 +5992,15 @@ reloadAllImages:
                // <rdar://problem/12186933> do weak binding only after all inserted images linked
                sMainExecutable->weakBind(gLinkContext);
 
                // <rdar://problem/12186933> 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 cache has branch island dylibs, tell debugger about them
-               if ( (sSharedCache != NULL) && (sSharedCache->mappingOffset >= 0x78) && (sSharedCache->branchPoolsOffset != 0) ) {
-                       uint32_t count = sSharedCache->branchPoolsCount;
+               if ( (sSharedCacheLoadInfo.loadAddress != NULL) && (sSharedCacheLoadInfo.loadAddress->header.mappingOffset >= 0x78) && (sSharedCacheLoadInfo.loadAddress->header.branchPoolsOffset != 0) ) {
+                       uint32_t count = sSharedCacheLoadInfo.loadAddress->header.branchPoolsCount;
                        dyld_image_info info[count];
                        dyld_image_info info[count];
-                       const uint64_t* poolAddress = (uint64_t*)((char*)sSharedCache + sSharedCache->branchPoolsOffset);
+                       const uint64_t* poolAddress = (uint64_t*)((char*)sSharedCacheLoadInfo.loadAddress + sSharedCacheLoadInfo.loadAddress->header.branchPoolsOffset);
                        // <rdar://problem/20799203> empty branch pools can be in development cache
                        if ( ((mach_header*)poolAddress)->magic == sMainExecutableMachHeader->magic ) {
                                for (int poolIndex=0; poolIndex < count; ++poolIndex) {
                        // <rdar://problem/20799203> 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;
+                                       uint64_t poolAddr = poolAddress[poolIndex] + sSharedCacheLoadInfo.slide;
                                        info[poolIndex].imageLoadAddress = (mach_header*)(long)poolAddr;
                                        info[poolIndex].imageFilePath = "dyld_shared_cache_branch_islands";
                                        info[poolIndex].imageFileModDate = 0;
                                        info[poolIndex].imageLoadAddress = (mach_header*)(long)poolAddr;
                                        info[poolIndex].imageFilePath = "dyld_shared_cache_branch_islands";
                                        info[poolIndex].imageFileModDate = 0;
@@ -6056,7 +6011,6 @@ reloadAllImages:
                                gProcessInfo->notification(dyld_image_adding, count, info);
                        }
                }
                                gProcessInfo->notification(dyld_image_adding, count, info);
                        }
                }
-       #endif
 
                CRSetCrashLogMessage("dyld: launch, running initializers");
        #if SUPPORT_OLD_CRT_INITIALIZATION
 
                CRSetCrashLogMessage("dyld: launch, running initializers");
        #if SUPPORT_OLD_CRT_INITIALIZATION
@@ -6069,6 +6023,7 @@ reloadAllImages:
        #endif
 
                // notify any montoring proccesses that this process is about to enter main()
        #endif
 
                // notify any montoring proccesses that this process is about to enter main()
+               dyld3::kdebug_trace_dyld_signpost(DBG_DYLD_SIGNPOST_START_MAIN_DYLD2, 0, 0);
                notifyMonitoringDyldMain();
 
                // find entry point for main executable
                notifyMonitoringDyldMain();
 
                // find entry point for main executable
@@ -6095,12 +6050,17 @@ reloadAllImages:
        }
 
        CRSetCrashLogMessage(NULL);
        }
 
        CRSetCrashLogMessage(NULL);
+
+       if (sSkipMain) {
+               dyld3::kdebug_trace_dyld_signpost(DBG_DYLD_SIGNPOST_START_MAIN, 0, 0);
+               result = (uintptr_t)&fake_main;
+               *startGlue = (uintptr_t)gLibSystemHelpers->startGlueToCallExit;
+       }
        
        return result;
 }
 
 
        
        return result;
 }
 
 
-
 } // namespace
 
 
 } // namespace
 
 
index e5f408681ff24e6c2acc9172c2de9570ceb6b6b0..59a6660f4995d34d99c21c12e235bbcc5a47907a 100644 (file)
@@ -28,7 +28,6 @@
 
 # gdb and Symbolication look at these to discover a process's loaded images
 _dyld_all_image_infos
 
 # 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
 __dyld_debugger_notification
 
 # CrashReporter uses this to get message as to why dyld terminated the process
index d8bca0d7b7a68b5f9a7c242d91996723edbafe5c..de649abd42135f56604564c4fff19f999785bb75 100644 (file)
@@ -65,9 +65,7 @@ namespace dyld {
 #if SUPPORT_ACCELERATE_TABLES
        extern bool                                                             gLogAppAPIs;
 #endif
 #if SUPPORT_ACCELERATE_TABLES
        extern bool                                                             gLogAppAPIs;
 #endif
-#if DYLD_SHARED_CACHE_SUPPORT
        extern bool                                                             gSharedCacheOverridden;
        extern bool                                                             gSharedCacheOverridden;
-#endif
        extern const struct LibSystemHelpers*   gLibSystemHelpers;
 #if SUPPORT_OLD_CRT_INITIALIZATION
        extern bool                                                             gRunInitializersOldWay;
        extern const struct LibSystemHelpers*   gLibSystemHelpers;
 #if SUPPORT_OLD_CRT_INITIALIZATION
        extern bool                                                             gRunInitializersOldWay;
@@ -110,12 +108,9 @@ namespace dyld {
        extern void                                     registerObjCNotifiers(_dyld_objc_notify_mapped, _dyld_objc_notify_init, _dyld_objc_notify_unmapped);
        extern bool                                     sharedCacheUUID(uuid_t uuid);
        extern void                                     garbageCollectImages();
        extern void                                     registerObjCNotifiers(_dyld_objc_notify_mapped, _dyld_objc_notify_init, _dyld_objc_notify_unmapped);
        extern bool                                     sharedCacheUUID(uuid_t uuid);
        extern void                                     garbageCollectImages();
-       extern int                                      openSharedCacheFile();
        extern const void*                      imMemorySharedCacheHeader();
        extern uintptr_t                        fastBindLazySymbol(ImageLoader** imageLoaderCache, uintptr_t lazyBindingInfoOffset);
        extern const void*                      imMemorySharedCacheHeader();
        extern uintptr_t                        fastBindLazySymbol(ImageLoader** imageLoaderCache, uintptr_t lazyBindingInfoOffset);
-#if DYLD_SHARED_CACHE_SUPPORT
        extern bool                                     inSharedCache(const char* path);
        extern bool                                     inSharedCache(const char* path);
-#endif
 #if LOG_BINDINGS
        extern void                                     logBindings(const char* format, ...);
 #endif
 #if LOG_BINDINGS
        extern void                                     logBindings(const char* format, ...);
 #endif
index c87aafff2780d964c068d71a3deac8335a55f5d1..65e568172bca605ab2434b6464e0057880941954 100644 (file)
 #include "ImageLoaderMachO.h"
 #include "dyld.h"
 #include "dyldLibSystemInterface.h"
 #include "ImageLoaderMachO.h"
 #include "dyld.h"
 #include "dyldLibSystemInterface.h"
+#include "DyldSharedCache.h"
 
 #undef _POSIX_C_SOURCE
 #include "dlfcn.h"
 
 
 #undef _POSIX_C_SOURCE
 #include "dlfcn.h"
 
+
+// this was in dyld_priv.h but it is no longer exported
+extern "C" {
+    const struct dyld_all_image_infos* _dyld_get_all_image_infos();
+}
+
 // from dyldExceptions.c
 extern "C" void __Unwind_SjLj_SetThreadKey(pthread_key_t key);
 
 // from dyldExceptions.c
 extern "C" void __Unwind_SjLj_SetThreadKey(pthread_key_t key);
 
@@ -67,6 +74,7 @@ extern uint32_t allImagesCount();
 extern const mach_header* allImagesIndexedMachHeader(uint32_t index);
 extern const char* allImagesIndexedPath(uint32_t index);
 
 extern const mach_header* allImagesIndexedMachHeader(uint32_t index);
 extern const char* allImagesIndexedPath(uint32_t index);
 
+extern "C" int _dyld_func_lookup(const char* name, void** address);
 
 // deprecated APIs are still availble on Mac OS X, but not on iPhone OS
 #if __IPHONE_OS_VERSION_MIN_REQUIRED   
 
 // deprecated APIs are still availble on Mac OS X, but not on iPhone OS
 #if __IPHONE_OS_VERSION_MIN_REQUIRED   
@@ -153,9 +161,7 @@ static struct dyld_func dyld_funcs[] = {
        {"__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 },
        {"__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 },
        {"__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_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 },
@@ -171,7 +177,6 @@ static struct dyld_func dyld_funcs[] = {
     {"__dyld_install_handlers",                                                (void*)_dyld_install_handlers },
     {"__dyld_link_edit_error",                                         (void*)NSLinkEditError },
     {"__dyld_unlink_module",                                           (void*)NSUnLinkModule },
     {"__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_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 },
@@ -198,7 +203,6 @@ static struct dyld_func dyld_funcs[] = {
     {"__dyld_NSCreateObjectFileImageFromMemory",               (void*)NSCreateObjectFileImageFromMemory },
     {"__dyld_NSDestroyObjectFileImage",                                        (void*)NSDestroyObjectFileImage },
     {"__dyld_NSLinkModule",                                                            (void*)NSLinkModule },
     {"__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_NSSymbolDefinitionCountInObjectFileImage",        (void*)NSSymbolDefinitionCountInObjectFileImage },
     {"__dyld_NSSymbolDefinitionNameInObjectFileImage", (void*)NSSymbolDefinitionNameInObjectFileImage },
     {"__dyld_NSIsSymbolDefinedInObjectFileImage",              (void*)NSIsSymbolDefinedInObjectFileImage },
@@ -734,14 +738,6 @@ bool _dyld_all_twolevel_modules_prebound(void)
        return FALSE; 
 }
 
        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 )
 bool _dyld_bind_fully_image_containing_address(const void* address)
 {
        if ( dyld::gLogAPIs )
@@ -849,8 +845,8 @@ NSObjectFileImageReturnCode NSCreateObjectFileImageFromMemory(const void* addres
 
 static bool validOFI(NSObjectFileImage objectFileImage)
 {
 
 static bool validOFI(NSObjectFileImage objectFileImage)
 {
-       const int ofiCount = sObjectFileImages.size();
-       for (int i=0; i < ofiCount; ++i) {
+       const size_t ofiCount = sObjectFileImages.size();
+       for (size_t i=0; i < ofiCount; ++i) {
                if ( sObjectFileImages[i] == objectFileImage )
                        return true;
        }
                if ( sObjectFileImages[i] == objectFileImage )
                        return true;
        }
@@ -905,13 +901,6 @@ bool NSDestroyObjectFileImage(NSObjectFileImage objectFileImage)
        return false;
 }
 
        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 )
 uint32_t NSSymbolDefinitionCountInObjectFileImage(NSObjectFileImage objectFileImage)
 {
        if ( dyld::gLogAPIs )
@@ -1145,9 +1134,9 @@ bool NSUnLinkModule(NSModule module, uint32_t options)
 
        // 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       
 
        // 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();
+       const size_t ofiCount = sObjectFileImages.size();
        bool found = false;
        bool found = false;
-       for (int i=0; i < ofiCount; ++i) {
+       for (size_t i=0; i < ofiCount; ++i) {
                NSObjectFileImage ofi = sObjectFileImages[i];
                if ( ofi->image == image )
                        found = true;
                NSObjectFileImage ofi = sObjectFileImages[i];
                if ( ofi->image == image )
                        found = true;
@@ -1346,12 +1335,10 @@ bool dlopen_preflight(const char* path)
                return true;
 #endif
 
                return true;
 #endif
 
-#if DYLD_SHARED_CACHE_SUPPORT
        // <rdar://problem/5910137> 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;
        // <rdar://problem/5910137> 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<const char*> rpathsFromCallerImage;
        
        bool result = false;
        std::vector<const char*> rpathsFromCallerImage;
@@ -1520,12 +1507,13 @@ void* dlopen(const char* path, int mode)
                image = load(path, context, cacheIndex);
 #if SUPPORT_ACCELERATE_TABLES
                if ( (image != NULL) && (cacheIndex != UINT32_MAX) ) {
                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;
+            // found in cache, but under a different path
+            const char* betterPath = dyld::getPathFromIndex(cacheIndex);
+            if ( (betterPath != NULL) && dyld::dlopenFromCache(betterPath, mode, &result) ) {
+                // Note: dlopenFromCache() releases the lock
+                if ( dyld::gLogAPIs )
+                    dyld::log("  %s(%s) ==> %p\n", __func__, path, result);
+                return result;
                        }
                }
 #endif
                        }
                }
 #endif
@@ -1950,11 +1938,7 @@ const char* dyld_image_path_containing_address(const void* address)
 
 bool dyld_shared_cache_some_image_overridden()
 {
 
 bool dyld_shared_cache_some_image_overridden()
 {
- #if DYLD_SHARED_CACHE_SUPPORT
        return dyld::gSharedCacheOverridden;
        return dyld::gSharedCacheOverridden;
- #else
-    return true;
- #endif
 }
 
 
 }
 
 
@@ -1992,15 +1976,15 @@ bool _dyld_is_memory_immutable(const void* addr, size_t length)
        uintptr_t checkStart = (uintptr_t)addr;
        uintptr_t checkEnd   = checkStart + 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.
        // 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;
+    const DyldSharedCache* cache = (DyldSharedCache*)dyld::imMemorySharedCacheHeader();
+    if ( cache != nullptr ) {
+        const dyld_cache_mapping_info* const mappings = (dyld_cache_mapping_info*)((char*)cache + cache->header.mappingOffset);
+               uintptr_t roStart    = (uintptr_t)cache;
+               uintptr_t roEnd      = roStart + (uintptr_t)mappings[0].size;
                if ( (roStart < checkStart) && (checkEnd < roEnd) )
                        return true;
                if ( (roStart < checkStart) && (checkEnd < roEnd) )
                        return true;
-       }
-#endif
+    }
 
        // Otherwise find if addr is in a dyld loaded image
        ImageLoader* image = dyld::findImageContainingAddress(addr);
 
        // Otherwise find if addr is in a dyld loaded image
        ImageLoader* image = dyld::findImageContainingAddress(addr);
@@ -2036,13 +2020,13 @@ bool _dyld_get_shared_cache_uuid(uuid_t uuid)
 
 const void* _dyld_get_shared_cache_range(size_t* length)
 {
 
 const void* _dyld_get_shared_cache_range(size_t* length)
 {
-#if DYLD_SHARED_CACHE_SUPPORT
-       uintptr_t cacheEndAddr = (dyld_shared_cache_ranges.ranges[2].start + dyld_shared_cache_ranges.ranges[2].length);
-       *length = cacheEndAddr - dyld_shared_cache_ranges.ranges[0].start;
-       return (void*)(dyld_shared_cache_ranges.ranges[0].start);
-#else
-       return NULL;
-#endif
+    const DyldSharedCache* cache = (DyldSharedCache*)dyld::imMemorySharedCacheHeader();
+    if ( cache != nullptr ) {
+        const dyld_cache_mapping_info* const mappings = (dyld_cache_mapping_info*)((char*)cache + cache->header.mappingOffset);
+        *length = (size_t)((mappings[2].address + mappings[2].size) - mappings[0].address);
+        return cache;
+    }
+       return nullptr;
 }
 
 
 }
 
 
index 35abc2a2c617a62c4ebd2d0621910ea27b53f636..6d7247297fe4c788eca89843010d623c55706cfd 100644 (file)
 #include "dyldLock.h"
 #include "start_glue.h"
 
 #include "dyldLock.h"
 #include "start_glue.h"
 
+#include "../dyld3/APIs.h"
+#include "../dyld3/AllImages.h"
+
+
+// this was in dyld_priv.h but it is no longer exported
+extern "C" {
+    const struct dyld_all_image_infos* _dyld_get_all_image_infos() __attribute__((visibility("hidden")));
+}
+
+
 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);
 
 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);
 
+//
+// private interface between libSystem.dylib and dyld
+//
+extern "C" int _dyld_func_lookup(const char* dyld_func_name, void **address);
+
+
+extern bool gUseDyld3;
 
 #ifndef LC_VERSION_MIN_MACOSX
        #define LC_VERSION_MIN_MACOSX 0x24
 
 #ifndef LC_VERSION_MIN_MACOSX
        #define LC_VERSION_MIN_MACOSX 0x24
@@ -76,6 +93,42 @@ extern "C" void __cxa_finalize_ranges(const struct __cxa_range_t ranges[], int c
        #define LC_LOAD_UPWARD_DYLIB (0x23|LC_REQ_DYLD) /* load of dylib whose initializers run later */
 #endif
 
        #define LC_LOAD_UPWARD_DYLIB (0x23|LC_REQ_DYLD) /* load of dylib whose initializers run later */
 #endif
 
+#ifndef LC_BUILD_VERSION
+       #define LC_BUILD_VERSION 0x32 /* build for platform min OS version */
+
+       /*
+        * The build_version_command contains the min OS version on which this 
+        * binary was built to run for its platform.  The list of known platforms and
+        * tool values following it.
+        */
+       struct build_version_command {
+               uint32_t        cmd;            /* LC_BUILD_VERSION */
+               uint32_t        cmdsize;        /* sizeof(struct build_version_command) plus */
+                                                               /* ntools * sizeof(struct build_tool_version) */
+               uint32_t        platform;       /* platform */
+               uint32_t        minos;          /* X.Y.Z is encoded in nibbles xxxx.yy.zz */
+               uint32_t        sdk;            /* X.Y.Z is encoded in nibbles xxxx.yy.zz */
+               uint32_t        ntools;         /* number of tool entries following this */
+       };
+
+       struct build_tool_version {
+               uint32_t        tool;           /* enum for the tool */
+               uint32_t        version;        /* version number of the tool */
+       };
+
+       /* Known values for the platform field above. */
+       #define PLATFORM_MACOS          1
+       #define PLATFORM_IOS            2
+       #define PLATFORM_TVOS           3
+       #define PLATFORM_WATCHOS        4
+       #define PLATFORM_BRIDGEOS       5
+
+       /* Known values for the tool field above. */
+       #define TOOL_CLANG      1
+       #define TOOL_SWIFT      2
+       #define TOOL_LD         3
+#endif
+
 
 // deprecated APIs are still availble on Mac OS X, but not on iPhone OS
 #if __IPHONE_OS_VERSION_MIN_REQUIRED   
 
 // deprecated APIs are still availble on Mac OS X, but not on iPhone OS
 #if __IPHONE_OS_VERSION_MIN_REQUIRED   
@@ -142,6 +195,9 @@ const char* libraryName)
 void NSInstallLinkEditErrorHandlers(
 const NSLinkEditErrorHandlers* handlers)
 {
 void NSInstallLinkEditErrorHandlers(
 const NSLinkEditErrorHandlers* handlers)
 {
+       if ( gUseDyld3 )
+               return dyld3::NSInstallLinkEditErrorHandlers(handlers);
+
        DYLD_LOCK_THIS_BLOCK;
        typedef void (*ucallback_t)(const char* symbol_name);
        typedef NSModule (*mcallback_t)(NSSymbol s, NSModule old, NSModule newhandler);
        DYLD_LOCK_THIS_BLOCK;
        typedef void (*ucallback_t)(const char* symbol_name);
        typedef NSModule (*mcallback_t)(NSSymbol s, NSModule old, NSModule newhandler);
@@ -159,6 +215,9 @@ const char*
 NSNameOfModule(
 NSModule module)
 {
 NSNameOfModule(
 NSModule module)
 {
+       if ( gUseDyld3 )
+               return dyld3::NSNameOfModule(module);
+
        DYLD_LOCK_THIS_BLOCK;
     static const char*  (*p)(NSModule module) = NULL;
 
        DYLD_LOCK_THIS_BLOCK;
     static const char*  (*p)(NSModule module) = NULL;
 
@@ -171,6 +230,9 @@ const char*
 NSLibraryNameForModule(
 NSModule module)
 {
 NSLibraryNameForModule(
 NSModule module)
 {
+       if ( gUseDyld3 )
+               return dyld3::NSLibraryNameForModule(module);
+
        DYLD_LOCK_THIS_BLOCK;
     static const char*  (*p)(NSModule module) = NULL;
 
        DYLD_LOCK_THIS_BLOCK;
     static const char*  (*p)(NSModule module) = NULL;
 
@@ -183,6 +245,9 @@ bool
 NSIsSymbolNameDefined(
 const char* symbolName)
 {
 NSIsSymbolNameDefined(
 const char* symbolName)
 {
+       if ( gUseDyld3 )
+               return dyld3::NSIsSymbolNameDefined(symbolName);
+
        DYLD_LOCK_THIS_BLOCK;
     static bool (*p)(const char* symbolName) = NULL;
 
        DYLD_LOCK_THIS_BLOCK;
     static bool (*p)(const char* symbolName) = NULL;
 
@@ -196,6 +261,9 @@ NSIsSymbolNameDefinedWithHint(
 const char* symbolName,
 const char* libraryNameHint)
 {
 const char* symbolName,
 const char* libraryNameHint)
 {
+       if ( gUseDyld3 )
+               return dyld3::NSIsSymbolNameDefinedWithHint(symbolName, libraryNameHint);
+
        DYLD_LOCK_THIS_BLOCK;
     static bool (*p)(const char* symbolName,
                          const char* libraryNameHint) = NULL;
        DYLD_LOCK_THIS_BLOCK;
     static bool (*p)(const char* symbolName,
                          const char* libraryNameHint) = NULL;
@@ -210,6 +278,9 @@ NSIsSymbolNameDefinedInImage(
 const struct mach_header *image,
 const char* symbolName)
 {
 const struct mach_header *image,
 const char* symbolName)
 {
+       if ( gUseDyld3 )
+               return dyld3::NSIsSymbolNameDefinedInImage(image, symbolName);
+
        DYLD_LOCK_THIS_BLOCK;
     static bool (*p)(const struct mach_header *image,
                          const char* symbolName) = NULL;
        DYLD_LOCK_THIS_BLOCK;
     static bool (*p)(const struct mach_header *image,
                          const char* symbolName) = NULL;
@@ -223,6 +294,9 @@ NSSymbol
 NSLookupAndBindSymbol(
 const char* symbolName)
 {
 NSLookupAndBindSymbol(
 const char* symbolName)
 {
+       if ( gUseDyld3 )
+               return dyld3::NSLookupAndBindSymbol(symbolName);
+
        DYLD_LOCK_THIS_BLOCK;
     static NSSymbol (*p)(const char* symbolName) = NULL;
 
        DYLD_LOCK_THIS_BLOCK;
     static NSSymbol (*p)(const char* symbolName) = NULL;
 
@@ -236,6 +310,9 @@ NSLookupAndBindSymbolWithHint(
 const char* symbolName,
 const char* libraryNameHint)
 {
 const char* symbolName,
 const char* libraryNameHint)
 {
+       if ( gUseDyld3 )
+               return dyld3::NSLookupAndBindSymbolWithHint(symbolName, libraryNameHint);
+
        DYLD_LOCK_THIS_BLOCK;
     static NSSymbol (*p)(const char* symbolName,
                         const char* libraryNameHint) = NULL;
        DYLD_LOCK_THIS_BLOCK;
     static NSSymbol (*p)(const char* symbolName,
                         const char* libraryNameHint) = NULL;
@@ -250,6 +327,9 @@ NSLookupSymbolInModule(
 NSModule module,
 const char* symbolName)
 {
 NSModule module,
 const char* symbolName)
 {
+       if ( gUseDyld3 )
+               return dyld3::NSLookupSymbolInModule(module, symbolName);
+
        DYLD_LOCK_THIS_BLOCK;
     static NSSymbol (*p)(NSModule module, const char* symbolName) = NULL;
 
        DYLD_LOCK_THIS_BLOCK;
     static NSSymbol (*p)(NSModule module, const char* symbolName) = NULL;
 
@@ -264,8 +344,11 @@ const struct mach_header *image,
 const char* symbolName,
 uint32_t options)
 {
 const char* symbolName,
 uint32_t options)
 {
+       if ( gUseDyld3 )
+               return dyld3::NSLookupSymbolInImage(image, symbolName, options);
+
        DYLD_LOCK_THIS_BLOCK;
        DYLD_LOCK_THIS_BLOCK;
-   static NSSymbol (*p)(const struct mach_header *image,
+    static NSSymbol (*p)(const struct mach_header *image,
                         const char* symbolName,
                         uint32_t options) = NULL;
 
                         const char* symbolName,
                         uint32_t options) = NULL;
 
@@ -278,6 +361,9 @@ const char*
 NSNameOfSymbol(
 NSSymbol symbol)
 {
 NSNameOfSymbol(
 NSSymbol symbol)
 {
+       if ( gUseDyld3 )
+               return dyld3::NSNameOfSymbol(symbol);
+
        DYLD_LOCK_THIS_BLOCK;
     static char * (*p)(NSSymbol symbol) = NULL;
 
        DYLD_LOCK_THIS_BLOCK;
     static char * (*p)(NSSymbol symbol) = NULL;
 
@@ -290,6 +376,9 @@ void *
 NSAddressOfSymbol(
 NSSymbol symbol)
 {
 NSAddressOfSymbol(
 NSSymbol symbol)
 {
+       if ( gUseDyld3 )
+               return dyld3::NSAddressOfSymbol(symbol);
+
        DYLD_LOCK_THIS_BLOCK;
     static void * (*p)(NSSymbol symbol) = NULL;
 
        DYLD_LOCK_THIS_BLOCK;
     static void * (*p)(NSSymbol symbol) = NULL;
 
@@ -302,6 +391,9 @@ NSModule
 NSModuleForSymbol(
 NSSymbol symbol)
 {
 NSModuleForSymbol(
 NSSymbol symbol)
 {
+       if ( gUseDyld3 )
+               return dyld3::NSModuleForSymbol(symbol);
+
        DYLD_LOCK_THIS_BLOCK;
     static NSModule (*p)(NSSymbol symbol) = NULL;
 
        DYLD_LOCK_THIS_BLOCK;
     static NSModule (*p)(NSSymbol symbol) = NULL;
 
@@ -314,6 +406,9 @@ bool
 NSAddLibrary(
 const char* pathName)
 {
 NSAddLibrary(
 const char* pathName)
 {
+       if ( gUseDyld3 )
+               return dyld3::NSAddLibrary(pathName);
+
        DYLD_LOCK_THIS_BLOCK;
     static bool (*p)(const char* pathName) = NULL;
 
        DYLD_LOCK_THIS_BLOCK;
     static bool (*p)(const char* pathName) = NULL;
 
@@ -326,6 +421,9 @@ bool
 NSAddLibraryWithSearching(
 const char* pathName)
 {
 NSAddLibraryWithSearching(
 const char* pathName)
 {
+       if ( gUseDyld3 )
+               return dyld3::NSAddLibrary(pathName);
+
        DYLD_LOCK_THIS_BLOCK;
     static bool (*p)(const char* pathName) = NULL;
 
        DYLD_LOCK_THIS_BLOCK;
     static bool (*p)(const char* pathName) = NULL;
 
@@ -339,6 +437,9 @@ NSAddImage(
 const char* image_name,
 uint32_t options)
 {
 const char* image_name,
 uint32_t options)
 {
+       if ( gUseDyld3 )
+               return dyld3::NSAddImage(image_name, options);
+
        DYLD_LOCK_THIS_BLOCK;
     static const struct mach_header * (*p)(const char* image_name,
                                           uint32_t options) = NULL;
        DYLD_LOCK_THIS_BLOCK;
     static const struct mach_header * (*p)(const char* image_name,
                                           uint32_t options) = NULL;
@@ -362,6 +463,9 @@ uint32_t options)
  */
 int32_t NSVersionOfLinkTimeLibrary(const char* libraryName)
 {
  */
 int32_t NSVersionOfLinkTimeLibrary(const char* libraryName)
 {
+       if ( gUseDyld3 )
+               return dyld3::NSVersionOfLinkTimeLibrary(libraryName);
+
        // Lazily call _NSGetMachExecuteHeader() and cache result
 #if __LP64__
     static mach_header_64* mh = NULL;
        // Lazily call _NSGetMachExecuteHeader() and cache result
 #if __LP64__
     static mach_header_64* mh = NULL;
@@ -400,6 +504,9 @@ int32_t NSVersionOfLinkTimeLibrary(const char* libraryName)
  */
 int32_t NSVersionOfRunTimeLibrary(const char* libraryName)
 {
  */
 int32_t NSVersionOfRunTimeLibrary(const char* libraryName)
 {
+       if ( gUseDyld3 )
+               return dyld3::NSVersionOfRunTimeLibrary(libraryName);
+
        uint32_t n = _dyld_image_count();
        for(uint32_t i = 0; i < n; i++){
            const mach_header* mh = _dyld_get_image_header(i);
        uint32_t n = _dyld_image_count();
        for(uint32_t i = 0; i < n; i++){
            const mach_header* mh = _dyld_get_image_header(i);
@@ -429,7 +536,7 @@ int32_t NSVersionOfRunTimeLibrary(const char* libraryName)
 #define PACKED_VERSION(major, minor, tiny) ((((major) & 0xffff) << 16) | (((minor) & 0xff) << 8) | ((tiny) & 0xff))
 
 
 #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)
+static bool getVersionLoadCommandInfo(const mach_header* mh, uint32_t* platform, uint32_t* minOS, uint32_t* sdk)
 {
        const load_command* startCmds = NULL;
        if ( mh->magic == MH_MAGIC_64 )
 {
        const load_command* startCmds = NULL;
        if ( mh->magic == MH_MAGIC_64 )
@@ -447,15 +554,37 @@ static bool getVersionLoadCommandInfo(const mach_header* mh, uint32_t* loadComma
                        return 0;
                }
                const version_min_command* versCmd;
                        return 0;
                }
                const version_min_command* versCmd;
+               const build_version_command* buildVersCmd;
                switch ( cmd->cmd ) {
                        case LC_VERSION_MIN_IPHONEOS:
                switch ( cmd->cmd ) {
                        case LC_VERSION_MIN_IPHONEOS:
+                               versCmd = (version_min_command*)cmd;
+                               *platform       = PLATFORM_IOS;
+                               *minOS          = versCmd->version;
+                               *sdk            = versCmd->sdk;
+                               return true;
                        case LC_VERSION_MIN_MACOSX:
                        case LC_VERSION_MIN_MACOSX:
+                               versCmd = (version_min_command*)cmd;
+                               *platform       = PLATFORM_MACOS;
+                               *minOS          = versCmd->version;
+                               *sdk            = versCmd->sdk;
+                               return true;
                        case LC_VERSION_MIN_TVOS:
                        case LC_VERSION_MIN_TVOS:
+                               versCmd = (version_min_command*)cmd;
+                               *platform       = PLATFORM_TVOS;
+                               *minOS          = versCmd->version;
+                               *sdk            = versCmd->sdk;
+                               return true;
                        case LC_VERSION_MIN_WATCHOS:
                                versCmd = (version_min_command*)cmd;
                        case LC_VERSION_MIN_WATCHOS:
                                versCmd = (version_min_command*)cmd;
-                               *loadCommand = versCmd->cmd;
-                               *minOS = versCmd->version;
-                               *sdk = versCmd->sdk;
+                               *platform       = PLATFORM_WATCHOS;
+                               *minOS          = versCmd->version;
+                               *sdk            = versCmd->sdk;
+                               return true;
+                       case LC_BUILD_VERSION:
+                               buildVersCmd = (build_version_command*)cmd;
+                               *platform        = buildVersCmd->platform;
+                               *minOS           = buildVersCmd->minos;
+                               *sdk             = buildVersCmd->sdk;
                                return true;
                }
                cmd = nextCmd;
                                return true;
                }
                cmd = nextCmd;
@@ -589,27 +718,71 @@ static uint32_t watchVersToIOSVers(uint32_t vers)
 
 uint32_t dyld_get_program_sdk_watch_os_version()
 {
 
 uint32_t dyld_get_program_sdk_watch_os_version()
 {
+       if ( gUseDyld3 )
+               return dyld3::dyld_get_program_sdk_watch_os_version();
+
        const mach_header* mh = (mach_header*)_NSGetMachExecuteHeader();
        const mach_header* mh = (mach_header*)_NSGetMachExecuteHeader();
-       uint32_t loadCommand;
+       uint32_t platform;
        uint32_t minOS;
        uint32_t sdk;
 
        uint32_t minOS;
        uint32_t sdk;
 
-       if ( getVersionLoadCommandInfo(mh, &loadCommand, &minOS, &sdk) ) {
-               if ( loadCommand == LC_VERSION_MIN_WATCHOS )
+       if ( getVersionLoadCommandInfo(mh, &platform, &minOS, &sdk) ) {
+               if ( platform == PLATFORM_WATCHOS )
                                return sdk;
        }
        return 0;
 }
 
 uint32_t dyld_get_program_min_watch_os_version()
                                return sdk;
        }
        return 0;
 }
 
 uint32_t dyld_get_program_min_watch_os_version()
+{
+       if ( gUseDyld3 )
+               return dyld3::dyld_get_program_min_watch_os_version();
+
+       const mach_header* mh = (mach_header*)_NSGetMachExecuteHeader();
+       uint32_t platform;
+       uint32_t minOS;
+       uint32_t sdk;
+
+       if ( getVersionLoadCommandInfo(mh, &platform, &minOS, &sdk) ) {
+               if ( platform == PLATFORM_WATCHOS )
+                               return minOS;  // return raw minOS (not mapped to iOS version)
+       }
+       return 0;
+}
+
+#endif
+
+
+
+#if TARGET_OS_BRIDGE
+static uint32_t bridgeVersToIOSVers(uint32_t vers)
+{
+       return vers + 0x00090000;
+}
+
+uint32_t dyld_get_program_sdk_bridge_os_version()
+{
+       const mach_header* mh = (mach_header*)_NSGetMachExecuteHeader();
+       uint32_t platform;
+       uint32_t minOS;
+       uint32_t sdk;
+
+       if ( getVersionLoadCommandInfo(mh, &platform, &minOS, &sdk) ) {
+               if ( platform == PLATFORM_BRIDGEOS )
+                               return sdk;
+       }
+       return 0;
+}
+
+uint32_t dyld_get_program_min_bridge_os_version()
 {
        const mach_header* mh = (mach_header*)_NSGetMachExecuteHeader();
 {
        const mach_header* mh = (mach_header*)_NSGetMachExecuteHeader();
-       uint32_t loadCommand;
+       uint32_t platform;
        uint32_t minOS;
        uint32_t sdk;
 
        uint32_t minOS;
        uint32_t sdk;
 
-       if ( getVersionLoadCommandInfo(mh, &loadCommand, &minOS, &sdk) ) {
-               if ( loadCommand == LC_VERSION_MIN_WATCHOS )
+       if ( getVersionLoadCommandInfo(mh, &platform, &minOS, &sdk) ) {
+               if ( platform == PLATFORM_BRIDGEOS )
                                return minOS;  // return raw minOS (not mapped to iOS version)
        }
        return 0;
                                return minOS;  // return raw minOS (not mapped to iOS version)
        }
        return 0;
@@ -628,30 +801,40 @@ uint32_t dyld_get_program_min_watch_os_version()
  */
 uint32_t dyld_get_sdk_version(const mach_header* mh)
 {
  */
 uint32_t dyld_get_sdk_version(const mach_header* mh)
 {
-       uint32_t loadCommand;
+       if ( gUseDyld3 )
+               return dyld3::dyld_get_sdk_version(mh);
+
+    uint32_t platform;
        uint32_t minOS;
        uint32_t sdk;
 
        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:
+       if ( getVersionLoadCommandInfo(mh, &platform, &minOS, &sdk) ) {
+               switch (platform) {
+#if TARGET_OS_BRIDGE
+                       case PLATFORM_BRIDGEOS:
+                               // new binary. sdk version looks like "2.0" but API wants "11.0"
+                               return bridgeVersToIOSVers(sdk);
+                       case PLATFORM_IOS:
+                               // old binary. sdk matches API semantics so can return directly.
+                               return sdk;
+#elif __WATCH_OS_VERSION_MIN_REQUIRED
+                       case PLATFORM_WATCHOS:
                                // new binary. sdk version looks like "2.0" but API wants "9.0"
                                return watchVersToIOSVers(sdk);
                                // new binary. sdk version looks like "2.0" but API wants "9.0"
                                return watchVersToIOSVers(sdk);
-                       case LC_VERSION_MIN_IPHONEOS:
+                       case PLATFORM_IOS:
                                // old binary. sdk matches API semantics so can return directly.
                                return sdk;
 #elif __TV_OS_VERSION_MIN_REQUIRED
                                // 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:
+                       case PLATFORM_TVOS:
+                       case PLATFORM_IOS:
                                return sdk;
 #elif __IPHONE_OS_VERSION_MIN_REQUIRED
                                return sdk;
 #elif __IPHONE_OS_VERSION_MIN_REQUIRED
-                       case LC_VERSION_MIN_IPHONEOS:
+                       case PLATFORM_IOS:
                                if ( sdk != 0 ) // old binaries might not have SDK set
                                        return sdk;
                                break;
 #else
                                if ( sdk != 0 ) // old binaries might not have SDK set
                                        return sdk;
                                break;
 #else
-                       case LC_VERSION_MIN_MACOSX:
+                       case PLATFORM_MACOS:
                                if ( sdk != 0 ) // old binaries might not have SDK set
                                        return sdk;
                                break;
                                if ( sdk != 0 ) // old binaries might not have SDK set
                                        return sdk;
                                break;
@@ -659,7 +842,7 @@ uint32_t dyld_get_sdk_version(const mach_header* mh)
                }
        }
 
                }
        }
 
-#if __WATCH_OS_VERSION_MIN_REQUIRED ||__TV_OS_VERSION_MIN_REQUIRED
+#if __WATCH_OS_VERSION_MIN_REQUIRED || __TV_OS_VERSION_MIN_REQUIRED || TARGET_OS_BRIDGE
        // All WatchOS and tv OS binaries should have version load command.
        return 0;
 #else
        // All WatchOS and tv OS binaries should have version load command.
        return 0;
 #else
@@ -670,33 +853,46 @@ uint32_t dyld_get_sdk_version(const mach_header* mh)
 
 uint32_t dyld_get_program_sdk_version()
 {
 
 uint32_t dyld_get_program_sdk_version()
 {
+       if ( gUseDyld3 )
+               return dyld3::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)
 {
        return dyld_get_sdk_version((mach_header*)_NSGetMachExecuteHeader());
 }
 
 uint32_t dyld_get_min_os_version(const struct mach_header* mh)
 {
-       uint32_t loadCommand;
+       if ( gUseDyld3 )
+               return dyld3::dyld_get_min_os_version(mh);
+
+       uint32_t platform;
        uint32_t minOS;
        uint32_t sdk;
 
        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:
+       if ( getVersionLoadCommandInfo(mh, &platform, &minOS, &sdk) ) {
+               switch (platform) {
+#if TARGET_OS_BRIDGE
+                       case PLATFORM_BRIDGEOS:
+                               // new binary. sdk version looks like "2.0" but API wants "11.0"
+                               return bridgeVersToIOSVers(minOS);
+                       case PLATFORM_IOS:
+                               // old binary. sdk matches API semantics so can return directly.
+                               return minOS;
+#elif __WATCH_OS_VERSION_MIN_REQUIRED
+                       case PLATFORM_WATCHOS:
                                // new binary. OS version looks like "2.0" but API wants "9.0"
                                return watchVersToIOSVers(minOS);
                                // new binary. OS version looks like "2.0" but API wants "9.0"
                                return watchVersToIOSVers(minOS);
-                       case LC_VERSION_MIN_IPHONEOS:
+                       case PLATFORM_IOS:
                                // old binary. OS matches API semantics so can return directly.
                                return minOS;
 #elif __TV_OS_VERSION_MIN_REQUIRED
                                // 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:
+                       case PLATFORM_TVOS:
+                       case PLATFORM_IOS:
                                return minOS;
 #elif __IPHONE_OS_VERSION_MIN_REQUIRED
                                return minOS;
 #elif __IPHONE_OS_VERSION_MIN_REQUIRED
-                       case LC_VERSION_MIN_IPHONEOS:
+                       case PLATFORM_IOS:
                                return minOS;
 #else
                                return minOS;
 #else
-                       case LC_VERSION_MIN_MACOSX:
+                       case PLATFORM_MACOS:
                                return minOS;
 #endif
                }
                                return minOS;
 #endif
                }
@@ -707,12 +903,18 @@ uint32_t dyld_get_min_os_version(const struct mach_header* mh)
 
 uint32_t dyld_get_program_min_os_version()
 {
 
 uint32_t dyld_get_program_min_os_version()
 {
+       if ( gUseDyld3 )
+               return dyld3::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)
 {
        return dyld_get_min_os_version((mach_header*)_NSGetMachExecuteHeader());
 }
 
 
 bool _dyld_get_image_uuid(const struct mach_header* mh, uuid_t uuid)
 {
+       if ( gUseDyld3 )
+               return dyld3::_dyld_get_image_uuid(mh, uuid);
+
        const load_command* startCmds = NULL;
        if ( mh->magic == MH_MAGIC_64 )
                startCmds = (load_command*)((char *)mh + sizeof(mach_header_64));
        const load_command* startCmds = NULL;
        if ( mh->magic == MH_MAGIC_64 )
                startCmds = (load_command*)((char *)mh + sizeof(mach_header_64));
@@ -754,6 +956,9 @@ NSCreateObjectFileImageFromFile(
 const char* pathName,
 NSObjectFileImage *objectFileImage)
 {
 const char* pathName,
 NSObjectFileImage *objectFileImage)
 {
+       if ( gUseDyld3 )
+               return dyld3::NSCreateObjectFileImageFromFile(pathName, objectFileImage);
+
        DYLD_LOCK_THIS_BLOCK;
     static NSObjectFileImageReturnCode (*p)(const char*, NSObjectFileImage*) = NULL;
 
        DYLD_LOCK_THIS_BLOCK;
     static NSObjectFileImageReturnCode (*p)(const char*, NSObjectFileImage*) = NULL;
 
@@ -776,6 +981,9 @@ const void* address,
 size_t size, 
 NSObjectFileImage *objectFileImage)
 {
 size_t size, 
 NSObjectFileImage *objectFileImage)
 {
+       if ( gUseDyld3 )
+               return dyld3::NSCreateObjectFileImageFromMemory(address, size, objectFileImage);
+
        DYLD_LOCK_THIS_BLOCK;
     static NSObjectFileImageReturnCode (*p)(const void*, size_t, NSObjectFileImage*) = NULL;
 
        DYLD_LOCK_THIS_BLOCK;
     static NSObjectFileImageReturnCode (*p)(const void*, size_t, NSObjectFileImage*) = NULL;
 
@@ -809,6 +1017,9 @@ bool
 NSDestroyObjectFileImage(
 NSObjectFileImage objectFileImage)
 {
 NSDestroyObjectFileImage(
 NSObjectFileImage objectFileImage)
 {
+       if ( gUseDyld3 )
+               return dyld3::NSDestroyObjectFileImage(objectFileImage);
+
        DYLD_LOCK_THIS_BLOCK;
     static bool (*p)(NSObjectFileImage) = NULL;
 
        DYLD_LOCK_THIS_BLOCK;
     static bool (*p)(NSObjectFileImage) = NULL;
 
@@ -824,6 +1035,9 @@ NSObjectFileImage objectFileImage,
 const char* moduleName,
 uint32_t options)
 {
 const char* moduleName,
 uint32_t options)
 {
+       if ( gUseDyld3 )
+               return dyld3::NSLinkModule(objectFileImage, moduleName, options);
+
        DYLD_LOCK_THIS_BLOCK;
     static NSModule (*p)(NSObjectFileImage, const char*, unsigned long) = NULL;
 
        DYLD_LOCK_THIS_BLOCK;
     static NSModule (*p)(NSObjectFileImage, const char*, unsigned long) = NULL;
 
@@ -844,6 +1058,9 @@ uint32_t
 NSSymbolDefinitionCountInObjectFileImage(
 NSObjectFileImage objectFileImage)
 {
 NSSymbolDefinitionCountInObjectFileImage(
 NSObjectFileImage objectFileImage)
 {
+       if ( gUseDyld3 )
+               return dyld3::NSSymbolDefinitionCountInObjectFileImage(objectFileImage);
+
        DYLD_LOCK_THIS_BLOCK;
     static uint32_t (*p)(NSObjectFileImage) = NULL;
 
        DYLD_LOCK_THIS_BLOCK;
     static uint32_t (*p)(NSObjectFileImage) = NULL;
 
@@ -864,6 +1081,9 @@ NSSymbolDefinitionNameInObjectFileImage(
 NSObjectFileImage objectFileImage,
 uint32_t ordinal)
 {
 NSObjectFileImage objectFileImage,
 uint32_t ordinal)
 {
+       if ( gUseDyld3 )
+               return dyld3::NSSymbolDefinitionNameInObjectFileImage(objectFileImage, ordinal);
+
        DYLD_LOCK_THIS_BLOCK;
     static const char*  (*p)(NSObjectFileImage, uint32_t) = NULL;
 
        DYLD_LOCK_THIS_BLOCK;
     static const char*  (*p)(NSObjectFileImage, uint32_t) = NULL;
 
@@ -881,6 +1101,9 @@ uint32_t
 NSSymbolReferenceCountInObjectFileImage(
 NSObjectFileImage objectFileImage)
 {
 NSSymbolReferenceCountInObjectFileImage(
 NSObjectFileImage objectFileImage)
 {
+       if ( gUseDyld3 )
+               return dyld3::NSSymbolReferenceCountInObjectFileImage(objectFileImage);
+
        DYLD_LOCK_THIS_BLOCK;
     static uint32_t (*p)(NSObjectFileImage) = NULL;
 
        DYLD_LOCK_THIS_BLOCK;
     static uint32_t (*p)(NSObjectFileImage) = NULL;
 
@@ -902,6 +1125,9 @@ NSObjectFileImage objectFileImage,
 uint32_t ordinal,
 bool *tentative_definition) /* can be NULL */
 {
 uint32_t ordinal,
 bool *tentative_definition) /* can be NULL */
 {
+       if ( gUseDyld3 )
+               return dyld3::NSSymbolReferenceNameInObjectFileImage(objectFileImage, ordinal, tentative_definition);
+
        DYLD_LOCK_THIS_BLOCK;
     static const char*  (*p)(NSObjectFileImage, uint32_t, bool*) = NULL;
 
        DYLD_LOCK_THIS_BLOCK;
     static const char*  (*p)(NSObjectFileImage, uint32_t, bool*) = NULL;
 
@@ -920,6 +1146,9 @@ NSIsSymbolDefinedInObjectFileImage(
 NSObjectFileImage objectFileImage,
 const char* symbolName)
 {
 NSObjectFileImage objectFileImage,
 const char* symbolName)
 {
+       if ( gUseDyld3 )
+               return dyld3::NSIsSymbolDefinedInObjectFileImage(objectFileImage, symbolName);
+
        DYLD_LOCK_THIS_BLOCK;
     static bool (*p)(NSObjectFileImage, const char*) = NULL;
 
        DYLD_LOCK_THIS_BLOCK;
     static bool (*p)(NSObjectFileImage, const char*) = NULL;
 
@@ -943,6 +1172,9 @@ const char* segmentName,
 const char* sectionName,
 unsigned long *size) /* can be NULL */
 {
 const char* sectionName,
 unsigned long *size) /* can be NULL */
 {
+       if ( gUseDyld3 )
+               return dyld3::NSGetSectionDataInObjectFileImage(objectFileImage, segmentName, sectionName, size);
+
        DYLD_LOCK_THIS_BLOCK;
     static void* (*p)(NSObjectFileImage, const char*, const char*, unsigned long*) = NULL;
 
        DYLD_LOCK_THIS_BLOCK;
     static void* (*p)(NSObjectFileImage, const char*, const char*, unsigned long*) = NULL;
 
@@ -960,6 +1192,9 @@ int *errorNumber,
 const char* *fileName,
 const char* *errorString)
 {
 const char* *fileName,
 const char* *errorString)
 {
+       if ( gUseDyld3 )
+               return dyld3::NSLinkEditError(c, errorNumber, fileName, errorString);
+
        DYLD_LOCK_THIS_BLOCK;
     static void (*p)(NSLinkEditErrors *c,
                     int *errorNumber, 
        DYLD_LOCK_THIS_BLOCK;
     static void (*p)(NSLinkEditErrors *c,
                     int *errorNumber, 
@@ -977,6 +1212,9 @@ NSUnLinkModule(
 NSModule module, 
 uint32_t options)
 {
 NSModule module, 
 uint32_t options)
 {
+       if ( gUseDyld3 )
+               return dyld3::NSUnLinkModule(module, options);
+
        DYLD_LOCK_THIS_BLOCK;
     static bool (*p)(NSModule module, uint32_t options) = NULL;
 
        DYLD_LOCK_THIS_BLOCK;
     static bool (*p)(NSModule module, uint32_t options) = NULL;
 
@@ -1014,6 +1252,9 @@ _NSGetExecutablePath(
 char *buf,
 uint32_t *bufsize)
 {
 char *buf,
 uint32_t *bufsize)
 {
+       if ( gUseDyld3 )
+               return dyld3::_NSGetExecutablePath(buf, bufsize);
+
        DYLD_NO_LOCK_THIS_BLOCK;
     static int (*p)(char *buf, uint32_t *bufsize) = NULL;
 
        DYLD_NO_LOCK_THIS_BLOCK;
     static int (*p)(char *buf, uint32_t *bufsize) = NULL;
 
@@ -1106,6 +1347,9 @@ void
 _dyld_register_func_for_add_image(
 void (*func)(const struct mach_header *mh, intptr_t vmaddr_slide))
 {
 _dyld_register_func_for_add_image(
 void (*func)(const struct mach_header *mh, intptr_t vmaddr_slide))
 {
+       if ( gUseDyld3 )
+               return dyld3::_dyld_register_func_for_add_image(func);
+
        DYLD_LOCK_THIS_BLOCK;
        typedef void (*callback_t)(const struct mach_header *mh, intptr_t vmaddr_slide);
     static void (*p)(callback_t func) = NULL;
        DYLD_LOCK_THIS_BLOCK;
        typedef void (*callback_t)(const struct mach_header *mh, intptr_t vmaddr_slide);
     static void (*p)(callback_t func) = NULL;
@@ -1124,6 +1368,9 @@ void
 _dyld_register_func_for_remove_image(
 void (*func)(const struct mach_header *mh, intptr_t vmaddr_slide))
 {
 _dyld_register_func_for_remove_image(
 void (*func)(const struct mach_header *mh, intptr_t vmaddr_slide))
 {
+       if ( gUseDyld3 )
+               return dyld3::_dyld_register_func_for_remove_image(func);
+
        DYLD_LOCK_THIS_BLOCK;
        typedef void (*callback_t)(const struct mach_header *mh, intptr_t vmaddr_slide);
     static void (*p)(callback_t func) = NULL;
        DYLD_LOCK_THIS_BLOCK;
        typedef void (*callback_t)(const struct mach_header *mh, intptr_t vmaddr_slide);
     static void (*p)(callback_t func) = NULL;
@@ -1207,21 +1454,6 @@ unsigned long *size)
        p(module, objc_module, size);
 }
 
        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
 #endif
 
 #if DEPRECATED_APIS_SUPPORTED
@@ -1236,6 +1468,9 @@ _dyld_present(void)
 uint32_t
 _dyld_image_count(void)
 {
 uint32_t
 _dyld_image_count(void)
 {
+       if ( gUseDyld3 )
+               return dyld3::_dyld_image_count();
+
        DYLD_NO_LOCK_THIS_BLOCK;
     static uint32_t (*p)(void) = NULL;
 
        DYLD_NO_LOCK_THIS_BLOCK;
     static uint32_t (*p)(void) = NULL;
 
@@ -1247,6 +1482,9 @@ _dyld_image_count(void)
 const struct mach_header *
 _dyld_get_image_header(uint32_t image_index)
 {
 const struct mach_header *
 _dyld_get_image_header(uint32_t image_index)
 {
+       if ( gUseDyld3 )
+               return dyld3::_dyld_get_image_header(image_index);
+
        DYLD_NO_LOCK_THIS_BLOCK;
     static struct mach_header * (*p)(uint32_t image_index) = NULL;
 
        DYLD_NO_LOCK_THIS_BLOCK;
     static struct mach_header * (*p)(uint32_t image_index) = NULL;
 
@@ -1258,6 +1496,9 @@ _dyld_get_image_header(uint32_t image_index)
 intptr_t
 _dyld_get_image_vmaddr_slide(uint32_t image_index)
 {
 intptr_t
 _dyld_get_image_vmaddr_slide(uint32_t image_index)
 {
+       if ( gUseDyld3 )
+               return dyld3::_dyld_get_image_vmaddr_slide(image_index);
+
        DYLD_NO_LOCK_THIS_BLOCK;
     static unsigned long (*p)(uint32_t image_index) = NULL;
 
        DYLD_NO_LOCK_THIS_BLOCK;
     static unsigned long (*p)(uint32_t image_index) = NULL;
 
@@ -1269,6 +1510,9 @@ _dyld_get_image_vmaddr_slide(uint32_t image_index)
 const char* 
 _dyld_get_image_name(uint32_t image_index)
 {
 const char* 
 _dyld_get_image_name(uint32_t image_index)
 {
+       if ( gUseDyld3 )
+               return dyld3::_dyld_get_image_name(image_index);
+
        DYLD_NO_LOCK_THIS_BLOCK;
     static const char*  (*p)(uint32_t image_index) = NULL;
 
        DYLD_NO_LOCK_THIS_BLOCK;
     static const char*  (*p)(uint32_t image_index) = NULL;
 
@@ -1280,6 +1524,9 @@ _dyld_get_image_name(uint32_t image_index)
 // SPI in Mac OS X 10.6
 intptr_t _dyld_get_image_slide(const struct mach_header* mh)
 {
 // SPI in Mac OS X 10.6
 intptr_t _dyld_get_image_slide(const struct mach_header* mh)
 {
+       if ( gUseDyld3 )
+               return dyld3::_dyld_get_image_slide(mh);
+
        DYLD_NO_LOCK_THIS_BLOCK;
     static intptr_t (*p)(const struct mach_header*) = NULL;
 
        DYLD_NO_LOCK_THIS_BLOCK;
     static intptr_t (*p)(const struct mach_header*) = NULL;
 
@@ -1289,9 +1536,13 @@ intptr_t _dyld_get_image_slide(const struct mach_header* mh)
 }
 
 
 }
 
 
+#if DEPRECATED_APIS_SUPPORTED
 bool
 _dyld_image_containing_address(const void* address)
 {
 bool
 _dyld_image_containing_address(const void* address)
 {
+       if ( gUseDyld3 )
+               return dyld3::_dyld_image_containing_address(address);
+
        DYLD_LOCK_THIS_BLOCK;
     static bool (*p)(const void*) = NULL;
 
        DYLD_LOCK_THIS_BLOCK;
     static bool (*p)(const void*) = NULL;
 
@@ -1304,6 +1555,9 @@ const struct mach_header *
 _dyld_get_image_header_containing_address(
 const void* address)
 {
 _dyld_get_image_header_containing_address(
 const void* address)
 {
+       if ( gUseDyld3 )
+               return dyld3::_dyld_get_image_header_containing_address(address);
+
        DYLD_LOCK_THIS_BLOCK;
     static const struct mach_header * (*p)(const void*) = NULL;
 
        DYLD_LOCK_THIS_BLOCK;
     static const struct mach_header * (*p)(const void*) = NULL;
 
@@ -1312,8 +1566,6 @@ const void* address)
        return p(address);
 }
 
        return p(address);
 }
 
-
-#if DEPRECATED_APIS_SUPPORTED
 bool _dyld_launched_prebound(void)
 {
        DYLD_LOCK_THIS_BLOCK;
 bool _dyld_launched_prebound(void)
 {
        DYLD_LOCK_THIS_BLOCK;
@@ -1407,7 +1659,6 @@ static bool isLaunchdOwned()
        return ( val != 0 );
 }
 
        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_missing()
 {
        // leave until dyld's that might call this are rare
@@ -1417,17 +1668,12 @@ static void shared_cache_out_of_date()
 {
        // leave until dyld's that might call this are rare
 }
 {
        // 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,
 
 
 // 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,
                                                                        &shared_cache_missing, &shared_cache_out_of_date,
-                                               #else
-                                                                       NULL, NULL,
-                                               #endif
                                                                        NULL, NULL,
                                                                        &pthread_key_create, &pthread_setspecific,
                                                                        &malloc_size,
                                                                        NULL, NULL,
                                                                        &pthread_key_create, &pthread_setspecific,
                                                                        &malloc_size,
@@ -1447,21 +1693,28 @@ static dyld::LibSystemHelpers sHelpers = { 13, &dyldGlobalLockAcquire, &dyldGlob
 // and call dyld, registering the helper functions.
 //
 extern "C" void tlv_initializer();
 // and call dyld, registering the helper functions.
 //
 extern "C" void tlv_initializer();
-extern "C" void _dyld_initializer();
 void _dyld_initializer()
 {      
    void (*p)(dyld::LibSystemHelpers*);
 
 void _dyld_initializer()
 {      
    void (*p)(dyld::LibSystemHelpers*);
 
-       _dyld_func_lookup("__dyld_register_thread_helpers", (void**)&p);
-       if(p != NULL)
-               p(&sHelpers);
-               
+       if ( gUseDyld3 ) {
+               dyld3::gAllImages.applyInitialImages();
+       }
+       else {
+               _dyld_func_lookup("__dyld_register_thread_helpers", (void**)&p);
+               if(p != NULL)
+                       p(&sHelpers);
+       }
+
        tlv_initializer();
 }
 
 
 char* dlerror()
 {
        tlv_initializer();
 }
 
 
 char* dlerror()
 {
+       if ( gUseDyld3 )
+               return dyld3::dlerror();
+
        DYLD_LOCK_THIS_BLOCK;
     static char* (*p)() = NULL;
 
        DYLD_LOCK_THIS_BLOCK;
     static char* (*p)() = NULL;
 
@@ -1472,6 +1725,9 @@ char* dlerror()
 
 int dladdr(const void* addr, Dl_info* info)
 {
 
 int dladdr(const void* addr, Dl_info* info)
 {
+       if ( gUseDyld3 )
+               return dyld3::dladdr(addr, info);
+
        DYLD_LOCK_THIS_BLOCK;
     static int (*p)(const void* , Dl_info*) = NULL;
 
        DYLD_LOCK_THIS_BLOCK;
     static int (*p)(const void* , Dl_info*) = NULL;
 
@@ -1482,6 +1738,9 @@ int dladdr(const void* addr, Dl_info* info)
 
 int dlclose(void* handle)
 {
 
 int dlclose(void* handle)
 {
+       if ( gUseDyld3 )
+               return dyld3::dlclose(handle);
+
        DYLD_LOCK_THIS_BLOCK;
     static int (*p)(void* handle) = NULL;
 
        DYLD_LOCK_THIS_BLOCK;
     static int (*p)(void* handle) = NULL;
 
@@ -1492,6 +1751,9 @@ int dlclose(void* handle)
 
 void* dlopen(const char* path, int mode)
 {      
 
 void* dlopen(const char* path, int mode)
 {      
+       if ( gUseDyld3 )
+               return dyld3::dlopen(path, mode);
+
        // dlopen is special. locking is done inside dyld to allow initializer to run without lock
        DYLD_NO_LOCK_THIS_BLOCK;
        
        // dlopen is special. locking is done inside dyld to allow initializer to run without lock
        DYLD_NO_LOCK_THIS_BLOCK;
        
@@ -1510,6 +1772,9 @@ void* dlopen(const char* path, int mode)
 
 bool dlopen_preflight(const char* path)
 {
 
 bool dlopen_preflight(const char* path)
 {
+       if ( gUseDyld3 )
+               return dyld3::dlopen_preflight(path);
+
        DYLD_LOCK_THIS_BLOCK;
     static bool (*p)(const char* path) = NULL;
 
        DYLD_LOCK_THIS_BLOCK;
     static bool (*p)(const char* path) = NULL;
 
@@ -1520,6 +1785,9 @@ bool dlopen_preflight(const char* path)
 
 void* dlsym(void* handle, const char* symbol)
 {
 
 void* dlsym(void* handle, const char* symbol)
 {
+       if ( gUseDyld3 )
+               return dyld3::dlsym(handle, symbol);
+
        DYLD_LOCK_THIS_BLOCK;
     static void* (*p)(void* handle, const char* symbol) = NULL;
 
        DYLD_LOCK_THIS_BLOCK;
     static void* (*p)(void* handle, const char* symbol) = NULL;
 
@@ -1528,9 +1796,11 @@ void* dlsym(void* handle, const char* symbol)
        return(p(handle, symbol));
 }
 
        return(p(handle, symbol));
 }
 
-
 const struct dyld_all_image_infos* _dyld_get_all_image_infos()
 {
 const struct dyld_all_image_infos* _dyld_get_all_image_infos()
 {
+       if ( gUseDyld3 )
+               return dyld3::_dyld_get_all_image_infos();
+
        DYLD_NO_LOCK_THIS_BLOCK;
     static struct dyld_all_image_infos* (*p)() = NULL;
 
        DYLD_NO_LOCK_THIS_BLOCK;
     static struct dyld_all_image_infos* (*p)() = NULL;
 
@@ -1542,6 +1812,9 @@ const struct dyld_all_image_infos* _dyld_get_all_image_infos()
 #if SUPPORT_ZERO_COST_EXCEPTIONS
 bool _dyld_find_unwind_sections(void* addr, dyld_unwind_sections* info)
 {
 #if SUPPORT_ZERO_COST_EXCEPTIONS
 bool _dyld_find_unwind_sections(void* addr, dyld_unwind_sections* info)
 {
+       if ( gUseDyld3 )
+               return dyld3::_dyld_find_unwind_sections(addr, info);
+
        DYLD_NO_LOCK_THIS_BLOCK;
     static void* (*p)(void*, dyld_unwind_sections*) = NULL;
 
        DYLD_NO_LOCK_THIS_BLOCK;
     static void* (*p)(void*, dyld_unwind_sections*) = NULL;
 
@@ -1568,6 +1841,9 @@ void* _dyld_fast_stub_entry(void* loadercache, long lazyinfo)
 
 const char* dyld_image_path_containing_address(const void* addr)
 {
 
 const char* dyld_image_path_containing_address(const void* addr)
 {
+       if ( gUseDyld3 )
+               return dyld3::dyld_image_path_containing_address(addr);
+
        DYLD_NO_LOCK_THIS_BLOCK;
     static const char* (*p)(const void*) = NULL;
 
        DYLD_NO_LOCK_THIS_BLOCK;
     static const char* (*p)(const void*) = NULL;
 
@@ -1578,6 +1854,9 @@ const char* dyld_image_path_containing_address(const void* addr)
 
 const struct mach_header* dyld_image_header_containing_address(const void* addr)
 {
 
 const struct mach_header* dyld_image_header_containing_address(const void* addr)
 {
+       if ( gUseDyld3 )
+               return dyld3::dyld_image_header_containing_address(addr);
+
        DYLD_NO_LOCK_THIS_BLOCK;
     static const mach_header* (*p)(const void*) = NULL;
 
        DYLD_NO_LOCK_THIS_BLOCK;
     static const mach_header* (*p)(const void*) = NULL;
 
@@ -1589,6 +1868,9 @@ const struct mach_header* dyld_image_header_containing_address(const void* addr)
 
 bool dyld_shared_cache_some_image_overridden()
 {
 
 bool dyld_shared_cache_some_image_overridden()
 {
+       if ( gUseDyld3 )
+               return dyld3::dyld_shared_cache_some_image_overridden();
+
        DYLD_NO_LOCK_THIS_BLOCK;
     static bool (*p)() = NULL;
 
        DYLD_NO_LOCK_THIS_BLOCK;
     static bool (*p)() = NULL;
 
@@ -1599,6 +1881,9 @@ bool dyld_shared_cache_some_image_overridden()
 
 bool _dyld_get_shared_cache_uuid(uuid_t uuid)
 {
 
 bool _dyld_get_shared_cache_uuid(uuid_t uuid)
 {
+       if ( gUseDyld3 )
+               return dyld3::_dyld_get_shared_cache_uuid(uuid);
+
        DYLD_NO_LOCK_THIS_BLOCK;
     static bool (*p)(uuid_t) = NULL;
 
        DYLD_NO_LOCK_THIS_BLOCK;
     static bool (*p)(uuid_t) = NULL;
 
@@ -1609,6 +1894,9 @@ bool _dyld_get_shared_cache_uuid(uuid_t uuid)
 
 const void* _dyld_get_shared_cache_range(size_t* length)
 {
 
 const void* _dyld_get_shared_cache_range(size_t* length)
 {
+       if ( gUseDyld3 )
+               return dyld3::_dyld_get_shared_cache_range(length);
+
        DYLD_NO_LOCK_THIS_BLOCK;
     static const void* (*p)(size_t*) = NULL;
 
        DYLD_NO_LOCK_THIS_BLOCK;
     static const void* (*p)(size_t*) = NULL;
 
@@ -1620,6 +1908,9 @@ const void* _dyld_get_shared_cache_range(size_t* length)
 
 bool dyld_process_is_restricted()
 {
 
 bool dyld_process_is_restricted()
 {
+       if ( gUseDyld3 )
+               return dyld3::dyld_process_is_restricted();
+
        DYLD_NO_LOCK_THIS_BLOCK;
     static bool (*p)() = NULL;
        
        DYLD_NO_LOCK_THIS_BLOCK;
     static bool (*p)() = NULL;
        
@@ -1628,9 +1919,11 @@ bool dyld_process_is_restricted()
        return p();
 }
 
        return p();
 }
 
-#if DYLD_SHARED_CACHE_SUPPORT
 const char* dyld_shared_cache_file_path()
 {
 const char* dyld_shared_cache_file_path()
 {
+       if ( gUseDyld3 )
+               return dyld3::dyld_shared_cache_file_path();
+
        DYLD_NO_LOCK_THIS_BLOCK;
     static const char* (*p)() = NULL;
        
        DYLD_NO_LOCK_THIS_BLOCK;
     static const char* (*p)() = NULL;
        
@@ -1638,10 +1931,12 @@ const char* dyld_shared_cache_file_path()
            _dyld_func_lookup("__dyld_shared_cache_file_path", (void**)&p);
        return p();
 }
            _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)
 {
 
 void dyld_dynamic_interpose(const struct mach_header* mh, const struct dyld_interpose_tuple array[], size_t count)
 {
+       if ( gUseDyld3 )
+               return dyld3::dyld_dynamic_interpose(mh, array, count);
+
        DYLD_LOCK_THIS_BLOCK;
     static void (*p)(const struct mach_header* mh, const struct dyld_interpose_tuple array[], size_t count) = NULL;
 
        DYLD_LOCK_THIS_BLOCK;
     static void (*p)(const struct mach_header* mh, const struct dyld_interpose_tuple array[], size_t count) = NULL;
 
@@ -1654,6 +1949,9 @@ void dyld_dynamic_interpose(const struct mach_header* mh, const struct dyld_inte
 // SPI called __fork
 void _dyld_fork_child()
 {
 // SPI called __fork
 void _dyld_fork_child()
 {
+       if ( gUseDyld3 )
+               return dyld3::_dyld_fork_child();
+
        DYLD_NO_LOCK_THIS_BLOCK;
     static void (*p)() = NULL;
 
        DYLD_NO_LOCK_THIS_BLOCK;
     static void (*p)() = NULL;
 
@@ -1724,14 +2022,17 @@ static const dyld_cache_header* findCacheInDirAndMap(const uuid_t cacheUuid, con
 
 int dyld_shared_cache_find_iterate_text(const uuid_t cacheUuid, const char* extraSearchDirs[], void (^callback)(const dyld_shared_cache_dylib_text_info* info))
 {
 
 int dyld_shared_cache_find_iterate_text(const uuid_t cacheUuid, const char* extraSearchDirs[], void (^callback)(const dyld_shared_cache_dylib_text_info* info))
 {
+       if ( gUseDyld3 )
+               return dyld3::dyld_shared_cache_find_iterate_text(cacheUuid, extraSearchDirs, callback);
+
        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();
        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) ) {
+       if ( (allInfo != NULL) && (allInfo->sharedCacheBaseAddress != 0) && (memcmp(allInfo->sharedCacheUUID, cacheUuid, 16) == 0) ) {
                // requested cache is already mapped, just re-use it
                // requested cache is already mapped, just re-use it
-               cacheHeader = (dyld_cache_header*)(SHARED_REGION_BASE + allInfo->sharedCacheSlide);
+               cacheHeader = (dyld_cache_header*)(allInfo->sharedCacheBaseAddress);
                needToUnmap = false;
        }
        else {
                needToUnmap = false;
        }
        else {
@@ -1763,15 +2064,18 @@ int dyld_shared_cache_find_iterate_text(const uuid_t cacheUuid, const char* extr
        }
 
        // walk imageText table and call callback for each entry
        }
 
        // walk imageText table and call callback for each entry
+       const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((char*)cacheHeader + cacheHeader->mappingOffset);
+       const uint64_t cacheUnslidBaseAddress = mappings[0].address;
        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;
        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.version                   = 2;
                dylibTextInfo.loadAddressUnslid = p->loadAddress;
                dylibTextInfo.textSegmentSize   = p->textSegmentSize;
                dylibTextInfo.path                              = (char*)cacheHeader + p->pathOffset;
                ::memcpy(dylibTextInfo.dylibUuid, p->uuid, 16);
                dylibTextInfo.loadAddressUnslid = p->loadAddress;
                dylibTextInfo.textSegmentSize   = p->textSegmentSize;
                dylibTextInfo.path                              = (char*)cacheHeader + p->pathOffset;
                ::memcpy(dylibTextInfo.dylibUuid, p->uuid, 16);
+               dylibTextInfo.textSegmentOffset = p->loadAddress - cacheUnslidBaseAddress;
                callback(&dylibTextInfo);
        }
 
                callback(&dylibTextInfo);
        }
 
@@ -1783,6 +2087,9 @@ int dyld_shared_cache_find_iterate_text(const uuid_t cacheUuid, const char* extr
 
 int dyld_shared_cache_iterate_text(const uuid_t cacheUuid, void (^callback)(const dyld_shared_cache_dylib_text_info* info))
 {
 
 int dyld_shared_cache_iterate_text(const uuid_t cacheUuid, void (^callback)(const dyld_shared_cache_dylib_text_info* info))
 {
+       if ( gUseDyld3 )
+               return dyld3::dyld_shared_cache_iterate_text(cacheUuid, callback);
+
        const char* extraSearchDirs[] = { NULL };
        return dyld_shared_cache_find_iterate_text(cacheUuid, extraSearchDirs, callback);
 }
        const char* extraSearchDirs[] = { NULL };
        return dyld_shared_cache_find_iterate_text(cacheUuid, extraSearchDirs, callback);
 }
@@ -1790,6 +2097,9 @@ int dyld_shared_cache_iterate_text(const uuid_t cacheUuid, void (^callback)(cons
 
 bool _dyld_is_memory_immutable(const void* addr, size_t length)
 {
 
 bool _dyld_is_memory_immutable(const void* addr, size_t length)
 {
+       if ( gUseDyld3 )
+               return dyld3::_dyld_is_memory_immutable(addr, length);
+
        DYLD_NO_LOCK_THIS_BLOCK;
     static bool (*p)(const void*, size_t) = NULL;
 
        DYLD_NO_LOCK_THIS_BLOCK;
     static bool (*p)(const void*, size_t) = NULL;
 
@@ -1803,6 +2113,9 @@ void _dyld_objc_notify_register(_dyld_objc_notify_mapped    mapped,
                                 _dyld_objc_notify_init      init,
                                 _dyld_objc_notify_unmapped  unmapped)
 {
                                 _dyld_objc_notify_init      init,
                                 _dyld_objc_notify_unmapped  unmapped)
 {
+       if ( gUseDyld3 )
+               return dyld3::_dyld_objc_notify_register(mapped, init, unmapped);
+
        DYLD_LOCK_THIS_BLOCK;
     static bool (*p)(_dyld_objc_notify_mapped, _dyld_objc_notify_init, _dyld_objc_notify_unmapped) = NULL;
 
        DYLD_LOCK_THIS_BLOCK;
     static bool (*p)(_dyld_objc_notify_mapped, _dyld_objc_notify_init, _dyld_objc_notify_unmapped) = NULL;
 
index 947d39aef17ab3605bd8d4d27d03d96e2866a7e3..c8b90d33c9b449e16103090d229d26b3dbd4bc17 100644 (file)
@@ -92,9 +92,12 @@ namespace dyld {
                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_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);
-       };
-
+        kern_return_t   (*task_register_dyld_get_process_state)(task_t task, dyld_kernel_process_info_t *dyld_process_state);
+        kern_return_t   (*task_info)(task_name_t target_task, task_flavor_t flavor, task_info_t task_info_out, mach_msg_type_number_t *task_info_outCnt);
+        kern_return_t   (*thread_info)(thread_inspect_t target_act, thread_flavor_t flavor, thread_info_t thread_info_out, mach_msg_type_number_t *thread_info_outCnt);
+        bool            (*kdebug_is_enabled)(uint32_t code);
+        int             (*kdebug_trace)(uint32_t code, uint64_t arg1, uint64_t arg2, uint64_t arg3, uint64_t arg4);
+    };
        extern const struct SyscallHelpers* gSyscallHelpers;
 
 
        extern const struct SyscallHelpers* gSyscallHelpers;
 
 
index f13064d27362c2535fd6a4a046c64791533219b6..5f4e7b5ccea3074131214642e4c9eb910e412040 100644 (file)
@@ -36,6 +36,8 @@
 #include "ImageLoader.h"
 #include "dyld.h"
 
 #include "ImageLoader.h"
 #include "dyld.h"
 
+extern "C"     void _dyld_debugger_notification(enum dyld_notify_mode mode, unsigned long count, uint64_t machHeaders[]);
+
 #if __IPHONE_OS_VERSION_MIN_REQUIRED
        #define INITIAL_UUID_IMAGE_COUNT 4
 #else
 #if __IPHONE_OS_VERSION_MIN_REQUIRED
        #define INITIAL_UUID_IMAGE_COUNT 4
 #else
index 51f7ad4ffe920befd36450c2fd233d241a8bed79..c6ed46addfa109af71fcf381e480b51b29f7e2ce 100644 (file)
 #include "dyld_images.h"
 #include "dyld_priv.h"
 
 #include "dyld_images.h"
 #include "dyld_priv.h"
 
+// this was in dyld_priv.h but it is no longer exported
+extern "C" {
+    const struct dyld_all_image_infos* _dyld_get_all_image_infos();
+}
+
+namespace {
+
+void withRemoteBuffer(task_t task, vm_address_t remote_address, size_t remote_size, bool allow_truncation, kern_return_t *kr, void (^block)(void *buffer)) {
+    kern_return_t r = KERN_SUCCESS;
+    mach_vm_address_t local_address = 0;
+    mach_vm_address_t local_size = remote_size;
+    while (1) {
+        vm_prot_t cur_protection, max_protection;
+        r = mach_vm_remap(mach_task_self(),
+                            &local_address,
+                            local_size,
+                            0,  // mask
+                            VM_FLAGS_ANYWHERE | VM_FLAGS_RETURN_DATA_ADDR | VM_FLAGS_RESILIENT_CODESIGN,
+                            task,
+                            remote_address,
+                            TRUE,  // Copy semantics: changes to this memory by the target process will not be visible in this process
+                            &cur_protection,
+                            &max_protection,
+                            VM_INHERIT_DEFAULT);
+        //Do this here to allow chaining of multiple embedded blocks with a single error out;
+        if (kr != NULL) {
+            *kr = r;
+        }
+        if (r == KERN_SUCCESS) {
+            // We got someting, call the block and then exit
+            block(reinterpret_cast<void *>(local_address));
+            vm_deallocate(mach_task_self(), local_address, local_size);
+            break;
+        }
+        if (!allow_truncation) {
+            break;
+        }
+        // We did not get something, but we are allowed to truncate and try again
+        uint64_t trunc_size = ((remote_address + local_size - 1) & PAGE_MASK) + 1;
+        if (local_size <= trunc_size) {
+            //Even if we truncate it will be in the same page, time to accept defeat
+            break;
+        } else {
+            local_size -= trunc_size;
+        }
+    }
+}
+
+template<typename T>
+void withRemoteObject(task_t task, vm_address_t remote_address, kern_return_t *kr, void (^block)(T t))
+{
+    withRemoteBuffer(task, remote_address, sizeof(T), false, kr, ^(void *buffer) {
+        block(*reinterpret_cast<T *>(buffer));
+    });
+}
+};
 
 //
 // Opaque object returned by _dyld_process_info_create()
 
 //
 // Opaque object returned by _dyld_process_info_create()
@@ -123,7 +179,10 @@ dyld_process_info_base* dyld_process_info_base::make(task_t task, const dyld_all
 {
     // 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();
 {
     // 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));
+    bool sameCacheAsThisProcess = !allImageInfo.processDetachedFromSharedRegion
+                                    && !myInfo->processDetachedFromSharedRegion
+                                    && ((memcmp(myInfo->sharedCacheUUID, allImageInfo.sharedCacheUUID, 16) == 0)
+                                    && (myInfo->sharedCacheSlide == allImageInfo.sharedCacheSlide));
     unsigned countOfPathsNeedingCopying = 0;
     if ( sameCacheAsThisProcess ) {
         for (int i=0; i < allImageInfo.infoArrayCount; ++i) {
     unsigned countOfPathsNeedingCopying = 0;
     if ( sameCacheAsThisProcess ) {
         for (int i=0; i < allImageInfo.infoArrayCount; ++i) {
@@ -181,7 +240,7 @@ dyld_process_info_base* dyld_process_info_base::make(task_t task, const dyld_all
             goto fail;
         }
     }
             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) ) {
     // 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) ) {
@@ -212,11 +271,13 @@ dyld_process_info_base* dyld_process_info_base::makeSuspended(task_t task, kern_
         return NULL;
     }
 
         return NULL;
     }
 
-    unsigned imageCount = 0;  // main executable and dyld
-    uint64_t            mainExecutableAddress = 0;
-    uint64_t            dyldAddress = 0;
+    __block unsigned    imageCount = 0;  // main executable and dyld
+    __block uint64_t    mainExecutableAddress = 0;
+    __block uint64_t    dyldAddress = 0;
     char                dyldPathBuffer[PATH_MAX+1];
     char                mainExecutablePathBuffer[PATH_MAX+1];
     char                dyldPathBuffer[PATH_MAX+1];
     char                mainExecutablePathBuffer[PATH_MAX+1];
+    __block char *      dyldPath = &dyldPathBuffer[0];
+    __block char *      mainExecutablePath = &mainExecutablePathBuffer[0];
     mach_vm_size_t      size;
     for (mach_vm_address_t address = 0; ; address += size) {
         vm_region_basic_info_data_64_t  info;
     mach_vm_size_t      size;
     for (mach_vm_address_t address = 0; ; address += size) {
         vm_region_basic_info_data_64_t  info;
@@ -226,31 +287,31 @@ dyld_process_info_base* dyld_process_info_base::makeSuspended(task_t task, kern_
                          (vm_region_info_t)&info, &infoCount, &objectName);
         if ( result != KERN_SUCCESS )
             break;
                          (vm_region_info_t)&info, &infoCount, &objectName);
         if ( result != KERN_SUCCESS )
             break;
-        if ( info.protection == (VM_PROT_READ|VM_PROT_EXECUTE) ) {
+        if ( info.protection != (VM_PROT_READ|VM_PROT_EXECUTE) )
+            continue;
             // read start of vm region to verify it is a mach header
             // read start of vm region to verify it is a mach header
-            mach_vm_size_t readSize = sizeof(mach_header_64);
-            mach_header_64 mhBuffer;
-            if ( mach_vm_read_overwrite(task, address, sizeof(mach_header_64), (vm_address_t)&mhBuffer, &readSize) != KERN_SUCCESS )
-                continue;
-            if ( (mhBuffer.magic != MH_MAGIC) && (mhBuffer.magic != MH_MAGIC_64) )
-                continue;
-            // now know the region is the start of a mach-o file
-            if ( mhBuffer.filetype == MH_EXECUTE ) {
-                mainExecutableAddress = address;
-                int len = proc_regionfilename(pid, mainExecutableAddress, mainExecutablePathBuffer, PATH_MAX);
-                if ( len != 0 )
-                    mainExecutablePathBuffer[len] = '\0';
-                ++imageCount;
-            }
-            else if ( mhBuffer.filetype == MH_DYLINKER ) {
-                dyldAddress = address;
-                int len = proc_regionfilename(pid, dyldAddress, dyldPathBuffer, PATH_MAX);
-                if ( len != 0 )
-                    dyldPathBuffer[len] = '\0';
-                ++imageCount;
-            }
+            withRemoteObject(task, address, NULL, ^(mach_header_64 mhBuffer){
+                if ( (mhBuffer.magic != MH_MAGIC) && (mhBuffer.magic != MH_MAGIC_64) )
+                    return;
+                // now know the region is the start of a mach-o file
+                if ( mhBuffer.filetype == MH_EXECUTE ) {
+                    mainExecutableAddress = address;
+                    int len = proc_regionfilename(pid, mainExecutableAddress, mainExecutablePath, PATH_MAX);
+                    if ( len != 0 ) {
+                        mainExecutablePath[len] = '\0';
+                    }
+                    ++imageCount;
+                }
+                else if ( mhBuffer.filetype == MH_DYLINKER ) {
+                    dyldAddress = address;
+                    int len = proc_regionfilename(pid, dyldAddress, dyldPath, PATH_MAX);
+                    if ( len != 0 ) {
+                        dyldPath[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, "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);
     }
     //fprintf(stderr, "dyld: addr=0x%llX, path=%s\n", dyldAddress, dyldPathBuffer);
     //fprintf(stderr, "app:  addr=0x%llX, path=%s\n", mainExecutableAddress, mainExecutablePathBuffer);
@@ -280,7 +341,7 @@ dyld_process_info_base* dyld_process_info_base::makeSuspended(task_t task, kern_
 
     // fill in info for dyld
     if ( dyldAddress != 0 ) {
 
     // fill in info for dyld
     if ( dyldAddress != 0 ) {
-        if ( kern_return_t r = obj->addDyldImage(task, dyldAddress, 0, dyldPathBuffer) ) {
+        if ( kern_return_t r = obj->addDyldImage(task, dyldAddress, 0, dyldPath) ) {
             if ( kr != NULL )
                 *kr = r;
             free(obj);
             if ( kr != NULL )
                 *kr = r;
             free(obj);
@@ -290,7 +351,7 @@ dyld_process_info_base* dyld_process_info_base::makeSuspended(task_t task, kern_
 
     // fill in info for each image
     if ( mainExecutableAddress != 0 ) {
 
     // fill in info for each image
     if ( mainExecutableAddress != 0 ) {
-        if ( kern_return_t r = obj->addImage(task, false, mainExecutableAddress, 0, mainExecutablePathBuffer) ) {
+        if ( kern_return_t r = obj->addImage(task, false, mainExecutableAddress, 0, mainExecutablePath) ) {
             if ( kr != NULL )
                 *kr = r;
             free(obj);
             if ( kr != NULL )
                 *kr = r;
             free(obj);
@@ -313,42 +374,13 @@ const char* dyld_process_info_base::addString(const char* str)
 
 const char* dyld_process_info_base::copyPath(task_t task, uint64_t stringAddressInTask, kern_return_t* kr)
 {
 
 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);
+    __block const char* retval = NULL;
+    withRemoteBuffer(task, stringAddressInTask, PATH_MAX+8, true, kr, ^(void *buffer) {
+        retval = addString(static_cast<const char *>(buffer));
+    });
+    return retval;
 }
 
 }
 
-
 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;
 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;
@@ -369,19 +401,16 @@ kern_return_t dyld_process_info_base::addImage(task_t task, bool sameCacheAsThis
         addInfoFromLoadCommands((mach_header*)imageAddress, imageAddress, 32*1024);
     }
     else {
         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;
+        __block kern_return_t kr = KERN_SUCCESS;
+        withRemoteObject(task, imageAddress, &kr, ^(mach_header_64 mhBuffer) {
+            size_t          headerPagesSize = sizeof(mach_header_64) + mhBuffer.sizeofcmds;
+            withRemoteBuffer(task, imageAddress, headerPagesSize, false, &kr, ^(void * buffer) {
+                addInfoFromLoadCommands((mach_header*)buffer, imageAddress, headerPagesSize);
+            });
+        });
+        if (kr != KERN_SUCCESS) {
+            return kr;
         }
         }
-        addInfoFromLoadCommands((mach_header*)localCopyBuffer, imageAddress, localCopyBufferSize);
-        vm_deallocate(mach_task_self(), localCopyBuffer, localCopyBufferSize);
     }
     _curImage->segmentsCount = _curSegmentIndex - _curImage->segmentStartIndex;
     _curImage++;
     }
     _curImage->segmentsCount = _curSegmentIndex - _curImage->segmentStartIndex;
     _curImage++;
@@ -391,7 +420,7 @@ kern_return_t dyld_process_info_base::addImage(task_t task, bool sameCacheAsThis
 
 kern_return_t dyld_process_info_base::addDyldImage(task_t task, uint64_t dyldAddress, uint64_t dyldPathAddress, const char* localPath)
 {
 
 kern_return_t dyld_process_info_base::addDyldImage(task_t task, uint64_t dyldAddress, uint64_t dyldPathAddress, const char* localPath)
 {
-    kern_return_t kr;
+    __block kern_return_t kr = KERN_SUCCESS;
     _curImage->loadAddress = dyldAddress;
     _curImage->segmentStartIndex = _curSegmentIndex;
     if ( localPath != NULL ) {
     _curImage->loadAddress = dyldAddress;
     _curImage->segmentStartIndex = _curSegmentIndex;
     if ( localPath != NULL ) {
@@ -403,19 +432,16 @@ kern_return_t dyld_process_info_base::addDyldImage(task_t task, uint64_t dyldAdd
             return 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;
+    withRemoteObject(task, dyldAddress, &kr, ^(mach_header_64 mhBuffer) {
+        size_t          headerPagesSize = sizeof(mach_header_64) + mhBuffer.sizeofcmds;
+        withRemoteBuffer(task, dyldAddress, headerPagesSize, false, &kr, ^(void * buffer) {
+            addInfoFromLoadCommands((mach_header*)buffer, dyldAddress, headerPagesSize);
+        });
+    });
+    if (kr != KERN_SUCCESS) {
+        return kr;
     }
     }
-    addInfoFromLoadCommands((mach_header*)localCopyBuffer, dyldAddress, localCopyBufferSize);
-    vm_deallocate(mach_task_self(), localCopyBuffer, localCopyBufferSize);
+
     _curImage->segmentsCount = _curSegmentIndex - _curImage->segmentStartIndex;
     _curImage++;
     return KERN_SUCCESS;
     _curImage->segmentsCount = _curSegmentIndex - _curImage->segmentStartIndex;
     _curImage++;
     return KERN_SUCCESS;
@@ -431,7 +457,7 @@ void dyld_process_info_base::addInfoFromLoadCommands(const mach_header* mh, uint
         startCmds = (load_command*)((char *)mh + sizeof(mach_header));
     else
         return;  // not a mach-o file, or wrong endianness
         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* const cmdsEnd = (load_command*)((char*)startCmds + mh->sizeofcmds);
     const load_command* cmd = startCmds;
     for(uint32_t i = 0; i < mh->ncmds; ++i) {
index 057278ab44e91491d877b47c28b1a35b5d38124b..cd8c6078969da424c5bfb6d395197df3a31f4a2e 100644 (file)
@@ -59,8 +59,10 @@ struct dyld_all_image_infos_32 {
     uint32_t        sharedCacheBaseAddress;
     uint64_t        infoArrayChangeTimestamp;
     uint32_t        dyldPath;
     uint32_t        sharedCacheBaseAddress;
     uint64_t        infoArrayChangeTimestamp;
     uint32_t        dyldPath;
-    uint32_t        notifyMachPorts[2];
-    uint32_t        reserved[11];
+    uint32_t        notifyMachPorts[8];
+    uint32_t        reserved[5];
+    uint32_t        compact_dyld_image_info_addr;
+    uint32_t        compact_dyld_image_info_size;
 };
 
 struct dyld_all_image_infos_64 {
 };
 
 struct dyld_all_image_infos_64 {
@@ -91,8 +93,10 @@ struct dyld_all_image_infos_64 {
     uint64_t        sharedCacheBaseAddress;
     uint64_t        infoArrayChangeTimestamp;
     uint64_t        dyldPath;
     uint64_t        sharedCacheBaseAddress;
     uint64_t        infoArrayChangeTimestamp;
     uint64_t        dyldPath;
-    uint32_t        notifyMachPorts[2];
-    uint64_t        reserved[12];
+    uint32_t        notifyMachPorts[8];
+    uint64_t        reserved[9];
+    uint64_t        compact_dyld_image_info_addr;
+    uint64_t        compact_dyld_image_info_size;
 };
 
 struct dyld_image_info_32 {
 };
 
 struct dyld_image_info_32 {
index 760f06c558eed83e21b3004802db76b68b7c3746..6d5eee3afaf9d1c6c2acac78b8181c641317e17a 100644 (file)
 #include "dyld_images.h"
 #include "dyld_priv.h"
 
 #include "dyld_images.h"
 #include "dyld_priv.h"
 
+#include "LaunchCache.h"
+#include "Loading.h"
+#include "AllImages.h"
+
+
 typedef void (^Notify)(bool unload, uint64_t timestamp, uint64_t machHeader, const uuid_t uuid, const char* path);
 typedef void (^NotifyExit)();
 typedef void (^NotifyMain)();
 typedef void (^Notify)(bool unload, uint64_t timestamp, uint64_t machHeader, const uuid_t uuid, const char* path);
 typedef void (^NotifyExit)();
 typedef void (^NotifyMain)();
@@ -46,18 +51,21 @@ typedef void (^NotifyMain)();
 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);
 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();
+                                                                               ~dyld_process_info_notify_base();
 
     uint32_t&           retainCount() const { return _retainCount; }
        void                            setNotifyMain(NotifyMain notifyMain) const { _notifyMain = notifyMain; }
 
 
     uint32_t&           retainCount() const { return _retainCount; }
        void                            setNotifyMain(NotifyMain notifyMain) const { _notifyMain = notifyMain; }
 
+    // override new and delete so we don't need to link with libc++
+    static void*        operator new(size_t sz) { return malloc(sz); }
+    static void         operator delete(void* p) { return free(p); }
+
 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();
 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;
 
        mutable uint32_t        _retainCount;
     dispatch_queue_t    _queue;
@@ -101,8 +109,7 @@ dyld_process_info_notify_base::~dyld_process_info_notify_base()
 
 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)
 {
 
 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);
+    dyld_process_info_notify_base* obj = new dyld_process_info_notify_base(queue, notify, notifyExit, task);
 
     if ( kern_return_t r = obj->makePorts() ) {
                if ( kr != NULL )
 
     if ( kern_return_t r = obj->makePorts() ) {
                if ( kr != NULL )
@@ -123,7 +130,7 @@ dyld_process_info_notify_base* dyld_process_info_notify_base::make(task_t task,
     return obj;
 
 fail:
     return obj;
 
 fail:
-    free(obj);
+    delete obj;
     return NULL;
 }
 
     return NULL;
 }
 
@@ -322,11 +329,178 @@ void _dyld_process_info_notify_retain(dyld_process_info_notify object)
 void _dyld_process_info_notify_release(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);
-       }
+    if ( object->retainCount() == 0 )
+        delete object;
+}
+
+
+
+
+
+
+
+namespace dyld3 {
+
+
+static mach_port_t sNotifyReplyPorts[DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT];
+static bool        sZombieNotifiers[DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT];
+
+static void notifyMonitoringDyld(bool unloading, unsigned portSlot, const launch_cache::DynArray<loader::ImageInfo>& imageInfos)
+{
+    if ( sZombieNotifiers[portSlot] )
+        return;
+
+    unsigned entriesSize = (unsigned)imageInfos.count()*sizeof(dyld_process_info_image_entry);
+    unsigned pathsSize = 0;
+    for (uintptr_t i=0; i < imageInfos.count(); ++i) {
+        launch_cache::Image image(imageInfos[i].imageData);
+        pathsSize += (strlen(image.path()) + 1);
+    }
+    unsigned totalSize = (sizeof(dyld_process_info_notify_header) + MAX_TRAILER_SIZE + 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 = (unsigned)imageInfos.count()/2;
+        const launch_cache::DynArray<loader::ImageInfo> firstHalf(imageHalfCount, (loader::ImageInfo*)&imageInfos[0]);
+        const launch_cache::DynArray<loader::ImageInfo> secondHalf(imageInfos.count() - imageHalfCount, (loader::ImageInfo*)&imageInfos[imageHalfCount]);
+        notifyMonitoringDyld(unloading, portSlot, firstHalf);
+        notifyMonitoringDyld(unloading, portSlot, secondHalf);
+        return;
+    }
+    // build buffer to send
+    dyld_all_image_infos*  allImageInfo = gAllImages.oldAllImageInfo();
+    uint8_t    buffer[totalSize];
+    dyld_process_info_notify_header* header = (dyld_process_info_notify_header*)buffer;
+    header->version          = 1;
+    header->imageCount       = (uint32_t)imageInfos.count();
+    header->imagesOffset     = sizeof(dyld_process_info_notify_header);
+    header->stringsOffset    = sizeof(dyld_process_info_notify_header) + entriesSize;
+    header->timestamp        = allImageInfo->infoArrayChangeTimestamp;
+    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 (uintptr_t i=0; i < imageInfos.count(); ++i) {
+        launch_cache::Image image(imageInfos[i].imageData);
+        strcpy(pathPool, image.path());
+        uint32_t len = (uint32_t)strlen(pathPool);
+        memcpy(entries->uuid, image.uuid(), sizeof(uuid_t));
+        entries->loadAddress = (uint64_t)imageInfos[i].loadAddress;
+        entries->pathStringOffset = (uint32_t)(pathPool - pathPoolStart);
+        entries->pathLength  = len;
+        pathPool += (len +1);
+        ++entries;
+    }
+    // lazily alloc reply port
+    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);
+        //log("allocated reply port %d\n", sNotifyReplyPorts[portSlot]);
+    }
+    //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  = allImageInfo->notifyPorts[portSlot];
+    h->msgh_reserved     = 0;
+    h->msgh_size         = (mach_msg_size_t)sizeof(buffer);
+    //log("sending to port[%d]=%d, size=%d, reply port=%d, id=0x%X\n", portSlot, allImageInfo->notifyPorts[portSlot], h->msgh_size, sNotifyReplyPorts[portSlot], h->msgh_id);
+    kern_return_t sendResult = mach_msg(h, MACH_SEND_MSG | MACH_RCV_MSG | MACH_RCV_TIMEOUT, h->msgh_size, h->msgh_size, sNotifyReplyPorts[portSlot], 2000, MACH_PORT_NULL);
+    //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
+        //log("process requesting notification gone. deallocation send port %d and receive port %d\n", allImageInfo->notifyPorts[portSlot], sNotifyReplyPorts[portSlot]);
+        mach_port_deallocate(mach_task_self(), allImageInfo->notifyPorts[portSlot]);
+        mach_port_deallocate(mach_task_self(), sNotifyReplyPorts[portSlot]);
+        allImageInfo->notifyPorts[portSlot] = 0;
+        sNotifyReplyPorts[portSlot] = 0;
+    }
+    else if ( sendResult == MACH_RCV_TIMED_OUT ) {
+        // client took too long, ignore him from now on
+        sZombieNotifiers[portSlot] = true;
+        mach_port_deallocate(mach_task_self(), sNotifyReplyPorts[portSlot]);
+        sNotifyReplyPorts[portSlot] = 0;
+    }
 }
 
 }
 
+void AllImages::notifyMonitorMain()
+{
+    dyld_all_image_infos* allImageInfo = gAllImages.oldAllImageInfo();
+    for (int slot=0; slot < DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT; ++slot) {
+        if ( (allImageInfo->notifyPorts[slot] != 0 ) && !sZombieNotifiers[slot] ) {
+            if ( sNotifyReplyPorts[slot] == 0 ) {
+                if ( !mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &sNotifyReplyPorts[slot]) )
+                    mach_port_insert_right(mach_task_self(), sNotifyReplyPorts[slot], sNotifyReplyPorts[slot], MACH_MSG_TYPE_MAKE_SEND);
+                //dyld::log("allocated reply port %d\n", sNotifyReplyPorts[slot]);
+            }
+            //dyld::log("found port to send to\n");
+            uint8_t messageBuffer[sizeof(mach_msg_header_t) + MAX_TRAILER_SIZE];
+            mach_msg_header_t* h = (mach_msg_header_t*)messageBuffer;
+            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           = DYLD_PROCESS_INFO_NOTIFY_MAIN_ID;
+            h->msgh_local_port   = sNotifyReplyPorts[slot];
+            h->msgh_remote_port  = allImageInfo->notifyPorts[slot];
+            h->msgh_reserved     = 0;
+            h->msgh_size         = (mach_msg_size_t)sizeof(messageBuffer);
+            //dyld::log("sending to port[%d]=%d, size=%d, reply port=%d, id=0x%X\n", slot, allImageInfo->notifyPorts[slot], h->msgh_size, sNotifyReplyPorts[slot], h->msgh_id);
+            kern_return_t sendResult = mach_msg(h, MACH_SEND_MSG | MACH_RCV_MSG | MACH_RCV_TIMEOUT, h->msgh_size, h->msgh_size, sNotifyReplyPorts[slot], 2000, 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", allImageInfo->notifyPorts[slot], sNotifyReplyPorts[slot]);
+                mach_port_deallocate(mach_task_self(), allImageInfo->notifyPorts[slot]);
+                mach_port_deallocate(mach_task_self(), sNotifyReplyPorts[slot]);
+                allImageInfo->notifyPorts[slot] = 0;
+                sNotifyReplyPorts[slot] = 0;
+            }
+            else if ( sendResult == MACH_RCV_TIMED_OUT ) {
+                // client took too long, ignore him from now on
+                sZombieNotifiers[slot] = true;
+                mach_port_deallocate(mach_task_self(), sNotifyReplyPorts[slot]);
+                sNotifyReplyPorts[slot] = 0;
+            }
+        }
+    }
+}
+
+void AllImages::notifyMonitorLoads(const launch_cache::DynArray<loader::ImageInfo>& newImages)
+{
+    // notify each monitoring process
+    dyld_all_image_infos* allImageInfo = gAllImages.oldAllImageInfo();
+    for (int slot=0; slot < DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT; ++slot) {
+        if ( allImageInfo->notifyPorts[slot] != 0 ) {
+             notifyMonitoringDyld(false, slot, newImages);
+        }
+        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;
+            sZombieNotifiers[slot] = false;
+        }
+    }
+}
+
+void AllImages::notifyMonitorUnloads(const launch_cache::DynArray<loader::ImageInfo>& unloadingImages)
+{
+    // notify each monitoring process
+    dyld_all_image_infos* allImageInfo = gAllImages.oldAllImageInfo();
+    for (int slot=0; slot < DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT; ++slot) {
+        if ( allImageInfo->notifyPorts[slot] != 0 ) {
+             notifyMonitoringDyld(true, slot, unloadingImages);
+        }
+        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;
+            sZombieNotifiers[slot] = false;
+        }
+    }
+}
+
+} // namespace dyld3
+
+
 
 
 
 
index 6eeed0a9eaf73da0e1da594b689ecc1c96a2659c..99a66448c24b0a206865b8f8ae019cb2099b72c1 100644 (file)
@@ -108,6 +108,38 @@ void _ZN10__cxxabiv112__unexpectedEPFvvE()
        _ZN4dyld4haltEPKc("dyld std::__unexpected()\n");
 }
 
        _ZN4dyld4haltEPKc("dyld std::__unexpected()\n");
 }
 
+// std::__terminate() called by C++ unwinding code
+void _ZSt11__terminatePFvvE(void (*func)())
+{
+       _ZN4dyld4haltEPKc("dyld std::__terminate()\n");
+}
+
+// std::__unexpected() called by C++ unwinding code
+void _ZSt12__unexpectedPFvvE(void (*func)())
+{
+       _ZN4dyld4haltEPKc("dyld std::__unexpected()\n");
+}
+
+// terminate_handler get_terminate()
+void* _ZSt13get_terminatev()
+{
+    return NULL;
+}
+
+// unexpected_handler get_unexpected()
+void* _ZSt14get_unexpectedv()
+{
+    return NULL;
+}
+
+// new_handler get_new_handler()
+void* _ZSt15get_new_handlerv()
+{
+    return NULL;
+}
+
+
+
 // __cxxabiv1::__terminate_handler
 void* _ZN10__cxxabiv119__terminate_handlerE  = &_ZSt9terminatev;
 
 // __cxxabiv1::__terminate_handler
 void* _ZN10__cxxabiv119__terminate_handlerE  = &_ZSt9terminatev;
 
@@ -425,6 +457,16 @@ void _ZN4dyld3logEPKcz(const char* format, ...) {
        va_end(list);
 }
 
        va_end(list);
 }
 
+#if __i386__
+void _ZN4dyld4vlogEPKcPc(const char* format, va_list list) {
+#else
+void _ZN4dyld4vlogEPKcP13__va_list_tag(const char* format, va_list list) {
+#endif
+       gSyscallHelpers->vlog(format, list);
+}
+
+
+
 void _ZN4dyld4warnEPKcz(const char* format, ...) {
        va_list list;
        va_start(list, format);
 void _ZN4dyld4warnEPKcz(const char* format, ...) {
        va_list list;
        va_start(list, format);
@@ -708,6 +750,30 @@ kern_return_t      task_register_dyld_get_process_state(task_t task, dyld_kernel_proc
        return KERN_NOT_SUPPORTED;
 }
 
        return KERN_NOT_SUPPORTED;
 }
 
+kern_return_t   task_info(task_name_t target_task, task_flavor_t flavor, task_info_t task_info_out, mach_msg_type_number_t *task_info_outCnt) {
+    if ( gSyscallHelpers->version >= 8 )
+        return gSyscallHelpers->task_info(target_task, flavor, task_info_out, task_info_outCnt);
+    return KERN_NOT_SUPPORTED;
+}
+
+kern_return_t   thread_info(thread_inspect_t target_act, thread_flavor_t flavor, thread_info_t thread_info_out, mach_msg_type_number_t *thread_info_outCnt) {
+    if ( gSyscallHelpers->version >= 8 )
+        return gSyscallHelpers->task_info(target_act, flavor, thread_info_out, thread_info_outCnt);
+    return KERN_NOT_SUPPORTED;
+}
+
+bool kdebug_is_enabled(uint32_t code) {
+    if ( gSyscallHelpers->version >= 8 )
+        return gSyscallHelpers->kdebug_is_enabled(code);
+    return false;
+}
+
+int kdebug_trace(uint32_t code, uint64_t arg1, uint64_t arg2, uint64_t arg3, uint64_t arg4) {
+    if ( gSyscallHelpers->version >= 8 )
+        return gSyscallHelpers->kdebug_trace(code, arg1, arg2, arg3, arg4);
+    return 0;
+}
+
 int* __error(void) {
        return gSyscallHelpers->errnoAddress();
 } 
 int* __error(void) {
        return gSyscallHelpers->errnoAddress();
 } 
@@ -735,5 +801,20 @@ int myerrno_fallback = 0;
 #endif
 
 
 #endif
 
 
+void* _NSConcreteStackBlock[32];
+void* _NSConcreteGlobalBlock[32];
+
+void _Block_object_assign()
+{
+       _ZN4dyld4haltEPKc("_Block_object_assign()");
+}
+
+void _Block_object_dispose(const void* object, int flags)
+{
+       // only support stack blocks in dyld: BLOCK_FIELD_IS_BYREF=8
+       if ( flags != 8 )
+               _ZN4dyld4haltEPKc("_Block_object_dispose()");
+}
+
 
 
 
 
index 81322259bbeaaed62390e42384397c2bcb49a571..a0b36615ab08c37f9987566bcdd91107bec0ab9a 100644 (file)
@@ -214,6 +214,9 @@ void* tlv_allocate_and_initialize_for_key(pthread_key_t key)
                }
                cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
        }
                }
                cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
        }
+    // no thread local storage in image: should never happen
+    if ( size == 0 )
+        return NULL;
        
        // allocate buffer and fill with template
        void* buffer = malloc(size);
        
        // allocate buffer and fill with template
        void* buffer = malloc(size);
@@ -306,7 +309,7 @@ static void tlv_initialize_descriptors(const struct mach_header* mh)
 }
 
 
 }
 
 
-void tlv_load_notification(const struct mach_header* mh, intptr_t slide)
+static 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.
 {
        // 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.
@@ -394,7 +397,7 @@ void _tlv_atexit(TermFunc func, void* objAddr)
         pthread_setspecific(tlv_terminators_key, list);
     }
     else {
         pthread_setspecific(tlv_terminators_key, list);
     }
     else {
-        if ( list->allocCount == list->allocCount ) {
+        if ( list->useCount == list->allocCount ) {
             // handle resizing allocation 
             uint32_t newAllocCount = list->allocCount * 2;
             size_t newAllocSize = offsetof(struct TLVTerminatorList, entries[newAllocCount]);
             // handle resizing allocation 
             uint32_t newAllocCount = list->allocCount * 2;
             size_t newAllocSize = offsetof(struct TLVTerminatorList, entries[newAllocCount]);
index 4bb44828dfcc415e14e76206d33a15a90513ef40..ab4d4fd8e6fc1c92c86197253e6aa95382f9f38e 100755 (executable)
@@ -133,15 +133,24 @@ if __name__ == "__main__":
     testsRunDstTopDir = "/AppleInternal/CoreOS/tests/dyld/"
     testsBuildDstTopDir = dstDir + testsRunDstTopDir
     shutil.rmtree(testsBuildDstTopDir, ignore_errors=True)
     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", "/")
+    dyldSrcDir = os.getenv("SRCROOT", "")
+    if not dyldSrcDir:
+        dyldSrcDir = os.getcwd()
+    testsSrcTopDir = dyldSrcDir + "/testing/test-cases/"
+    sdkDir = os.getenv("SDKROOT", "")
+    if not sdkDir:
+        #sdkDir = subprocess.check_output(["xcrun", "--show-sdk-path"]).rstrip()
+        sdkDir = "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.13.Internal.sdk"
     toolsDir = os.getenv("TOOLCHAIN_DIR", "/")
     defaultMinOS = ""
     toolsDir = os.getenv("TOOLCHAIN_DIR", "/")
     defaultMinOS = ""
+    minVersNum = "10.12"
     minOSOption = os.getenv("DEPLOYMENT_TARGET_CLANG_FLAG_NAME", "")
     if minOSOption:
         minOSVersName = os.getenv("DEPLOYMENT_TARGET_CLANG_ENV_NAME", "")
         if minOSVersName:
             minVersNum = os.getenv(minOSVersName, "")
     minOSOption = os.getenv("DEPLOYMENT_TARGET_CLANG_FLAG_NAME", "")
     if minOSOption:
         minOSVersName = os.getenv("DEPLOYMENT_TARGET_CLANG_ENV_NAME", "")
         if minOSVersName:
             minVersNum = os.getenv(minOSVersName, "")
+    else:
+        minOSOption = "mmacosx-version-min"
     platformName = os.getenv("PLATFORM_NAME", "osx")
     archOptions = ""
     archList = os.getenv("RC_ARCHS", "")
     platformName = os.getenv("PLATFORM_NAME", "osx")
     archOptions = ""
     archList = os.getenv("RC_ARCHS", "")
@@ -155,9 +164,11 @@ if __name__ == "__main__":
         elif platformName == "appletvos":
             archOptions = "-arch arm64"
         else:
         elif platformName == "appletvos":
             archOptions = "-arch arm64"
         else:
-            archOptions = ""
-            for arch in string.split(archList, " "):
-                archOptions = archOptions + " -arch " + arch
+            if archList:
+                for arch in string.split(archList, " "):
+                    archOptions = archOptions + " -arch " + arch
+            else:
+                archOptions = "-arch x86_64"
     allTests = []
     for f in os.listdir(testsSrcTopDir):
         if f.endswith(".dtest"):
     allTests = []
     for f in os.listdir(testsSrcTopDir):
         if f.endswith(".dtest"):
diff --git a/testing/test-cases/NSAddImage-fail.dtest/main.c b/testing/test-cases/NSAddImage-fail.dtest/main.c
new file mode 100644 (file)
index 0000000..74bf245
--- /dev/null
@@ -0,0 +1,35 @@
+// BUILD_ONLY: MacOSX
+
+// BUILD:  $CC main.c            -o $BUILD_DIR/NSAddImage-fail.exe -Wno-deprecated-declarations
+
+// RUN:  ./NSAddImage-fail.exe return
+// RUN:  NOCR_TEST_NAME="NSAddImage-fail expected abort" $REQUIRE_CRASH  ./NSAddImage-fail.exe abort
+
+
+
+#include <stdio.h>
+#include <string.h>
+#include <dlfcn.h>
+#include <mach-o/dyld.h>
+
+
+int main(int argc, const char* argv[])
+{
+    const char* arg = argv[1];
+
+    if ( strcmp(arg, "return") == 0 ) {
+        printf("[BEGIN] NSAddImage-fail %s\n", arg);
+        const struct mach_header* mh = NSAddImage("/xqz/42/libnotfound.xxx", NSADDIMAGE_OPTION_RETURN_ONLY_IF_LOADED);
+        if ( mh == NULL )
+            printf("[PASS] NSAddImage-fail %s\n", arg);
+        else
+            printf("[FAIL] NSAddImage-fail %s\n", arg);
+    }
+    else {
+        // run with nocr which print BEGIN/PASS/FAIL
+        NSAddImage("/xqz/42/libnotfound.xxx", 0);
+    }
+
+       return 0;
+}
+
diff --git a/testing/test-cases/NSAddImage-loaded.dtest/main.c b/testing/test-cases/NSAddImage-loaded.dtest/main.c
new file mode 100644 (file)
index 0000000..db7ca4b
--- /dev/null
@@ -0,0 +1,33 @@
+// BUILD_ONLY: MacOSX
+
+// BUILD:  $CC main.c            -o $BUILD_DIR/NSAddImage-loaded.exe -Wno-deprecated-declarations
+
+// RUN:  ./NSAddImage-loaded.exe return
+
+
+
+#include <stdio.h>
+#include <string.h>
+#include <dlfcn.h>
+#include <mach-o/dyld.h>
+
+
+int main(int argc, const char* argv[])
+{
+    printf("[BEGIN] NSAddImage-loaded\n");
+
+    // verify value is returned for image already loaded
+    const struct mach_header* mh = NSAddImage("/usr/lib/libSystem.B.dylib", NSADDIMAGE_OPTION_RETURN_ONLY_IF_LOADED);
+    if ( mh == NULL )
+        printf("[FAIL] NSAddImage-loaded\n");
+
+    // verify existing dylib is not loaded if it is not already loaded
+    mh = NSAddImage("/usr/lib/libz.dylib", NSADDIMAGE_OPTION_RETURN_ONLY_IF_LOADED);
+    if ( mh != NULL )
+        printf("[FAIL] NSAddImage-loaded\n");
+
+    printf("[PASS] NSAddImage-loaded\n");
+
+       return 0;
+}
+
diff --git a/testing/test-cases/NSAddressOfSymbol-basic.dtest/main.c b/testing/test-cases/NSAddressOfSymbol-basic.dtest/main.c
new file mode 100644 (file)
index 0000000..ddd9c74
--- /dev/null
@@ -0,0 +1,40 @@
+// BUILD_ONLY: MacOSX
+
+// BUILD:  $CC main.c            -o $BUILD_DIR/NSAddressOfSymbol-basic.exe -Wno-deprecated-declarations
+
+// RUN:  ./NSAddressOfSymbol-basic.exe
+
+
+
+#include <stdio.h>
+#include <string.h>
+#include <dlfcn.h>
+#include <mach-o/dyld.h>
+
+extern struct mach_header __dso_handle;
+
+int main(int argc, const char* argv[])
+{
+    printf("[BEGIN] NSAddressOfSymbol-basic\n");
+
+    NSSymbol sym = NSLookupSymbolInImage(&__dso_handle, "_main", NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR);
+    if ( sym == NULL ) {
+        printf("[FAIL] NSAddressOfSymbol-basic can't find main\n");
+        return 0;
+    }
+    void* mainAddr = NSAddressOfSymbol(sym);
+    if ( mainAddr != &main ) {
+        printf("[FAIL] NSAddressOfSymbol-basic address returned %p is not &main=%p\n", mainAddr, &main);
+        return 0;
+    }
+
+    // verify NULL works
+    if ( NSAddressOfSymbol(NULL) != NULL ) {
+        printf("[FAIL] NSAddressOfSymbol-basic NULL not handle\n");
+        return 0;
+    }
+
+    printf("[PASS] NSAddressOfSymbol-basic\n");
+       return 0;
+}
+
diff --git a/testing/test-cases/NSCreateObjectFileImageFromFile-basic.dtest/foo.c b/testing/test-cases/NSCreateObjectFileImageFromFile-basic.dtest/foo.c
new file mode 100644 (file)
index 0000000..837ec00
--- /dev/null
@@ -0,0 +1,7 @@
+
+#include <stdio.h>
+#include <string.h>
+
+void fooInBundle()
+{
+}
diff --git a/testing/test-cases/NSCreateObjectFileImageFromFile-basic.dtest/main.c b/testing/test-cases/NSCreateObjectFileImageFromFile-basic.dtest/main.c
new file mode 100644 (file)
index 0000000..49341b3
--- /dev/null
@@ -0,0 +1,70 @@
+// BUILD_ONLY: MacOSX
+
+// BUILD:  $CC main.c  -o $BUILD_DIR/NSCreateObjectFileImageFromFile-basic.exe -Wno-deprecated-declarations
+// BUILD:  $CC foo.c   -o $BUILD_DIR/foo.bundle -bundle
+
+// RUN:  ./NSCreateObjectFileImageFromFile-basic.exe $RUN_DIR/foo.bundle
+
+
+#include <stdio.h>
+#include <string.h>
+#include <dlfcn.h>
+#include <mach-o/dyld.h>
+
+
+int main(int argc, const char* argv[])
+{
+    printf("[BEGIN] NSCreateObjectFileImageFromFile-basic\n");
+
+    const char* path = argv[1];
+
+       NSObjectFileImage ofi;
+       if ( NSCreateObjectFileImageFromFile(path, &ofi) != NSObjectFileImageSuccess ) {
+               printf("[FAIL] NSCreateObjectFileImageFromFile failed\n");
+               return 0;
+       }
+       
+       NSModule mod = NSLinkModule(ofi, path, NSLINKMODULE_OPTION_NONE);
+       if ( mod == NULL ) {
+               printf("[FAIL] NSLinkModule failed\n");
+               return 0;
+       }
+       
+       NSSymbol sym = NSLookupSymbolInModule(mod, "_fooInBundle");
+       if ( sym == NULL ) {
+               printf("[FAIL] NSLookupSymbolInModule failed\n");
+               return 0;
+       }
+
+       void* func = NSAddressOfSymbol(sym);
+       if ( func == NULL ) {
+               printf("[FAIL] NSAddressOfSymbol failed\n");
+               return 0;
+       }
+
+    Dl_info info;
+    if ( dladdr(func, &info) == 0 ) {
+        printf("[FAIL] dladdr(&p, xx) failed");
+               return 0;
+    }
+    //printf("_fooInBundle found in %s\n", info.dli_fname);
+
+       if ( !NSUnLinkModule(mod, NSUNLINKMODULE_OPTION_NONE) ) {
+               printf("[FAIL] NSUnLinkModule failed\n");
+               return 0;
+       }
+
+    if ( dladdr(func, &info) != 0 ) {
+        printf("[FAIL] dladdr(&p, xx) found but should not have\n");
+               return 0;
+    }
+
+       if ( !NSDestroyObjectFileImage(ofi) ) {
+               printf("[FAIL] NSDestroyObjectFileImage failed\n");
+               return 0;
+       }
+
+    printf("[PASS] NSCreateObjectFileImageFromFile-basic\n");
+       return 0;
+}
+
diff --git a/testing/test-cases/NSCreateObjectFileImageFromMemory-basic.dtest/foo.c b/testing/test-cases/NSCreateObjectFileImageFromMemory-basic.dtest/foo.c
new file mode 100644 (file)
index 0000000..837ec00
--- /dev/null
@@ -0,0 +1,7 @@
+
+#include <stdio.h>
+#include <string.h>
+
+void fooInBundle()
+{
+}
diff --git a/testing/test-cases/NSCreateObjectFileImageFromMemory-basic.dtest/main.c b/testing/test-cases/NSCreateObjectFileImageFromMemory-basic.dtest/main.c
new file mode 100644 (file)
index 0000000..888c043
--- /dev/null
@@ -0,0 +1,115 @@
+// BUILD_ONLY: MacOSX
+
+// BUILD:  $CC main.c  -o $BUILD_DIR/NSCreateObjectFileImageFromMemory-basic.exe -Wno-deprecated-declarations
+// BUILD:  $CC foo.c   -o $BUILD_DIR/foo.bundle -bundle
+
+// RUN:  ./NSCreateObjectFileImageFromMemory-basic.exe $RUN_DIR/foo.bundle
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h> 
+#include <sys/mman.h> 
+#include <unistd.h>
+#include <fcntl.h>
+#include <dlfcn.h>
+#include <mach-o/dyld.h>
+
+
+static void checkBundle(const char* path, bool unlinkBeforeDestroy)
+{
+       int fd = open(path, O_RDONLY, 0);
+       if ( fd == -1 ) {
+               printf("[FAIL] open(%s) failed", path);
+               exit(0);
+       }
+
+       struct stat stat_buf;
+       if ( fstat(fd, &stat_buf) == -1) {
+               printf("[FAIL] fstat() failed\n");
+               exit(0);
+       }
+
+       void* loadAddress = mmap(NULL, stat_buf.st_size, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0);
+       if ( loadAddress == ((void*)(-1)) ) {
+               printf("[FAIL] mmap() failed\n");
+               exit(0);
+       }
+
+       close(fd);
+
+       NSObjectFileImage ofi;
+       if ( NSCreateObjectFileImageFromMemory(loadAddress, stat_buf.st_size, &ofi) != NSObjectFileImageSuccess ) {
+               printf("[FAIL] NSCreateObjectFileImageFromMemory failed\n");
+               exit(0);
+       }
+
+       NSModule mod = NSLinkModule(ofi, path, NSLINKMODULE_OPTION_NONE);
+       if ( mod == NULL ) {
+               printf("[FAIL] NSLinkModule failed\n");
+               exit(0);
+       }
+       
+   if ( !unlinkBeforeDestroy ) {
+        // API lets you destroy ofi and NSModule lives on
+        if ( !NSDestroyObjectFileImage(ofi) ) {
+            printf("[FAIL] NSDestroyObjectFileImage failed\n");
+            exit(0);
+        }
+    }
+
+       NSSymbol sym = NSLookupSymbolInModule(mod, "_fooInBundle");
+       if ( sym == NULL ) {
+               printf("[FAIL] NSLookupSymbolInModule failed\n");
+               exit(0);
+       }
+
+       void* func = NSAddressOfSymbol(sym);
+       if ( func == NULL ) {
+               printf("[FAIL] NSAddressOfSymbol failed\n");
+               exit(0);
+       }
+
+    Dl_info info;
+    if ( dladdr(func, &info) == 0 ) {
+        printf("[FAIL] dladdr(&p, xx) failed\n");
+        exit(0);
+    }
+    //printf("_fooInBundle found in %s\n", info.dli_fname);
+
+    if ( !NSUnLinkModule(mod, NSUNLINKMODULE_OPTION_NONE) ) {
+            printf("[FAIL] NSUnLinkModule failed\n");
+            exit(0);
+    }
+
+    if ( dladdr(func, &info) != 0 ) {
+        printf("[FAIL] dladdr(&p, xx) found but should not have\n");
+        exit(0);
+    }
+
+    if ( unlinkBeforeDestroy ) {
+        if ( !NSDestroyObjectFileImage(ofi) ) {
+            printf("[FAIL] NSDestroyObjectFileImage failed\n");
+            exit(0);
+        }
+    }
+}
+
+
+int main(int argc, const char* argv[])
+{
+    printf("[BEGIN] NSCreateObjectFileImageFromMemory-basic\n");
+
+    checkBundle(argv[1], true);
+    checkBundle(argv[1], false);
+
+    // Now go again enough times to flush out any limits in our dlopen encodings.
+    for (unsigned i = 0; i != 255; ++i)
+      checkBundle(argv[1], false);
+
+    printf("[PASS] NSCreateObjectFileImageFromMemory-basic\n");
+    return 0;
+}
+
diff --git a/testing/test-cases/NSLookupSymbolInImage-basic.dtest/main.c b/testing/test-cases/NSLookupSymbolInImage-basic.dtest/main.c
new file mode 100644 (file)
index 0000000..1adab58
--- /dev/null
@@ -0,0 +1,39 @@
+// BUILD_ONLY: MacOSX
+
+// BUILD:  $CC main.c            -o $BUILD_DIR/NSLookupSymbolInImage-basic.exe -Wno-deprecated-declarations
+
+// RUN:  ./NSLookupSymbolInImage-basic.exe
+
+
+#include <stdio.h>
+#include <string.h>
+#include <dlfcn.h>
+#include <mach-o/dyld.h>
+
+extern struct mach_header __dso_handle;
+
+int main(int argc, const char* argv[])
+{
+    printf("[BEGIN] NSLookupSymbolInImage-basic\n");
+
+    // verify known symbol works
+    NSSymbol sym = NSLookupSymbolInImage(&__dso_handle, "_main", NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR);
+    if ( sym == NULL ) {
+         printf("[FAIL] NSLookupSymbolInImage-basic _main\n");
+         return 0;
+    }
+
+    // verify mode where NSLookupSymbolInImage() returns NULL if symbol not found
+    sym = NSLookupSymbolInImage(&__dso_handle, "_42hhg", NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR);
+    if ( sym != NULL ) {
+         printf("[FAIL] NSLookupSymbolInImage-basic _42hhg\n");
+         return 0;
+    }
+
+    // Note: NSLookupSymbolInImage is documented to abort if symbol not found and NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR not used,
+    // but dyld 2 just returned NULL, so no need to test that.
+
+    printf("[PASS] NSLookupSymbolInImage-basic\n");
+       return 0;
+}
+
diff --git a/testing/test-cases/_dyld_is_memory_immutable.dtest/bar.c b/testing/test-cases/_dyld_is_memory_immutable.dtest/bar.c
new file mode 100644 (file)
index 0000000..70b4d1b
--- /dev/null
@@ -0,0 +1,4 @@
+const char* bar()
+{
+    return "bar";
+}
diff --git a/testing/test-cases/_dyld_is_memory_immutable.dtest/foo.c b/testing/test-cases/_dyld_is_memory_immutable.dtest/foo.c
new file mode 100644 (file)
index 0000000..a5db092
--- /dev/null
@@ -0,0 +1,4 @@
+const char* foo()
+{
+    return "foo";
+}
diff --git a/testing/test-cases/_dyld_is_memory_immutable.dtest/main.c b/testing/test-cases/_dyld_is_memory_immutable.dtest/main.c
new file mode 100644 (file)
index 0000000..a58b85b
--- /dev/null
@@ -0,0 +1,79 @@
+
+// BUILD:  $CC foo.c -dynamiclib  -install_name $RUN_DIR/libfoo.dylib -o $BUILD_DIR/libfoo.dylib
+// BUILD:  $CC bar.c -dynamiclib  -install_name $RUN_DIR/libbar.dylib -o $BUILD_DIR/libbar.dylib
+// BUILD:  $CC main.c -DRUN_DIR="$RUN_DIR" $BUILD_DIR/libfoo.dylib    -o $BUILD_DIR/dyld_immutable_test.exe
+
+// RUN:  ./dyld_immutable_test.exe
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <dlfcn.h>
+#include <mach-o/dyld.h>
+#include <mach-o/dyld_priv.h>
+
+typedef const char* (*BarProc)(void);
+
+extern uint32_t _cpu_capabilities;
+extern const char* foo();
+
+const char* myStr = "myStr";
+
+int myInt;
+
+
+int main()
+{
+    printf("[BEGIN] _dyld_is_memory_immutable\n");
+
+    if ( !_dyld_is_memory_immutable(myStr, 6) ) {
+               printf("[FAIL] _dyld_is_memory_immutable() returned false for string in main executable\n");
+        return 0;
+    }
+
+    if ( _dyld_is_memory_immutable(strdup("hello"), 6) ) {
+               printf("[FAIL] _dyld_is_memory_immutable() returned true for result from strdup()\n");
+        return 0;
+    }
+
+    if ( _dyld_is_memory_immutable(&myInt, 4) ) {
+               printf("[FAIL] _dyld_is_memory_immutable() returned true for global variabe in main executable\n");
+        return 0;
+    }
+
+    if ( !_dyld_is_memory_immutable(foo(), 4) ) {
+               printf("[FAIL] _dyld_is_memory_immutable() returned false for string in statically linked dylib\n");
+        return 0;
+    }
+
+    if ( !_dyld_is_memory_immutable(&strcpy, 4) ) {
+               printf("[FAIL] _dyld_is_memory_immutable() returned false for function in dyld shared cache\n");
+        return 0;
+    }
+
+    if ( _dyld_is_memory_immutable(&_cpu_capabilities, 4) ) {
+               printf("[FAIL] _dyld_is_memory_immutable() returned true for global variable in shared cache\n");
+        return 0;
+    }
+
+       void* handle = dlopen(RUN_DIR "/libbar.dylib", RTLD_FIRST);
+    if ( handle == NULL ) {
+               printf("[FAIL] dlopen(libbar.dylib) failed");
+        return 0;
+    }
+
+    BarProc proc = dlsym(handle, "bar");
+    if ( proc == NULL ) {
+               printf("[FAIL] dlsym(bar) failed\n");
+        return 0;
+    }
+    const char* barStr = (*proc)();
+    if ( _dyld_is_memory_immutable(barStr, 4) ) {
+               printf("[FAIL] _dyld_is_memory_immutable() returned true for string in unloadable dylib\n");
+        return 0;
+    }
+
+
+    printf("[PASS] _dyld_is_memory_immutable\n");
+    return 0;
+}
+
diff --git a/testing/test-cases/_dyld_register_func_for_add_image.dtest/foo.c b/testing/test-cases/_dyld_register_func_for_add_image.dtest/foo.c
new file mode 100644 (file)
index 0000000..85e6cd8
--- /dev/null
@@ -0,0 +1 @@
+void foo() {}
diff --git a/testing/test-cases/_dyld_register_func_for_add_image.dtest/main.cxx b/testing/test-cases/_dyld_register_func_for_add_image.dtest/main.cxx
new file mode 100644 (file)
index 0000000..905a153
--- /dev/null
@@ -0,0 +1,77 @@
+
+// BUILD:  $CXX main.cxx -o $BUILD_DIR/dyld_register_test.exe -DRUN_DIR="$RUN_DIR"
+// BUILD:  $CC  foo.c -dynamiclib  -install_name $RUN_DIR/libfoo.dylib -o $BUILD_DIR/libfoo.dylib
+
+// RUN:  ./dyld_register_test.exe
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <dlfcn.h>
+#include <mach-o/dyld.h>
+#include <mach-o/dyld_priv.h>
+
+#include <unordered_set>
+
+extern mach_header __dso_handle;
+
+static std::unordered_set<const mach_header*> sCurrentImages;
+
+static void notify(const mach_header* mh, intptr_t vmaddr_slide)
+{
+    //fprintf(stderr, "mh=%p\n", mh);
+    if ( sCurrentImages.count(mh) != 0 ) {
+        printf("[FAIL] _dyld_register_func_for_add_image: notified twice about %p\n", mh);
+        exit(0);
+    }
+    sCurrentImages.insert(mh);
+}
+
+
+int main()
+{
+    printf("[BEGIN] _dyld_register_func_for_add_image\n");
+
+    _dyld_register_func_for_add_image(&notify);
+
+    // verify we were notified about already loaded images
+    if ( sCurrentImages.count(&__dso_handle) == 0 ) {
+               printf("[FAIL] _dyld_register_func_for_add_image() did not notify us about main executable");
+               exit(0);
+    }
+    const mach_header* libSysMH = dyld_image_header_containing_address((void*)&printf);
+    if ( sCurrentImages.count(libSysMH) == 0 ) {
+               printf("[FAIL] _dyld_register_func_for_add_image() did not notify us about libsystem_c.dylib");
+               exit(0);
+    }
+
+       void* handle = dlopen(RUN_DIR "/libfoo.dylib", RTLD_FIRST);
+       if ( handle == NULL ) {
+               printf("[FAIL] dlopen(\"%s\") failed with: %s", RUN_DIR "/libfoo.dylib", dlerror());
+               exit(0);
+       }
+       
+       void* sym = dlsym(handle, "foo");
+       if ( sym == NULL ) {
+               printf("[FAIL] dlsym(handle, \"foo\") failed");
+               exit(0);
+       }
+
+    // verify we were notified about load of libfoo.dylib
+    const mach_header* libfooMH = dyld_image_header_containing_address(sym);
+    if ( sCurrentImages.count(libfooMH) == 0 ) {
+               printf("[FAIL] _dyld_register_func_for_add_image() did not notify us about libfoo.dylib");
+               exit(0);
+    }
+
+
+       int result = dlclose(handle);
+       if ( result != 0 ) {
+        printf("[FAIL] dlclose(handle) returned %d", result);
+        exit(0);
+       }
+
+
+    printf("[PASS] _dyld_register_func_for_add_image\n");
+    return 0;
+}
+
index c79c78a59a6205de4c1eb5fffdf6de7f547b68b2..672595da577898ee298de9b59f9c2507783e43ea 100644 (file)
@@ -24,12 +24,12 @@ int main()
 
     Dl_info info;
     if ( dladdr(&main, &info) == 0 ) {
 
     Dl_info info;
     if ( dladdr(&main, &info) == 0 ) {
-        printf("[FAIL] dladdr(&main, xx) failed");
+        printf("[FAIL] dladdr(&main, xx) failed\n");
         return 0;
     }
 
     if ( info.dli_sname != NULL ){
         return 0;
     }
 
     if ( info.dli_sname != NULL ){
-        printf("[FAIL] dladdr() returned: \"%s\" instead of NULL", info.dli_sname);
+        printf("[FAIL] dladdr() returned: \"%s\" instead of NULL\n", info.dli_sname);
         return 0;
     }
 
         return 0;
     }
 
index d047353f2dea02492c6783567dcadc4bf9acb8ad..eeeabca8a57ffca2e34d18ac2ea6d4d4bf0a92a6 100644 (file)
@@ -30,19 +30,19 @@ static void verifybar()
 {
     Dl_info info;
     if ( dladdr(&bar, &info) == 0 ) {
 {
     Dl_info info;
     if ( dladdr(&bar, &info) == 0 ) {
-        printf("[FAIL] dladdr(&bar, xx) failed");
+        printf("[FAIL] dladdr(&bar, xx) failed\n");
         exit(0);
     }
     if ( strcmp(info.dli_sname, "bar") != 0 ) {
         exit(0);
     }
     if ( strcmp(info.dli_sname, "bar") != 0 ) {
-        printf("[FAIL] dladdr()->dli_sname is \"%s\" instead of \"bar\"", info.dli_sname);
+        printf("[FAIL] dladdr()->dli_sname is \"%s\" instead of \"bar\"\n", info.dli_sname);
         exit(0);
     }
     if ( info.dli_saddr != &bar) {
         exit(0);
     }
     if ( info.dli_saddr != &bar) {
-        printf("[FAIL] dladdr()->dli_saddr is not &bar");
+        printf("[FAIL] dladdr()->dli_saddr is not &bar\n");
         exit(0);
     }
     if ( info.dli_fbase != dyld_image_header_containing_address(&bar) ) {
         exit(0);
     }
     if ( info.dli_fbase != dyld_image_header_containing_address(&bar) ) {
-        printf("[FAIL] dladdr()->dli_fbase is not image that contains &bar");
+        printf("[FAIL] dladdr()->dli_fbase is not image that contains &bar\n");
         exit(0);
     }
 }
         exit(0);
     }
 }
@@ -52,19 +52,19 @@ static void verifyfoo()
 {
     Dl_info info;
     if ( dladdr(&foo, &info) == 0 ) {
 {
     Dl_info info;
     if ( dladdr(&foo, &info) == 0 ) {
-        printf("[FAIL] dladdr(&foo, xx) failed");
+        printf("[FAIL] dladdr(&foo, xx) failed\n");
         exit(0);
     }
     if ( strcmp(info.dli_sname, "foo") != 0 ) {
         exit(0);
     }
     if ( strcmp(info.dli_sname, "foo") != 0 ) {
-        printf("[FAIL] dladdr()->dli_sname is \"%s\" instead of \"foo\"", info.dli_sname);
+        printf("[FAIL] dladdr()->dli_sname is \"%s\" instead of \"foo\"\n", info.dli_sname);
         exit(0);
     }
     if ( info.dli_saddr != &foo) {
         exit(0);
     }
     if ( info.dli_saddr != &foo) {
-        printf("[FAIL] dladdr()->dli_saddr is not &foo");
+        printf("[FAIL] dladdr()->dli_saddr is not &foo\n");
         exit(0);
     }
     if ( info.dli_fbase != dyld_image_header_containing_address(&foo) ) {
         exit(0);
     }
     if ( info.dli_fbase != dyld_image_header_containing_address(&foo) ) {
-        printf("[FAIL] dladdr()->dli_fbase is not image that contains &foo");
+        printf("[FAIL] dladdr()->dli_fbase is not image that contains &foo\n");
         exit(0);
     }
 }
         exit(0);
     }
 }
@@ -74,19 +74,19 @@ static void verifyhide()
 {
     Dl_info info;
     if ( dladdr(&hide, &info) == 0 ) {
 {
     Dl_info info;
     if ( dladdr(&hide, &info) == 0 ) {
-        printf("[FAIL] dladdr(&hide, xx) failed");
+        printf("[FAIL] dladdr(&hide, xx) failed\n");
         exit(0);
     }
     if ( strcmp(info.dli_sname, "hide") != 0 ) {
         exit(0);
     }
     if ( strcmp(info.dli_sname, "hide") != 0 ) {
-        printf("[FAIL] dladdr()->dli_sname is \"%s\" instead of \"hide\"", info.dli_sname);
+        printf("[FAIL] dladdr()->dli_sname is \"%s\" instead of \"hide\"\n", info.dli_sname);
         exit(0);
     }
     if ( info.dli_saddr != &hide) {
         exit(0);
     }
     if ( info.dli_saddr != &hide) {
-        printf("[FAIL] dladdr()->dli_saddr is not &hide");
+        printf("[FAIL] dladdr()->dli_saddr is not &hide\n");
         exit(0);
     }
     if ( info.dli_fbase != dyld_image_header_containing_address(&hide) ) {
         exit(0);
     }
     if ( info.dli_fbase != dyld_image_header_containing_address(&hide) ) {
-        printf("[FAIL] dladdr()->dli_fbase is not image that contains &hide");
+        printf("[FAIL] dladdr()->dli_fbase is not image that contains &hide\n");
         exit(0);
     }
 }
         exit(0);
     }
 }
@@ -96,19 +96,19 @@ static void verifymalloc()
 {
     Dl_info info;
     if ( dladdr(&malloc, &info) == 0 ) {
 {
     Dl_info info;
     if ( dladdr(&malloc, &info) == 0 ) {
-        printf("[FAIL] dladdr(&malloc, xx) failed");
+        printf("[FAIL] dladdr(&malloc, xx) failed\n");
         exit(0);
     }
     if ( strcmp(info.dli_sname, "malloc") != 0 ) {
         exit(0);
     }
     if ( strcmp(info.dli_sname, "malloc") != 0 ) {
-        printf("[FAIL] dladdr()->dli_sname is \"%s\" instead of \"malloc\"", info.dli_sname);
+        printf("[FAIL] dladdr()->dli_sname is \"%s\" instead of \"malloc\"\n", info.dli_sname);
         exit(0);
     }
     if ( info.dli_saddr != &malloc) {
         exit(0);
     }
     if ( info.dli_saddr != &malloc) {
-        printf("[FAIL] dladdr()->dli_saddr is not &malloc");
+        printf("[FAIL] dladdr()->dli_saddr is not &malloc\n");
         exit(0);
     }
     if ( info.dli_fbase != dyld_image_header_containing_address(&malloc) ) {
         exit(0);
     }
     if ( info.dli_fbase != dyld_image_header_containing_address(&malloc) ) {
-        printf("[FAIL] dladdr()->dli_fbase is not image that contains &malloc");
+        printf("[FAIL] dladdr()->dli_fbase is not image that contains &malloc\n");
         exit(0);
     }
 }
         exit(0);
     }
 }
diff --git a/testing/test-cases/dladdr-dylib.dtest/foo.c b/testing/test-cases/dladdr-dylib.dtest/foo.c
new file mode 100644 (file)
index 0000000..e45b27f
--- /dev/null
@@ -0,0 +1,99 @@
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h> 
+#include <dlfcn.h> 
+#include <mach-o/dyld_priv.h>
+
+extern void* __dso_handle;
+
+int dylib_bar()
+{
+    return 2;
+}
+
+static int dylib_foo()
+{
+    return 3;
+}
+
+__attribute__((visibility("hidden"))) int dylib_hide()
+{
+    return 4;
+}
+
+// checks global symbol
+static void verifybar()
+{
+    Dl_info info;
+    if ( dladdr(&dylib_bar, &info) == 0 ) {
+        printf("[FAIL] dladdr(&dylib_bar, xx) failed\n");
+        exit(0);
+    }
+    if ( strcmp(info.dli_sname, "dylib_bar") != 0 ) {
+        printf("[FAIL] dladdr()->dli_sname is \"%s\" instead of \"dylib_bar\"\n", info.dli_sname);
+        exit(0);
+    }
+    if ( info.dli_saddr != &dylib_bar) {
+        printf("[FAIL] dladdr()->dli_saddr is not &dylib_bar\n");
+        exit(0);
+    }
+    if ( info.dli_fbase != &__dso_handle ) {
+        printf("[FAIL] dladdr()->dli_fbase is not image that contains &dylib_bar\n");
+        exit(0);
+    }
+}
+
+// checks local symbol
+static void verifyfoo()
+{
+    Dl_info info;
+    if ( dladdr(&dylib_foo, &info) == 0 ) {
+        printf("[FAIL] dladdr(&dylib_foo, xx) failed\n");
+        exit(0);
+    }
+    if ( strcmp(info.dli_sname, "dylib_foo") != 0 ) {
+        printf("[FAIL] dladdr()->dli_sname is \"%s\" instead of \"dylib_foo\"\n", info.dli_sname);
+        exit(0);
+    }
+    if ( info.dli_saddr != &dylib_foo) {
+        printf("[FAIL] dladdr()->dli_saddr is not &dylib_foo\n");
+        exit(0);
+    }
+    if ( info.dli_fbase != &__dso_handle ) {
+        printf("[FAIL] dladdr()->dli_fbase is not image that contains &dylib_foo\n");
+        exit(0);
+    }
+}
+
+// checks hidden symbol
+static void verifyhide()
+{
+    Dl_info info;
+    if ( dladdr(&dylib_hide, &info) == 0 ) {
+        printf("[FAIL] dladdr(&dylib_hide, xx) failed\n");
+        exit(0);
+    }
+    if ( strcmp(info.dli_sname, "dylib_hide") != 0 ) {
+        printf("[FAIL] dladdr()->dli_sname is \"%s\" instead of \"dylib_hide\"\n", info.dli_sname);
+        exit(0);
+    }
+    if ( info.dli_saddr != &dylib_hide) {
+        printf("[FAIL] dladdr()->dli_saddr is not &dylib_hide\n");
+        exit(0);
+    }
+    if ( info.dli_fbase != &__dso_handle ) {
+        printf("[FAIL] dladdr()->dli_fbase is not image that contains &dylib_hide\n");
+        exit(0);
+    }
+}
+
+
+void verifyDylib()
+{
+    verifybar();
+    verifyfoo();
+    verifyhide();
+}
+
diff --git a/testing/test-cases/dladdr-dylib.dtest/main.c b/testing/test-cases/dladdr-dylib.dtest/main.c
new file mode 100644 (file)
index 0000000..5d9d0c9
--- /dev/null
@@ -0,0 +1,134 @@
+
+// BUILD:  $CC foo.c -dynamiclib  -install_name $RUN_DIR/libfoo.dylib -o $BUILD_DIR/libfoo.dylib
+// BUILD:  $CC main.c $BUILD_DIR/libfoo.dylib -o $BUILD_DIR/dladdr-basic.exe
+
+// RUN:  ./dladdr-basic.exe
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h> 
+#include <dlfcn.h> 
+#include <mach-o/dyld_priv.h>
+
+extern void* __dso_handle;
+
+extern void verifyDylib();
+
+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 ) {
+        printf("[FAIL] dladdr(&bar, xx) failed\n");
+        exit(0);
+    }
+    if ( strcmp(info.dli_sname, "bar") != 0 ) {
+        printf("[FAIL] dladdr()->dli_sname is \"%s\" instead of \"bar\"\n", info.dli_sname);
+        exit(0);
+    }
+    if ( info.dli_saddr != &bar) {
+        printf("[FAIL] dladdr()->dli_saddr is not &bar\n");
+        exit(0);
+    }
+    if ( info.dli_fbase != &__dso_handle ) {
+        printf("[FAIL] dladdr()->dli_fbase is not image that contains &bar\n");
+        exit(0);
+    }
+}
+
+// checks local symbol
+static void verifyfoo()
+{
+    Dl_info info;
+    if ( dladdr(&foo, &info) == 0 ) {
+        printf("[FAIL] dladdr(&foo, xx) failed\n");
+        exit(0);
+    }
+    if ( strcmp(info.dli_sname, "foo") != 0 ) {
+        printf("[FAIL] dladdr()->dli_sname is \"%s\" instead of \"foo\"\n", info.dli_sname);
+        exit(0);
+    }
+    if ( info.dli_saddr != &foo) {
+        printf("[FAIL] dladdr()->dli_saddr is not &foo\n");
+        exit(0);
+    }
+    if ( info.dli_fbase != &__dso_handle ) {
+        printf("[FAIL] dladdr()->dli_fbase is not image that contains &foo\n");
+        exit(0);
+    }
+}
+
+// checks hidden symbol
+static void verifyhide()
+{
+    Dl_info info;
+    if ( dladdr(&hide, &info) == 0 ) {
+        printf("[FAIL] dladdr(&hide, xx) failed\n");
+        exit(0);
+    }
+    if ( strcmp(info.dli_sname, "hide") != 0 ) {
+        printf("[FAIL] dladdr()->dli_sname is \"%s\" instead of \"hide\"\n", info.dli_sname);
+        exit(0);
+    }
+    if ( info.dli_saddr != &hide) {
+        printf("[FAIL] dladdr()->dli_saddr is not &hide\n");
+        exit(0);
+    }
+    if ( info.dli_fbase != &__dso_handle ) {
+        printf("[FAIL] dladdr()->dli_fbase is not image that contains &hide\n");
+        exit(0);
+    }
+}
+
+// checks dylib symbol
+static void verifymalloc()
+{
+    Dl_info info;
+    if ( dladdr(&malloc, &info) == 0 ) {
+        printf("[FAIL] dladdr(&malloc, xx) failed\n");
+        exit(0);
+    }
+    if ( strcmp(info.dli_sname, "malloc") != 0 ) {
+        printf("[FAIL] dladdr()->dli_sname is \"%s\" instead of \"malloc\"\n", info.dli_sname);
+        exit(0);
+    }
+    if ( info.dli_saddr != &malloc) {
+        printf("[FAIL] dladdr()->dli_saddr is not &malloc\n");
+        exit(0);
+    }
+    if ( info.dli_fbase != dyld_image_header_containing_address(&malloc) ) {
+        printf("[FAIL] dladdr()->dli_fbase is not image that contains &malloc\n");
+        exit(0);
+    }
+}
+
+
+int main()
+{
+    printf("[BEGIN] dladdr-basic\n");
+    verifybar();
+    verifyhide();
+    verifyfoo();
+    verifymalloc();
+
+    verifyDylib();
+
+    printf("[PASS] dladdr-basic\n");
+    return 0;
+}
+
diff --git a/testing/test-cases/dlopen-DYLD_LIBRARY_PATH.dtest/bar.c b/testing/test-cases/dlopen-DYLD_LIBRARY_PATH.dtest/bar.c
new file mode 100644 (file)
index 0000000..39ac49e
--- /dev/null
@@ -0,0 +1,5 @@
+int bar()
+{
+       return VALUE;
+}
+
diff --git a/testing/test-cases/dlopen-DYLD_LIBRARY_PATH.dtest/foo.c b/testing/test-cases/dlopen-DYLD_LIBRARY_PATH.dtest/foo.c
new file mode 100644 (file)
index 0000000..958e1af
--- /dev/null
@@ -0,0 +1,7 @@
+extern int bar();
+
+int foo()
+{
+       return bar() + VALUE;
+}
+
diff --git a/testing/test-cases/dlopen-DYLD_LIBRARY_PATH.dtest/main.c b/testing/test-cases/dlopen-DYLD_LIBRARY_PATH.dtest/main.c
new file mode 100644 (file)
index 0000000..96eb08b
--- /dev/null
@@ -0,0 +1,84 @@
+
+// BUILD:  mkdir -p $BUILD_DIR/door1 $BUILD_DIR/door2
+// BUILD:  $CC bar.c -dynamiclib -o $BUILD_DIR/door1/libbar.dylib -install_name $RUN_DIR/libbar.dylib -DVALUE=3
+// BUILD:  $CC bar.c -dynamiclib -o $BUILD_DIR/door2/libbar.dylib -install_name $RUN_DIR/libbar.dylib -DVALUE=17
+// BUILD:  $CC foo.c -dynamiclib -o $BUILD_DIR/door1/libfoo.dylib -install_name $RUN_DIR/libfoo.dylib -DVALUE=10  $BUILD_DIR/door1/libbar.dylib
+// BUILD:  $CC foo.c -dynamiclib -o $BUILD_DIR/door2/libfoo.dylib -install_name $RUN_DIR/libfoo.dylib -DVALUE=25  $BUILD_DIR/door2/libbar.dylib
+// BUILD:  $CC main.c            -o $BUILD_DIR/main.exe 
+// BUILD:  $DYLD_ENV_VARS_ENABLE $BUILD_DIR/main.exe
+
+// RUN:  DYLD_LIBRARY_PATH=$RUN_DIR/door1/                  ./main.exe  13
+// RUN:  DYLD_LIBRARY_PATH=$RUN_DIR/door2                   ./main.exe  42
+// RUN:  DYLD_LIBRARY_PATH=$RUN_DIR/door3/:$RUN_DIR/door2/  ./main.exe  42
+
+#include <stdio.h>
+#include <dlfcn.h>
+#include <stdlib.h>
+
+// Program dlopen()s libfoo.dylib which was linked against libbar.dylib
+// Neither have valid paths and must be found via DYLD_LIBRARY_PATH
+// This test direct and indirect loading.
+
+int main(int argc, const char* argv[])
+{
+    const char* env = getenv("DYLD_LIBRARY_PATH");
+    if ( env == NULL ) {
+        printf("[BEGIN] dlopen-DYLD_LIBRARY_PATH\n");
+        printf("[FAIL] dlopen-DYLD_LIBRARY_PATH, env not set\n");
+               return 0;
+    }
+    const char* valueStr = argv[1];
+    if ( valueStr == NULL ) {
+        printf("[BEGIN] dlopen-DYLD_LIBRARY_PATH\n");
+        printf("[FAIL] dlopen-DYLD_LIBRARY_PATH, arg1 value not set\n");
+               return 0;
+    }
+    char* end;
+    long value = strtol(valueStr, &end, 0);
+
+    printf("[BEGIN] dlopen-DYLD_LIBRARY_PATH %s\n", env);
+
+       void* handle = dlopen("/bogus/libfoo.dylib", RTLD_LAZY);
+       if ( handle == NULL ) {
+        printf("dlerror(): %s\n", dlerror());
+        printf("[FAIL] dlopen-DYLD_LIBRARY_PATH %s\n", env);
+               return 0;
+       }
+       
+    typedef int (*FooProc)();
+
+       FooProc sym = (FooProc)dlsym(handle, "foo");
+       if ( sym == NULL ) {
+        printf("dlerror(): %s\n", dlerror());
+        printf("[FAIL] dlopen-DYLD_LIBRARY_PATH %s\n", env);
+               return 0;
+       }
+
+    int result = (*sym)();
+    if ( result != value ) {
+        printf("result=%d, expected %ld (str=%s)\n", result, value, valueStr);
+        printf("[FAIL] dlopen-DYLD_LIBRARY_PATH %s\n", env);
+               return 0;
+       }
+
+       int r = dlclose(handle);
+       if ( r != 0 ) {
+        printf("dlclose() returned %d\n", r);
+        printf("[FAIL] dlopen-DYLD_LIBRARY_PATH %s\n", env);
+               return 0;
+       }
+
+       void* handle2 = dlopen("/junk/libfoo.dylib", RTLD_LAZY);
+       if ( handle2 == NULL ) {
+        printf("dlerror(): %s\n", dlerror());
+        printf("[FAIL] dlopen-DYLD_LIBRARY_PATH %s\n", env);
+               return 0;
+       }
+
+
+
+    printf("[PASS] dlopen-DYLD_LIBRARY_PATH %s\n", env);
+
+       return 0;
+}
+
diff --git a/testing/test-cases/dlopen-bad-file.dtest/bad.txt b/testing/test-cases/dlopen-bad-file.dtest/bad.txt
new file mode 100644 (file)
index 0000000..43b3565
--- /dev/null
@@ -0,0 +1 @@
+bad file
diff --git a/testing/test-cases/dlopen-bad-file.dtest/main.c b/testing/test-cases/dlopen-bad-file.dtest/main.c
new file mode 100644 (file)
index 0000000..63108fd
--- /dev/null
@@ -0,0 +1,47 @@
+
+// BUILD:  cp bad.txt $BUILD_DIR/libnota.dylib
+// BUILD:  $CC main.c  -o $BUILD_DIR/dlopen-bad-file.exe -DRUN_DIR="$RUN_DIR"
+
+// RUN:  ./dlopen-bad-file.exe
+
+#include <stdio.h>
+#include <dlfcn.h>
+#include <string.h>
+
+
+
+int main()
+{
+    printf("[BEGIN] dlopen-bad-file\n");
+
+    // try to dlopen() a text file
+       void* handle = dlopen(RUN_DIR "/libnota.dylib", RTLD_FIRST);
+       if ( handle != NULL ) {
+        printf("[FAIL] dlopen-bad-file should have failed on non-mach-o file %s\n", RUN_DIR "/libnota.dylib");
+               return 0;
+       }
+    const char* message = dlerror();
+    if ( (strstr(message, "mach-o") == NULL) && (strstr(message, "too short") == NULL) ) {
+        printf("dlerror: %s\n", message);
+        printf("[FAIL] dlopen-bad-file dlerror() message did not contain 'mach-o'\n");
+               return 0;
+       }
+
+    // try to dlopen() a directory
+       handle = dlopen(RUN_DIR, RTLD_FIRST);
+       if ( handle != NULL ) {
+        printf("[FAIL] dlopen-bad-file should have failed on dir %s\n", RUN_DIR);
+               return 0;
+       }
+    message = dlerror();
+    if ( strstr(message, "not a file") == NULL ) {
+        printf("dlerror: %s\n", message);
+        printf("[FAIL] dlopen-bad-file dlerror() message did not contain 'not a file'\n");
+               return 0;
+       }
+
+    printf("[PASS] dlopen-bad-file\n");
+
+       return 0;
+}
+
diff --git a/testing/test-cases/dlopen-empty-data.dtest/foo.c b/testing/test-cases/dlopen-empty-data.dtest/foo.c
new file mode 100644 (file)
index 0000000..fd55770
--- /dev/null
@@ -0,0 +1,8 @@
+
+int dummy;
+
+int foo()
+{
+       return 10;
+}
+
diff --git a/testing/test-cases/dlopen-empty-data.dtest/main.c b/testing/test-cases/dlopen-empty-data.dtest/main.c
new file mode 100644 (file)
index 0000000..9bf8051
--- /dev/null
@@ -0,0 +1,27 @@
+
+// BUILD:  $CC foo.c -dynamiclib  -install_name $RUN_DIR/libfoo-static.dylib  -o $BUILD_DIR/libfoo-static.dylib
+// BUILD:  $CC foo.c -dynamiclib  -install_name $RUN_DIR/libfoo-dynamic.dylib -o $BUILD_DIR/libfoo-dynamic.dylib
+// BUILD:  $CC main.c $BUILD_DIR/libfoo-static.dylib -o $BUILD_DIR/dlopen-empty-data.exe -DRUN_DIR="$RUN_DIR"
+
+
+// RUN:  ./dlopen-empty-data.exe
+
+#include <stdio.h>
+#include <dlfcn.h>
+
+// libfoo-static.dylib and libfoo-dynamic.dylib each have an empty (no disk size) DATA segment
+
+int main()
+{
+    printf("[BEGIN] dlopen-empty-data\n");
+
+    void* handle = dlopen(RUN_DIR "/libfoo-dynamic.dylib", RTLD_LAZY);
+    if ( handle == NULL ) {
+        printf("[FAIL]  dlopen-empty-data: libfoo-dynamic.dylib could not be loaded: %s\n", dlerror());
+        return 0;
+    }
+
+    printf("[PASS]  dlopen-empty-data\n");
+    return 0;
+}
+
diff --git a/testing/test-cases/dlopen-flat.dtest/bar.c b/testing/test-cases/dlopen-flat.dtest/bar.c
new file mode 100644 (file)
index 0000000..0f338d1
--- /dev/null
@@ -0,0 +1,15 @@
+
+extern int foo();
+
+extern int gInitialisersCalled;
+
+__attribute__((constructor))
+static void onLoad() {
+       ++gInitialisersCalled;
+}
+
+typedef int(*retTy)();
+
+retTy bar() {
+       return &foo;
+}
\ No newline at end of file
diff --git a/testing/test-cases/dlopen-flat.dtest/foo.c b/testing/test-cases/dlopen-flat.dtest/foo.c
new file mode 100644 (file)
index 0000000..2414437
--- /dev/null
@@ -0,0 +1,13 @@
+
+
+extern int gInitialisersCalled;
+
+__attribute__((constructor))
+static void onLoad() {
+       ++gInitialisersCalled;
+}
+
+
+int foo() {
+       return 0;
+}
\ No newline at end of file
diff --git a/testing/test-cases/dlopen-flat.dtest/main.c b/testing/test-cases/dlopen-flat.dtest/main.c
new file mode 100644 (file)
index 0000000..194b6af
--- /dev/null
@@ -0,0 +1,96 @@
+
+// BUILD:  $CC foo.c -dynamiclib -Wl,-U,_gInitialisersCalled                                         -install_name $RUN_DIR/libfoo.dylib -o $BUILD_DIR/libfoo.dylib
+// BUILD:  $CC bar.c -dynamiclib -Wl,-U,_gInitialisersCalled $BUILD_DIR/libfoo.dylib -flat_namespace -install_name $RUN_DIR/libbar.dylib -o $BUILD_DIR/libbar.dylib
+// BUILD:  $CC main.c -DRUN_DIR="$RUN_DIR"                                                                                               -o $BUILD_DIR/dlopen-flat.exe
+
+// RUN:  DYLD_LIBRARY_PATH=$RUN_DIR ./dlopen-flat.exe
+
+#include <stdio.h>
+#include <dlfcn.h>
+
+int gInitialisersCalled = 0;
+
+int main() {
+       printf("[BEGIN] dlopen-flat\n");
+       int result;
+
+       // Foo exports foo()
+       void* fooHandle = 0;
+       {
+               const char* path = RUN_DIR "/libfoo.dylib";
+               fooHandle = dlopen(path, RTLD_LAZY);
+               if (!fooHandle) {
+                       printf("dlopen failed with error: %s\n", dlerror());
+                       return 1;
+               }
+               if (gInitialisersCalled != 1) {
+               printf("gInitialisersCalled != 1\n");
+               printf("[FAIL] dlopen-flat\n");
+                       return 1;
+               }
+       }
+       // Now unload foo which should do something.
+       result = dlclose(fooHandle);
+       if (result != 0) {
+        printf("dlclose() returned %c\n", result);
+        printf("[FAIL] dlopen-flat\n");
+               return 1;
+       }
+
+       // Open foo again which should do something.
+       {
+               const char* path = RUN_DIR "/libfoo.dylib";
+               fooHandle = dlopen(path, RTLD_LAZY);
+               if (!fooHandle) {
+                       printf("dlopen failed with error: %s\n", dlerror());
+                       return 1;
+               }
+               if (gInitialisersCalled != 2) {
+               printf("gInitialisersCalled != 2\n");
+               printf("[FAIL] dlopen-flat\n");
+                       return 1;
+               }
+       }
+
+       // Bar is going to resolve foo()
+       void* barHandle = 0;
+       {
+               const char* path = RUN_DIR "/libbar.dylib";
+               barHandle = dlopen(path, RTLD_LAZY);
+               if (!barHandle) {
+                       printf("dlopen failed with error: %s\n", dlerror());
+                       return 1;
+               }
+               if (gInitialisersCalled != 3) {
+               printf("gInitialisersCalled != 3\n");
+               printf("[FAIL] dlopen-flat\n");
+                       return 1;
+               }
+       }
+       // Now unload foo which shouldn't do anything.
+       result = dlclose(fooHandle);
+       if (result != 0) {
+        printf("dlclose() returned %c\n", result);
+        printf("[FAIL] dlopen-flat\n");
+               return 1;
+       }
+
+       // Open foo again which shouldn't do anything.
+       {
+               const char* path = RUN_DIR "/libfoo.dylib";
+               fooHandle = dlopen(path, RTLD_LAZY);
+               if (!fooHandle) {
+                       printf("dlopen failed with error: %s\n", dlerror());
+                       return 1;
+               }
+               if (gInitialisersCalled != 3) {
+               printf("gInitialisersCalled != 3\n");
+               printf("[FAIL] dlopen-flat\n");
+                       return 1;
+               }
+       }
+
+    printf("[PASS] dlopen-flat\n");
+       return 0;
+}
+
diff --git a/testing/test-cases/dlopen-indirect-groupNum.dtest/bar.c b/testing/test-cases/dlopen-indirect-groupNum.dtest/bar.c
new file mode 100644 (file)
index 0000000..4381650
--- /dev/null
@@ -0,0 +1,7 @@
+
+#include <stdio.h>
+#include <string.h>
+
+void barInDylib()
+{
+}
diff --git a/testing/test-cases/dlopen-indirect-groupNum.dtest/baz.c b/testing/test-cases/dlopen-indirect-groupNum.dtest/baz.c
new file mode 100644 (file)
index 0000000..98ea4f1
--- /dev/null
@@ -0,0 +1,10 @@
+
+#include <stdio.h>
+#include <string.h>
+
+extern void barInDylib();
+
+void bazInDylib()
+{
+       return barInDylib();
+}
diff --git a/testing/test-cases/dlopen-indirect-groupNum.dtest/foo.c b/testing/test-cases/dlopen-indirect-groupNum.dtest/foo.c
new file mode 100644 (file)
index 0000000..837ec00
--- /dev/null
@@ -0,0 +1,7 @@
+
+#include <stdio.h>
+#include <string.h>
+
+void fooInBundle()
+{
+}
diff --git a/testing/test-cases/dlopen-indirect-groupNum.dtest/main.c b/testing/test-cases/dlopen-indirect-groupNum.dtest/main.c
new file mode 100644 (file)
index 0000000..4547e90
--- /dev/null
@@ -0,0 +1,147 @@
+// BUILD_ONLY: MacOSX
+
+// BUILD:  $CC main.c  -o $BUILD_DIR/dlopen-indirect-groupNum.exe -Wno-deprecated-declarations
+// BUILD:  $CC foo.c   -o $BUILD_DIR/foo.bundle -bundle
+// BUILD:  $CC bar.c -dynamiclib  -install_name $RUN_DIR/libbar.dylib -o $BUILD_DIR/libbar.dylib
+// BUILD:  $CC baz.c -dynamiclib  -install_name $RUN_DIR/libbaz.dylib -o $BUILD_DIR/libbaz.dylib $BUILD_DIR/libbar.dylib
+
+// RUN:  ./dlopen-indirect-groupNum.exe $RUN_DIR/foo.bundle $RUN_DIR/libbar.dylib $RUN_DIR/libbaz.dylib
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h> 
+#include <sys/mman.h> 
+#include <unistd.h>
+#include <fcntl.h>
+#include <dlfcn.h>
+#include <mach-o/dyld.h>
+
+
+static void checkBundle(const char* path, bool unlinkBeforeDestroy)
+{
+       int fd = open(path, O_RDONLY, 0);
+       if ( fd == -1 ) {
+               printf("[FAIL] open(%s) failed", path);
+               exit(0);
+       }
+
+       struct stat stat_buf;
+       if ( fstat(fd, &stat_buf) == -1) {
+               printf("[FAIL] fstat() failed\n");
+               exit(0);
+       }
+
+       void* loadAddress = mmap(NULL, stat_buf.st_size, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0);
+       if ( loadAddress == ((void*)(-1)) ) {
+               printf("[FAIL] mmap() failed\n");
+               exit(0);
+       }
+
+       close(fd);
+
+       NSObjectFileImage ofi;
+       if ( NSCreateObjectFileImageFromMemory(loadAddress, stat_buf.st_size, &ofi) != NSObjectFileImageSuccess ) {
+               printf("[FAIL] NSCreateObjectFileImageFromMemory failed\n");
+               exit(0);
+       }
+
+       NSModule mod = NSLinkModule(ofi, path, NSLINKMODULE_OPTION_NONE);
+       if ( mod == NULL ) {
+               printf("[FAIL] NSLinkModule failed\n");
+               exit(0);
+       }
+       
+   if ( !unlinkBeforeDestroy ) {
+        // API lets you destroy ofi and NSModule lives on
+        if ( !NSDestroyObjectFileImage(ofi) ) {
+            printf("[FAIL] NSDestroyObjectFileImage failed\n");
+            exit(0);
+        }
+    }
+
+       NSSymbol sym = NSLookupSymbolInModule(mod, "_fooInBundle");
+       if ( sym == NULL ) {
+               printf("[FAIL] NSLookupSymbolInModule failed\n");
+               exit(0);
+       }
+
+       void* func = NSAddressOfSymbol(sym);
+       if ( func == NULL ) {
+               printf("[FAIL] NSAddressOfSymbol failed\n");
+               exit(0);
+       }
+
+    Dl_info info;
+    if ( dladdr(func, &info) == 0 ) {
+        printf("[FAIL] dladdr(&p, xx) failed\n");
+        exit(0);
+    }
+    //printf("_fooInBundle found in %s\n", info.dli_fname);
+
+    if ( !NSUnLinkModule(mod, NSUNLINKMODULE_OPTION_NONE) ) {
+            printf("[FAIL] NSUnLinkModule failed\n");
+            exit(0);
+    }
+
+    if ( dladdr(func, &info) != 0 ) {
+        printf("[FAIL] dladdr(&p, xx) found but should not have\n");
+        exit(0);
+    }
+
+    if ( unlinkBeforeDestroy ) {
+        if ( !NSDestroyObjectFileImage(ofi) ) {
+            printf("[FAIL] NSDestroyObjectFileImage failed\n");
+            exit(0);
+        }
+    }
+}
+
+
+
+static void tryImage(const char* path, const char* symbol)
+{
+    void* handle = dlopen(path, RTLD_LAZY);
+    if ( handle == NULL ) {
+        printf("dlerror(): %s\n", dlerror());
+        printf("[FAIL] dlopen-indirect-groupNum %s\n", path);
+        exit(0);
+    }
+    
+    void* sym = dlsym(handle, symbol);
+    if ( sym == NULL ) {
+        printf("dlerror(): %s\n", dlerror());
+        printf("[FAIL] dlopen-indirect-groupNum %s\n", path);
+        exit(0);
+    }
+    
+    int result = dlclose(handle);
+    if ( result != 0 ) {
+        printf("dlclose() returned %c\n", result);
+        printf("[FAIL] dlopen-indirect-groupNum %s\n", path);
+        exit(0);
+    }
+}
+
+
+int main(int argc, const char* argv[])
+{
+    printf("[BEGIN] dlopen-indirect-groupNum\n");
+
+    checkBundle(argv[1], true);
+    checkBundle(argv[1], false);
+
+    // Now go again enough times to flush out any limits in our dlopen encodings.
+    for (unsigned i = 0; i != 255; ++i)
+        checkBundle(argv[1], false);
+
+    // Open bar.dylib
+    tryImage(argv[2], "barInDylib");
+
+    // And now open baz.dylib which depends on bar.dylib
+    tryImage(argv[3], "bazInDylib");
+
+    printf("[PASS] dlopen-indirect-groupNum\n");
+    return 0;
+}
\ No newline at end of file
diff --git a/testing/test-cases/dlopen-intertwined.dtest/A.c b/testing/test-cases/dlopen-intertwined.dtest/A.c
new file mode 100644 (file)
index 0000000..2fadfc3
--- /dev/null
@@ -0,0 +1,11 @@
+#include <string.h>
+#include <stdio.h>
+
+extern void setState(const char* from);
+
+
+void a(const char* from) {
+    char buffer[100];
+    sprintf(buffer, "a() from %s", from);
+    setState(buffer);
+}
diff --git a/testing/test-cases/dlopen-intertwined.dtest/B.c b/testing/test-cases/dlopen-intertwined.dtest/B.c
new file mode 100644 (file)
index 0000000..aed2a47
--- /dev/null
@@ -0,0 +1,11 @@
+extern void c(const char*);
+
+void b() {  }
+
+void __attribute__((constructor))
+initB()
+{
+    c("initB");
+}
+
+
diff --git a/testing/test-cases/dlopen-intertwined.dtest/C.c b/testing/test-cases/dlopen-intertwined.dtest/C.c
new file mode 100644 (file)
index 0000000..338ddb5
--- /dev/null
@@ -0,0 +1,16 @@
+#include <string.h>
+#include <stdio.h>
+
+extern void setState(const char* from);
+
+void c(const char* from) {
+    char buffer[100];
+    sprintf(buffer, "c() from %s", from);
+    setState(buffer);
+}
+
+void __attribute__((constructor))
+initC()
+{
+    setState("initC");
+}
diff --git a/testing/test-cases/dlopen-intertwined.dtest/D.c b/testing/test-cases/dlopen-intertwined.dtest/D.c
new file mode 100644 (file)
index 0000000..4d9da2c
--- /dev/null
@@ -0,0 +1,20 @@
+#include <string.h>
+#include <stdio.h>
+
+extern void setState(const char* from);
+extern void c(const char* from);
+
+void d(const char* from) {
+    char buffer[100];
+    sprintf(buffer, "d() from %s", from);
+    setState(buffer);
+}
+
+void __attribute__((constructor))
+initD()
+{
+    c("initD");
+}
+
+
+
diff --git a/testing/test-cases/dlopen-intertwined.dtest/E.c b/testing/test-cases/dlopen-intertwined.dtest/E.c
new file mode 100644 (file)
index 0000000..2700f3c
--- /dev/null
@@ -0,0 +1,13 @@
+extern void a(const char*);
+
+void e() { }
+
+
+void __attribute__((constructor))
+initE()
+{
+    a("initE");
+}
+
+
+
diff --git a/testing/test-cases/dlopen-intertwined.dtest/F.c b/testing/test-cases/dlopen-intertwined.dtest/F.c
new file mode 100644 (file)
index 0000000..88789cf
--- /dev/null
@@ -0,0 +1,12 @@
+extern void d(const char*);
+
+void f() {  }
+
+void __attribute__((constructor))
+initF()
+{
+    d("initF");
+}
+
+
+
diff --git a/testing/test-cases/dlopen-intertwined.dtest/base.c b/testing/test-cases/dlopen-intertwined.dtest/base.c
new file mode 100644 (file)
index 0000000..e3143fb
--- /dev/null
@@ -0,0 +1,26 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static const char* expectedStrings[] = {
+    "a() from main",
+    "initC",
+    "c() from initB",
+    "c() from initD",
+    "a() from initE",
+    "d() from initF",
+    "DONE"
+};
+
+static const char** curState = expectedStrings;
+
+void setState(const char* from)
+{
+    printf("%s\n", from);
+    if ( strcmp(*curState, from) != 0 ) {
+        printf("[FAIL] dlopen-intertwined: expected %s\n", *curState);
+        exit(0);
+    }
+    ++curState;
+}
+
diff --git a/testing/test-cases/dlopen-intertwined.dtest/main.c b/testing/test-cases/dlopen-intertwined.dtest/main.c
new file mode 100644 (file)
index 0000000..11580d3
--- /dev/null
@@ -0,0 +1,63 @@
+
+
+// BUILD:  $CC base.c -dynamiclib -o $BUILD_DIR/libbase.dylib -install_name $RUN_DIR/libbase.dylib
+// BUILD:  $CC A.c -dynamiclib -o $BUILD_DIR/libA.dylib -install_name $RUN_DIR/libA.dylib $BUILD_DIR/libbase.dylib
+// BUILD:  $CC C.c -dynamiclib -o $BUILD_DIR/libC.dylib -install_name $RUN_DIR/libC.dylib $BUILD_DIR/libbase.dylib
+// BUILD:  $CC B.c -dynamiclib -o $BUILD_DIR/libB.dylib -install_name $RUN_DIR/libB.dylib $BUILD_DIR/libbase.dylib $BUILD_DIR/libC.dylib
+// BUILD:  $CC D.c -dynamiclib -o $BUILD_DIR/libD.dylib -install_name $RUN_DIR/libD.dylib $BUILD_DIR/libbase.dylib $BUILD_DIR/libC.dylib
+// BUILD:  $CC E.c -dynamiclib -o $BUILD_DIR/libE.dylib -install_name $RUN_DIR/libE.dylib $BUILD_DIR/libbase.dylib $BUILD_DIR/libA.dylib
+// BUILD:  $CC F.c -dynamiclib -o $BUILD_DIR/libF.dylib -install_name $RUN_DIR/libF.dylib $BUILD_DIR/libbase.dylib $BUILD_DIR/libD.dylib
+// BUILD:  $CC main.c -o $BUILD_DIR/dlopen-intertwined.exe $BUILD_DIR/libbase.dylib $BUILD_DIR/libA.dylib -DRUN_DIR="$RUN_DIR"
+
+// RUN:  ./dlopen-intertwined.exe
+
+#include <stdio.h>
+#include <dlfcn.h>
+#include <stdlib.h>
+#include <dispatch/dispatch.h>
+
+// main deps on A
+// main dlopens B which deps on C
+// main dlopens D which deps on C
+// main dlopens E which deps on A
+// main dlopens F which deps on D
+
+extern void a(const char*);
+extern void setState(const char* from);
+
+int main()
+{
+    printf("[BEGIN] dlopen-intertwined\n");
+
+    a("main");
+    
+    void* handle = dlopen(RUN_DIR "/libB.dylib", RTLD_LAZY);
+    if ( handle == NULL ) {
+        printf("[FAIL] dlopen-intertwined: %s\n", dlerror());
+        exit(0);
+    }
+
+    handle = dlopen(RUN_DIR "/libD.dylib", RTLD_LAZY);
+    if ( handle == NULL ) {
+        printf("[FAIL] dlopen-intertwined: %s\n", dlerror());
+        exit(0);
+    }
+
+    handle = dlopen(RUN_DIR "/libE.dylib", RTLD_LAZY);
+    if ( handle == NULL ) {
+        printf("[FAIL] dlopen-intertwined: %s\n", dlerror());
+        exit(0);
+    }
+
+    handle = dlopen(RUN_DIR "/libF.dylib", RTLD_LAZY);
+    if ( handle == NULL ) {
+        printf("[FAIL] dlopen-intertwined: %s\n", dlerror());
+        exit(0);
+    }
+
+    setState("DONE");
+
+    printf("[PASS] dlopen-intertwined\n");
+       return 0;
+}
+
diff --git a/testing/test-cases/dlopen-long-error-message.dtest/main.c b/testing/test-cases/dlopen-long-error-message.dtest/main.c
new file mode 100644 (file)
index 0000000..8b0a16a
--- /dev/null
@@ -0,0 +1,30 @@
+
+// BUILD:  $CC main.c  -o $BUILD_DIR/dlopen-long-error-message.exe
+
+// RUN:  ./dlopen-long-error-message.exe
+
+#include <stdio.h>
+#include <dlfcn.h>
+#include <string.h>
+#include <stdlib.h>
+
+
+
+int main()
+{
+    printf("[BEGIN] dlopen-long-error-message\n");
+
+    for (int i=0; i < 10; ++i) {
+        void* handle = dlopen("/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/libbogus.dylib", RTLD_FIRST);
+        if ( handle != NULL ) {
+            printf("[FAIL] dlopen-long-error-message should have failed on non-existent file\n");
+            return 0;
+        }
+        free(strdup("hello there"));
+    }
+
+    printf("[PASS] dlopen-long-error-message\n");
+
+       return 0;
+}
+
diff --git a/testing/test-cases/dlopen-race.dtest/foo.c b/testing/test-cases/dlopen-race.dtest/foo.c
new file mode 100644 (file)
index 0000000..c8e9924
--- /dev/null
@@ -0,0 +1,5 @@
+int foo()
+{
+       return 10;
+}
+
diff --git a/testing/test-cases/dlopen-race.dtest/main.c b/testing/test-cases/dlopen-race.dtest/main.c
new file mode 100644 (file)
index 0000000..bad9ff7
--- /dev/null
@@ -0,0 +1,33 @@
+
+// BUILD:  $CC foo.c -dynamiclib  -install_name $RUN_DIR/libfoo.dylib -o $BUILD_DIR/libfoo.dylib
+// BUILD:  $CC main.c -o $BUILD_DIR/dlopen-race.exe -DRUN_DIR="$RUN_DIR"
+
+// RUN:  ./dlopen-race.exe
+
+#include <stdio.h>
+#include <dlfcn.h>
+#include <stdlib.h>
+#include <dispatch/dispatch.h>
+
+
+
+int main()
+{
+    printf("[BEGIN] dlopen-read\n");
+
+    __block bool allGood = true;
+    dispatch_apply(6, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(size_t index) {
+        for (int i=0; i < 500; ++i) {
+            void* handle = dlopen(RUN_DIR "/libfoo.dylib", RTLD_LAZY);
+            if ( handle == NULL ) {
+                printf("[FAIL] dlopen-read: %s\n", dlerror());
+                exit(0);
+            }
+            dlclose(handle);
+        }
+    });
+
+    printf("[PASS] dlopen-read\n");
+       return 0;
+}
+
diff --git a/testing/test-cases/dlopen-recurse.dtest/bar.c b/testing/test-cases/dlopen-recurse.dtest/bar.c
new file mode 100644 (file)
index 0000000..c86856e
--- /dev/null
@@ -0,0 +1,5 @@
+int bar()
+{
+       return 0;
+}
+
diff --git a/testing/test-cases/dlopen-recurse.dtest/foo.c b/testing/test-cases/dlopen-recurse.dtest/foo.c
new file mode 100644 (file)
index 0000000..eb1114d
--- /dev/null
@@ -0,0 +1,16 @@
+#include <dlfcn.h>
+
+
+void myinit() __attribute__((constructor));
+
+void myinit()
+{
+    // call dlopen() in initializer
+    void* handle = dlopen(RUN_DIR "/libbar.dylib", RTLD_LAZY);
+}
+
+int foo()
+{
+       return 0;
+}
+
diff --git a/testing/test-cases/dlopen-recurse.dtest/main.c b/testing/test-cases/dlopen-recurse.dtest/main.c
new file mode 100644 (file)
index 0000000..53c244e
--- /dev/null
@@ -0,0 +1,26 @@
+
+// BUILD:  $CC bar.c -dynamiclib  -install_name $RUN_DIR/libbar.dylib -o $BUILD_DIR/libbar.dylib
+// BUILD:  $CC foo.c -dynamiclib  -install_name $RUN_DIR/libfoo.dylib -o $BUILD_DIR/libfoo.dylib -DRUN_DIR="$RUN_DIR"
+// BUILD:  $CC main.c -o $BUILD_DIR/dlopen-recurse.exe -DRUN_DIR="$RUN_DIR"
+
+// RUN:  ./dlopen-recurse.exe
+
+#include <stdio.h>
+#include <dlfcn.h>
+#include <stdlib.h>
+#include <dispatch/dispatch.h>
+
+
+
+int main()
+{
+    printf("[BEGIN] dlopen-recurse\n");
+
+    // libfoo's initializer calls dlopen().  If that hangs, we have a locking bug
+    void* handle = dlopen(RUN_DIR "/libfoo.dylib", RTLD_LAZY);
+    dlclose(handle);
+
+    printf("[PASS] dlopen-recurse\n");
+       return 0;
+}
+
diff --git a/testing/test-cases/dlsym-RTLD_DEFAULT.dtest/foo.c b/testing/test-cases/dlsym-RTLD_DEFAULT.dtest/foo.c
new file mode 100644 (file)
index 0000000..1f6bcd8
--- /dev/null
@@ -0,0 +1,6 @@
+
+void foo() { }
+
+#if DYN
+void foo2() {}
+#endif
diff --git a/testing/test-cases/dlsym-RTLD_DEFAULT.dtest/main.c b/testing/test-cases/dlsym-RTLD_DEFAULT.dtest/main.c
new file mode 100644 (file)
index 0000000..de667dd
--- /dev/null
@@ -0,0 +1,85 @@
+
+// BUILD:  $CC foo.c -dynamiclib  -install_name $RUN_DIR/libfoo-static.dylib  -o $BUILD_DIR/libfoo-static.dylib
+// BUILD:  $CC foo.c -dynamiclib  -install_name $RUN_DIR/libfoo-dynamic.dylib -o $BUILD_DIR/libfoo-dynamic.dylib -DDYN
+// BUILD:  $CC main.c $BUILD_DIR/libfoo-static.dylib -o $BUILD_DIR/dlsym-RTLD_DEFAULT.exe -DRUN_DIR="$RUN_DIR"
+
+// RUN:  ./dlsym-RTLD_DEFAULT.exe
+
+#include <stdio.h>
+#include <dlfcn.h>
+#include <mach-o/dyld_priv.h>
+
+
+// verify RTLD_DEFAULT search order
+
+int mainSymbol = 4;
+
+
+// my local implemention of free
+void free(void* p) { }
+
+
+static bool symbolInImage(const char* symName, const char* image)
+{
+    void* sym = dlsym(RTLD_DEFAULT, symName);
+    if ( sym == NULL )
+        return false;
+    const char* imagePath = dyld_image_path_containing_address(sym);
+    if ( imagePath == NULL )
+        return false;
+    return (strstr(imagePath, image) != NULL);
+}
+
+
+
+
+int main()
+{
+    printf("[BEGIN] dlsym-RTLD_DEFAULT\n");
+
+    // verify mainSymbol is found in main executable
+    if ( !symbolInImage("mainSymbol", "dlsym-RTLD_DEFAULT") ) {
+        printf("[FAIL]  dlsym-RTLD_DEFAULT: mainSymbol\n");
+        return 0;
+    }
+
+    // verify free is found in main executable, overrideing one in OS
+    if ( !symbolInImage("free", "dlsym-RTLD_DEFAULT") ) {
+        printf("[FAIL]  dlsym-RTLD_DEFAULT: free\n");
+        return 0;
+    }
+
+    // verify foo is found in libfoo-static.dylib
+    if ( !symbolInImage("foo", "libfoo-static.dylib") ) {
+        printf("[FAIL]  dlsym-RTLD_DEFAULT: foo not in libfoo-static.dylib\n");
+        return 0;
+    }
+
+    void* handle = dlopen(RUN_DIR "/libfoo-dynamic.dylib", RTLD_LAZY);
+    if ( handle == NULL ) {
+        printf("[FAIL]  dlsym-RTLD_DEFAULT: libfoo-dynamic.dylib could not be loaded\n");
+        return 0;
+    }
+
+    // verify foo is still found in statically linked lib
+    if ( !symbolInImage("foo", "libfoo-static.dylib") ) {
+        printf("[FAIL]  dlsym-RTLD_DEFAULT: foo not in libfoo-static.dylib\n");
+        return 0;
+    }
+
+    // verify foo2 is found in libfoo-dynamic.dylib"
+    if ( !symbolInImage("foo2", "libfoo-dynamic.dylib") ) {
+        printf("[FAIL]  dlsym-RTLD_DEFAULT: foo2 not in libfoo-dynamic.dylib\n");
+        return 0;
+    }
+
+    // renamed and re-exported symbols work
+    if ( dlsym(RTLD_DEFAULT, "strcmp") == NULL ) {
+        printf("[FAIL]  dlsym-RTLD_DEFAULT: strcmp not found\n");
+        return 0;
+    }
+    
+    printf("[PASS]  dlsym-RTLD_DEFAULT\n");
+       return 0;
+}
+
diff --git a/testing/test-cases/dlsym-RTLD_MAIN_ONLY.dtest/foo.c b/testing/test-cases/dlsym-RTLD_MAIN_ONLY.dtest/foo.c
new file mode 100644 (file)
index 0000000..1f6bcd8
--- /dev/null
@@ -0,0 +1,6 @@
+
+void foo() { }
+
+#if DYN
+void foo2() {}
+#endif
diff --git a/testing/test-cases/dlsym-RTLD_MAIN_ONLY.dtest/main.c b/testing/test-cases/dlsym-RTLD_MAIN_ONLY.dtest/main.c
new file mode 100644 (file)
index 0000000..80c9ef5
--- /dev/null
@@ -0,0 +1,79 @@
+
+// BUILD:  $CC foo.c -dynamiclib  -install_name $RUN_DIR/libfoo-static.dylib  -o $BUILD_DIR/libfoo-static.dylib
+// BUILD:  $CC foo.c -dynamiclib  -install_name $RUN_DIR/libfoo-dynamic.dylib -o $BUILD_DIR/libfoo-dynamic.dylib -DDYN
+// BUILD:  $CC main.c $BUILD_DIR/libfoo-static.dylib -o $BUILD_DIR/dlsym-RTLD_MAIN_ONLY.exe -DRUN_DIR="$RUN_DIR"
+
+// RUN:  ./dlsym-RTLD_MAIN_ONLY.exe
+
+#include <stdio.h>
+#include <dlfcn.h>
+#include <mach-o/dyld_priv.h>
+
+
+// verify RTLD_MAIN_ONLY search order
+
+int mainSymbol = 4;
+
+
+// my local implemention of free
+void free(void* p) { }
+
+
+static bool symbolInImage(const char* symName, const char* image)
+{
+    void* sym = dlsym(RTLD_MAIN_ONLY, symName);
+    if ( sym == NULL )
+        return false;
+    const char* imagePath = dyld_image_path_containing_address(sym);
+    if ( imagePath == NULL )
+        return false;
+    return (strstr(imagePath, image) != NULL);
+}
+
+
+
+
+int main()
+{
+    printf("[BEGIN] dlsym-RTLD_MAIN_ONLY\n");
+
+    // verify mainSymbol is found
+    if ( !symbolInImage("mainSymbol", "dlsym-RTLD_MAIN_ONLY") ) {
+        printf("[FAIL]  dlsym-RTLD_MAIN_ONLY: mainSymbol should have been found\n");
+        return 0;
+    }
+
+    // verify free is found in this program - not in OS
+    if ( !symbolInImage("free", "dlsym-RTLD_MAIN_ONLY") ) {
+        printf("[FAIL]  dlsym-RTLD_MAIN_ONLY: free\n");
+        return 0;
+    }
+
+    // verify foo is not found
+    if ( dlsym(RTLD_MAIN_ONLY, "foo") != NULL ) {
+        printf("[FAIL]  dlsym-RTLD_MAIN_ONLY: foo should not have been found\n");
+        return 0;
+    }
+
+    void* handle = dlopen(RUN_DIR "/libfoo-dynamic.dylib", RTLD_LAZY);
+    if ( handle == NULL ) {
+        printf("[FAIL]  dlsym-RTLD_MAIN_ONLY: libfoo-dynamic.dylib could not be loaded\n");
+        return 0;
+    }
+
+    // verify foo is still not found
+    if ( dlsym(RTLD_MAIN_ONLY, "foo") != NULL ) {
+        printf("[FAIL]  dlsym-RTLD_MAIN_ONLY: foo should not have been found after dlopen\n");
+        return 0;
+    }
+
+    // verify foo2 is not found in libfoo-dynamic.dylib", because RTLD_MAIN_ONLY only searches main executable
+    if ( dlsym(RTLD_MAIN_ONLY, "foo2") != NULL ) {
+        printf("[FAIL]  dlsym-RTLD_MAIN_ONLY: foo2 found but should not have been\n");
+        return 0;
+    }
+
+    printf("[PASS]  dlsym-RTLD_MAIN_ONLY\n");
+       return 0;
+}
+
diff --git a/testing/test-cases/dlsym-RTLD_NEXT.dtest/foo.c b/testing/test-cases/dlsym-RTLD_NEXT.dtest/foo.c
new file mode 100644 (file)
index 0000000..1f6bcd8
--- /dev/null
@@ -0,0 +1,6 @@
+
+void foo() { }
+
+#if DYN
+void foo2() {}
+#endif
diff --git a/testing/test-cases/dlsym-RTLD_NEXT.dtest/main.c b/testing/test-cases/dlsym-RTLD_NEXT.dtest/main.c
new file mode 100644 (file)
index 0000000..7d81e99
--- /dev/null
@@ -0,0 +1,79 @@
+
+// BUILD:  $CC foo.c -dynamiclib  -install_name $RUN_DIR/libfoo-static.dylib  -o $BUILD_DIR/libfoo-static.dylib
+// BUILD:  $CC foo.c -dynamiclib  -install_name $RUN_DIR/libfoo-dynamic.dylib -o $BUILD_DIR/libfoo-dynamic.dylib -DDYN
+// BUILD:  $CC main.c $BUILD_DIR/libfoo-static.dylib -o $BUILD_DIR/dlsym-RTLD_NEXT.exe -DRUN_DIR="$RUN_DIR"
+
+// RUN:  ./dlsym-RTLD_NEXT.exe
+
+#include <stdio.h>
+#include <dlfcn.h>
+#include <mach-o/dyld_priv.h>
+
+
+// verify RTLD_NEXT search order
+
+int mainSymbol = 4;
+
+
+// my local implemention of free
+void free(void* p) { }
+
+
+static bool symbolInImage(const char* symName, const char* image)
+{
+    void* sym = dlsym(RTLD_NEXT, symName);
+    if ( sym == NULL )
+        return false;
+    const char* imagePath = dyld_image_path_containing_address(sym);
+    if ( imagePath == NULL )
+        return false;
+    return (strstr(imagePath, image) != NULL);
+}
+
+
+
+
+int main()
+{
+    printf("[BEGIN] dlsym-RTLD_NEXT\n");
+
+    // verify mainSymbol is not found
+    if ( dlsym(RTLD_NEXT, "mainSymbol") != NULL ) {
+        printf("[FAIL]  dlsym-RTLD_NEXT: mainSymbol should not have been found\n");
+        return 0;
+    }
+
+    // verify free is found in OS (not local one)
+    if ( !symbolInImage("free", "/usr/lib/") ) {
+        printf("[FAIL]  dlsym-RTLD_NEXT: free\n");
+        return 0;
+    }
+
+    // verify foo is found in libfoo-static.dylib
+    if ( !symbolInImage("foo", "libfoo-static.dylib") ) {
+        printf("[FAIL]  dlsym-RTLD_NEXT: foo not in libfoo-static.dylib\n");
+        return 0;
+    }
+
+    void* handle = dlopen(RUN_DIR "/libfoo-dynamic.dylib", RTLD_LAZY);
+    if ( handle == NULL ) {
+        printf("[FAIL]  dlsym-RTLD_NEXT: libfoo-dynamic.dylib could not be loaded\n");
+        return 0;
+    }
+
+    // verify foo is still found in statically linked lib
+    if ( !symbolInImage("foo", "libfoo-static.dylib") ) {
+        printf("[FAIL]  dlsym-RTLD_NEXT: foo not in libfoo-static.dylib\n");
+        return 0;
+    }
+
+    // verify foo2 is not found in libfoo-dynamic.dylib", because RTLD_NEXT only searches thing this image would have seen
+    if ( symbolInImage("foo2", "libfoo-dynamic.dylib") ) {
+        printf("[FAIL]  dlsym-RTLD_NEXT: foo2 found but should not have been\n");
+        return 0;
+    }
+
+    printf("[PASS]  dlsym-RTLD_NEXT\n");
+       return 0;
+}
+
diff --git a/testing/test-cases/dlsym-RTLD_SELF.dtest/foo.c b/testing/test-cases/dlsym-RTLD_SELF.dtest/foo.c
new file mode 100644 (file)
index 0000000..1f6bcd8
--- /dev/null
@@ -0,0 +1,6 @@
+
+void foo() { }
+
+#if DYN
+void foo2() {}
+#endif
diff --git a/testing/test-cases/dlsym-RTLD_SELF.dtest/main.c b/testing/test-cases/dlsym-RTLD_SELF.dtest/main.c
new file mode 100644 (file)
index 0000000..cd921ff
--- /dev/null
@@ -0,0 +1,79 @@
+
+// BUILD:  $CC foo.c -dynamiclib  -install_name $RUN_DIR/libfoo-static.dylib  -o $BUILD_DIR/libfoo-static.dylib
+// BUILD:  $CC foo.c -dynamiclib  -install_name $RUN_DIR/libfoo-dynamic.dylib -o $BUILD_DIR/libfoo-dynamic.dylib -DDYN
+// BUILD:  $CC main.c $BUILD_DIR/libfoo-static.dylib -o $BUILD_DIR/dlsym-RTLD_SELF.exe -DRUN_DIR="$RUN_DIR"
+
+// RUN:  ./dlsym-RTLD_SELF.exe
+
+#include <stdio.h>
+#include <dlfcn.h>
+#include <mach-o/dyld_priv.h>
+
+
+// verify RTLD_SELF search order
+
+int mainSymbol = 4;
+
+
+// my local implemention of free
+void free(void* p) { }
+
+
+static bool symbolInImage(const char* symName, const char* image)
+{
+    void* sym = dlsym(RTLD_SELF, symName);
+    if ( sym == NULL )
+        return false;
+    const char* imagePath = dyld_image_path_containing_address(sym);
+    if ( imagePath == NULL )
+        return false;
+    return (strstr(imagePath, image) != NULL);
+}
+
+
+
+
+int main()
+{
+    printf("[BEGIN] dlsym-RTLD_SELF\n");
+
+    // verify mainSymbol is found
+    if ( dlsym(RTLD_SELF, "mainSymbol") == NULL ) {
+        printf("[FAIL]  dlsym-RTLD_SELF: mainSymbol should have been found\n");
+        return 0;
+    }
+
+    // verify free is found in this program - not in OS
+    if ( !symbolInImage("free", "dlsym-RTLD_SELF") ) {
+        printf("[FAIL]  dlsym-RTLD_SELF: free\n");
+        return 0;
+    }
+
+    // verify foo is found in libfoo-static.dylib
+    if ( !symbolInImage("foo", "libfoo-static.dylib") ) {
+        printf("[FAIL]  dlsym-RTLD_SELF: foo not in libfoo-static.dylib\n");
+        return 0;
+    }
+
+    void* handle = dlopen(RUN_DIR "/libfoo-dynamic.dylib", RTLD_LAZY);
+    if ( handle == NULL ) {
+        printf("[FAIL]  dlsym-RTLD_SELF: libfoo-dynamic.dylib could not be loaded\n");
+        return 0;
+    }
+
+    // verify foo is still found in statically linked lib
+    if ( !symbolInImage("foo", "libfoo-static.dylib") ) {
+        printf("[FAIL]  dlsym-RTLD_SELF: foo not in libfoo-static.dylib\n");
+        return 0;
+    }
+
+    // verify foo2 is not found in libfoo-dynamic.dylib", because RTLD_SELF only searches thing this image would have seen
+    if ( symbolInImage("foo2", "libfoo-dynamic.dylib") ) {
+        printf("[FAIL]  dlsym-RTLD_SELF: foo2 found but should not have been\n");
+        return 0;
+    }
+
+    printf("[PASS]  dlsym-RTLD_SELF\n");
+       return 0;
+}
+
diff --git a/testing/test-cases/dlsym-handle.dtest/bar.c b/testing/test-cases/dlsym-handle.dtest/bar.c
new file mode 100644 (file)
index 0000000..30ade8f
--- /dev/null
@@ -0,0 +1,2 @@
+
+void bar() { }
diff --git a/testing/test-cases/dlsym-handle.dtest/base.c b/testing/test-cases/dlsym-handle.dtest/base.c
new file mode 100644 (file)
index 0000000..e543e16
--- /dev/null
@@ -0,0 +1,3 @@
+
+void base() { }
+
diff --git a/testing/test-cases/dlsym-handle.dtest/foo.c b/testing/test-cases/dlsym-handle.dtest/foo.c
new file mode 100644 (file)
index 0000000..ab61e80
--- /dev/null
@@ -0,0 +1,2 @@
+
+void foo() { }
diff --git a/testing/test-cases/dlsym-handle.dtest/main.c b/testing/test-cases/dlsym-handle.dtest/main.c
new file mode 100644 (file)
index 0000000..52e7750
--- /dev/null
@@ -0,0 +1,99 @@
+
+// BUILD:  $CC base.c -dynamiclib  -install_name $RUN_DIR/libbase.dylib  -o $BUILD_DIR/libbase.dylib
+// BUILD:  $CC foo.c  -dynamiclib $BUILD_DIR/libbase.dylib -install_name $RUN_DIR/libfoo.dylib  -o $BUILD_DIR/libfoo.dylib
+// BUILD:  $CC bar.c  -dynamiclib $BUILD_DIR/libbase.dylib -install_name $RUN_DIR/libbar.dylib  -o $BUILD_DIR/libbar.dylib
+// BUILD:  $CC main.c -o $BUILD_DIR/dlsym-handle.exe -DRUN_DIR="$RUN_DIR"
+
+// RUN:  ./dlsym-handle.exe
+
+#include <stdio.h>
+#include <string.h>
+#include <dlfcn.h>
+#include <mach-o/dyld_priv.h>
+
+
+// verify RTLD_DEFAULT search order
+
+int mainSymbol = 4;
+
+int main()
+{
+    printf("[BEGIN] dlsym-handle\n");
+
+    void* fooHandle = dlopen(RUN_DIR "/libfoo.dylib", RTLD_LAZY);
+    if ( fooHandle == NULL ) {
+        printf("[FAIL]  dlsym-handle: libfoo.dylib could not be loaded, %s\n", dlerror());
+        return 0;
+    }
+
+    void* barHandle = dlopen(RUN_DIR "/libbar.dylib", RTLD_LAZY);
+    if ( barHandle == NULL ) {
+        printf("[FAIL]  dlsym-handle: libbar.dylib could not be loaded, %s\n", dlerror());
+        return 0;
+    }
+
+    // verify fooHandle does not find mainSymbol
+    if ( dlsym(fooHandle, "mainSymbol") != NULL ) {
+        printf("[FAIL]  dlsym-handle: mainSymbol was found with fooHandle\n");
+        return 0;
+    }
+
+    // verify fooHandle can find foo
+    if ( dlsym(fooHandle, "foo") == NULL ) {
+        printf("[FAIL]  dlsym-handle: foo not found with fooHandle\n");
+        return 0;
+    }
+
+    // verify fooHandle can find base
+    if ( dlsym(fooHandle, "base") == NULL ) {
+        printf("[FAIL]  dlsym-handle: base not found with fooHandle\n");
+        return 0;
+    }
+
+    // verify fooHandle cannot find bar
+    if ( dlsym(fooHandle, "bar") != NULL ) {
+        printf("[FAIL]  dlsym-handle: bar found with fooHandle\n");
+        return 0;
+    }
+
+    // verify barHandle can find bar
+    if ( dlsym(barHandle, "bar") == NULL ) {
+        printf("[FAIL]  dlsym-handle: bar not found with barHandle\n");
+        return 0;
+    }
+
+    // verify barHandle can find base
+    if ( dlsym(barHandle, "base") == NULL ) {
+        printf("[FAIL]  dlsym-handle: base not found with barHandle\n");
+        return 0;
+    }
+
+    // verify barHandle cannot find foo
+    if ( dlsym(barHandle, "foo") != NULL ) {
+        printf("[FAIL]  dlsym-handle: foo found with barHandle\n");
+        return 0;
+    }
+
+    // verify renamed and re-exported symbols work
+    if ( dlsym(RTLD_DEFAULT, "strcmp") == NULL ) {
+        printf("[FAIL]  dlsym-handle: strcmp not found\n");
+        return 0;
+    }
+    
+    // verify bad handle errors
+    if ( dlsym((void*)0xdeadbeef, "malloc") != NULL ) {
+        printf("[FAIL]  dlsym-handle: malloc found with bad handle\n");
+        return 0;
+    }
+    else {
+        const char* message = dlerror();
+        if ( strstr(message, "invalid") == NULL ) {
+            printf("[FAIL]  dlsym-handle: invalid handle error message missing 'invalid'\n");
+            return 0;
+        }
+    }
+
+    printf("[PASS]  dlsym-handle\n");
+       return 0;
+}
+
diff --git a/testing/test-cases/dlsym-re-export.dtest/foo.c b/testing/test-cases/dlsym-re-export.dtest/foo.c
new file mode 100644 (file)
index 0000000..c8e9924
--- /dev/null
@@ -0,0 +1,5 @@
+int foo()
+{
+       return 10;
+}
+
diff --git a/testing/test-cases/dlsym-re-export.dtest/main.c b/testing/test-cases/dlsym-re-export.dtest/main.c
new file mode 100644 (file)
index 0000000..b1752d6
--- /dev/null
@@ -0,0 +1,43 @@
+
+// BUILD:  mkdir -p $BUILD_DIR/sub1 $BUILD_DIR/sub2
+// BUILD:  $CC sub1.c -dynamiclib -install_name @rpath/libsub1.dylib   -o $BUILD_DIR/sub1/libsub1.dylib
+// BUILD:  $CC sub2.c -dynamiclib -install_name @rpath/libsub2.dylib   -o $BUILD_DIR/sub2/libsub2.dylib
+// BUILD:  $CC foo.c  -dynamiclib -install_name $RUN_DIR/libfoo.dylib  -o $BUILD_DIR/libfoo.dylib -rpath @loader_path/sub1 -Wl,-reexport_library,$BUILD_DIR/sub1/libsub1.dylib -Wl,-reexport_library,$BUILD_DIR/sub2/libsub2.dylib
+// BUILD:  $CC main.c  -o $BUILD_DIR/dlsym-reexport.exe -DRUN_DIR="$RUN_DIR" -rpath @loader_path/sub2
+
+// RUN:  ./dlsym-reexport.exe
+
+// rpath for sub1 is found in libfoo.dylib.  rpath for sub2 is found in dlsym-reexport.exe
+
+#include <stdio.h>
+#include <dlfcn.h>
+
+int main()
+{
+    printf("[BEGIN] dlsym-re-export\n");
+    // RTLD_FIRST means dlsym() should only search libfoo.dylib (and any re-exports)
+       void* handle = dlopen(RUN_DIR "/libfoo.dylib", RTLD_FIRST);
+       if ( handle == NULL ) {
+        printf("dlerror(): %s\n", dlerror());
+        printf("[FAIL]  dlsym-re-export\n");
+               return 0;
+       }
+
+       void* sym1 = dlsym(handle, "sub1");
+       if ( sym1 == NULL ) {
+        printf("dlerror(): %s\n", dlerror());
+        printf("[FAIL]  dlsym-re-export\n");
+               return 0;
+       }
+
+       void* sym2 = dlsym(handle, "sub2");
+       if ( sym2 == NULL ) {
+        printf("dlerror(): %s\n", dlerror());
+        printf("[FAIL]  dlsym-re-export\n");
+               return 0;
+       }
+
+    printf("[PASS]  dlsym-re-export\n");
+       return 0;
+}
+
diff --git a/testing/test-cases/dlsym-re-export.dtest/sub1.c b/testing/test-cases/dlsym-re-export.dtest/sub1.c
new file mode 100644 (file)
index 0000000..98c93f6
--- /dev/null
@@ -0,0 +1,5 @@
+int sub1()
+{
+       return 1;
+}
+
diff --git a/testing/test-cases/dlsym-re-export.dtest/sub2.c b/testing/test-cases/dlsym-re-export.dtest/sub2.c
new file mode 100644 (file)
index 0000000..852b89b
--- /dev/null
@@ -0,0 +1,5 @@
+int sub2()
+{
+       return 2;
+}
+
diff --git a/testing/test-cases/dtrace.dtest/main.c b/testing/test-cases/dtrace.dtest/main.c
new file mode 100644 (file)
index 0000000..237dbd1
--- /dev/null
@@ -0,0 +1,27 @@
+// BUILD:  /usr/sbin/dtrace -h -s main.d -o $TEMP_DIR/probes.h
+// BUILD:  $CC main.c -I$TEMP_DIR -o $BUILD_DIR/dtrace.exe
+// BUILD:  $DYLD_ENV_VARS_ENABLE $BUILD_DIR/dtrace.exe
+
+// RUN:    $SUDO dtrace -l -n 'dyld_testing*:dtrace.exe:main:callback' -c ./dtrace.exe
+
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/sdt.h>
+
+#include "probes.h"
+
+int main()
+{
+    printf("[BEGIN] dtrace\n");
+
+    DYLD_TESTING_CALLBACK();
+
+    if (!DYLD_TESTING_CALLBACK_ENABLED())
+        printf("[FAIL] !DYLD_TESTING_CALLBACK_ENABLED\n");
+
+    printf("[PASS] dtrace\n");
+       return 0;
+}
diff --git a/testing/test-cases/dtrace.dtest/main.d b/testing/test-cases/dtrace.dtest/main.d
new file mode 100644 (file)
index 0000000..5918888
--- /dev/null
@@ -0,0 +1,3 @@
+provider dyld_testing {
+       probe callback();
+};
diff --git a/testing/test-cases/dyld_get_sdk_version.dtest/bad.txt b/testing/test-cases/dyld_get_sdk_version.dtest/bad.txt
new file mode 100644 (file)
index 0000000..43b3565
--- /dev/null
@@ -0,0 +1 @@
+bad file
diff --git a/testing/test-cases/dyld_get_sdk_version.dtest/main.c b/testing/test-cases/dyld_get_sdk_version.dtest/main.c
new file mode 100644 (file)
index 0000000..efa9be1
--- /dev/null
@@ -0,0 +1,33 @@
+
+// BUILD:  $CC main.c  -o $BUILD_DIR/sdk-check.exe
+
+// RUN:  ./sdk-check.exe
+
+#include <stdio.h>
+#include <string.h>
+#include <mach-o/dyld_priv.h>
+
+extern struct mach_header __dso_handle;
+
+int main()
+{
+    printf("[BEGIN] dyld_get_sdk_version\n");
+
+    // should succeed
+    if ( dyld_get_sdk_version(&__dso_handle) == 0 ) {
+        printf("[FAIL] dyld_get_sdk_version: expected SDK\n");
+        return 0;
+    }
+
+    // should fail
+    const char* text = "bad text";
+    if ( dyld_get_sdk_version((struct mach_header*)text) != 0 ) {
+        printf("[FAIL] dyld_get_sdk_version: expected failure\n");
+        return 0;
+    }
+
+    printf("[PASS] dyld_get_sdk_version\n");
+
+       return 0;
+}
+
diff --git a/testing/test-cases/dyld_image_path_containing_address.dtest/main.c b/testing/test-cases/dyld_image_path_containing_address.dtest/main.c
new file mode 100644 (file)
index 0000000..9064caf
--- /dev/null
@@ -0,0 +1,32 @@
+
+// BUILD:  $CC main.c            -o $BUILD_DIR/dyld_image_path_containing_address-test.exe
+
+// RUN:  ./dyld_image_path_containing_address-test.exe
+
+
+#include <stdio.h>
+#include <string.h>
+#include <dlfcn.h>
+#include <mach-o/dyld.h>
+#include <mach-o/dyld_priv.h>
+
+
+int main()
+{
+    printf("[BEGIN] dyld_image_path_containing_address-test\n");
+
+    int count = _dyld_image_count();
+    for (int i=0; i < count; ++i) {
+        const struct mach_header* mh    = _dyld_get_image_header(i);
+        const char*               name1 = _dyld_get_image_name(i);
+        const char*               name2 = dyld_image_path_containing_address(mh);
+        if ( strcmp(name1, name2) != 0 ) {
+            printf("[FAIL] dyld_image_path_containing_address-test: %s != %s\n", name1, name2);
+            return 0;
+        }
+    }
+
+    printf("[PASS] dyld_image_path_containing_address-test\n");
+       return 0;
+}
+
index ffbe74e4952a180cb4f15977f33c07d687ada2a0..4f6e00a01d6a2054ae3348d5faf4a6b6dd86e4a6 100644 (file)
@@ -1,8 +1,10 @@
+#include <signal.h>
+#include <unistd.h>
 #include <mach/mach.h>
 
 int main()
 {
 #include <mach/mach.h>
 
 int main()
 {
-    task_suspend(mach_task_self());
+    (void)kill(getpid(), SIGSTOP);
     return 0;
 }
 
     return 0;
 }
 
index 9e5b41e28715105eb3cdeab65a235cbae7da0f1a..41c5ccbbb4ff1a0420389866d6eeeda04e99a6a5 100644 (file)
@@ -15,6 +15,7 @@
 #include <mach/mach.h>
 #include <mach/machine.h>
 #include <mach-o/dyld_process_info.h>
 #include <mach/mach.h>
 #include <mach/machine.h>
 #include <mach-o/dyld_process_info.h>
+#include <Availability.h>
 
 
 extern char** environ;
 
 
 extern char** environ;
@@ -29,9 +30,14 @@ extern char** environ;
     cpu_type_t otherArch[] = { CPU_TYPE_ARM64 };
 #endif
 
     cpu_type_t otherArch[] = { CPU_TYPE_ARM64 };
 #endif
 
-static task_t launchTest(const char* testProgPath, bool launchOtherArch, bool launchSuspended)
+struct task_and_pid {
+    pid_t pid;
+    task_t task;
+};
+
+static struct task_and_pid launchTest(const char* testProgPath, bool launchOtherArch, bool launchSuspended)
 {
 {
-    posix_spawnattr_t attr;
+    posix_spawnattr_t attr = 0;
     if ( posix_spawnattr_init(&attr) != 0 ) {
         printf("[FAIL] dyld_process_info posix_spawnattr_init()\n");
         exit(0);
     if ( posix_spawnattr_init(&attr) != 0 ) {
         printf("[FAIL] dyld_process_info posix_spawnattr_init()\n");
         exit(0);
@@ -50,31 +56,42 @@ static task_t launchTest(const char* testProgPath, bool launchOtherArch, bool la
         }
     }
 
         }
     }
 
-    pid_t childPid;
+    struct task_and_pid child = {0, 0};
     const char* argv[] = { testProgPath, NULL };
     const char* argv[] = { testProgPath, NULL };
-    int psResult = posix_spawn(&childPid, testProgPath, NULL, &attr, (char**)argv, environ);
+    int psResult = posix_spawn(&child.pid, 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);
     }
     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);
+    if (posix_spawnattr_destroy(&attr) != 0) {
+        printf("[FAIL] dyld_process_info posix_spawnattr_destroy()\n");
+        exit(0);
+    }
 
 
-    task_t childTask = 0;
-    if ( task_for_pid(mach_task_self(), childPid, &childTask) != KERN_SUCCESS ) {
+    if ( task_for_pid(mach_task_self(), child.pid, &child.task) != KERN_SUCCESS ) {
         printf("[FAIL] dyld_process_info task_for_pid()\n");
         printf("[FAIL] dyld_process_info task_for_pid()\n");
-        kill(childPid, SIGKILL);
+        kill(child.pid, SIGKILL);
         exit(0);
     }
 
         exit(0);
     }
 
+#if __x86_64__
+    //printf("child pid=%d task=%d (%s, %s)\n", child.pid, child.task, launchOtherArch ? "i386" : "x86_64",  launchSuspended ? "suspended" : "active");
+#endif
+
     // wait until process is up and has suspended itself
     struct task_basic_info info;
     do {
         unsigned count = TASK_BASIC_INFO_COUNT;
     // 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);
+        kern_return_t kr = task_info(child.task, TASK_BASIC_INFO, (task_info_t)&info, &count);
         sleep(1);
     } while ( info.suspend_count == 0 );
 
         sleep(1);
     } while ( info.suspend_count == 0 );
 
-    return childTask;
+    return child;
+}
+
+static void killTest(struct task_and_pid tp) {
+    int r = kill(tp.pid, SIGKILL);
+    waitpid(tp.pid, &r, 0);
 }
 
 static bool hasCF(task_t task, bool launchedSuspended)
 }
 
 static bool hasCF(task_t task, bool launchedSuspended)
@@ -127,43 +144,55 @@ static bool hasCF(task_t task, bool launchedSuspended)
 
 int main(int argc, const char* argv[])
 {
 
 int main(int argc, const char* argv[])
 {
+    kern_return_t kr = KERN_SUCCESS;
     printf("[BEGIN] dyld_process_info\n");
 
     if ( argc < 2 ) {
         printf("[FAIL] dyld_process_info missing argument\n");
         exit(0);
     }
     printf("[BEGIN] dyld_process_info\n");
 
     if ( argc < 2 ) {
         printf("[FAIL] dyld_process_info missing argument\n");
         exit(0);
     }
+
     const char* testProgPath = argv[1];
     const char* testProgPath = argv[1];
-    task_t childTask;
+    struct task_and_pid child;
 
     // launch test program same arch as this program
 
     // launch test program same arch as this program
-    childTask = launchTest(testProgPath, false, false);
-    if ( ! hasCF(childTask, false) ) {
+    child = launchTest(testProgPath, false, false);
+    if ( ! hasCF(child.task, false) ) {
         printf("[FAIL] dyld_process_info same arch does not link with CF and dyld\n");
         printf("[FAIL] dyld_process_info same arch does not link with CF and dyld\n");
-        task_terminate(childTask);
+        killTest(child);
         exit(0);
     }
         exit(0);
     }
-    task_terminate(childTask);
-    
+    killTest(child);
+
     // launch test program suspended
     // launch test program suspended
-    childTask = launchTest(testProgPath, false, true);
-    if ( ! hasCF(childTask, true) ) {
+    child = launchTest(testProgPath, false, true);
+    if ( ! hasCF(child.task, true) ) {
         printf("[FAIL] dyld_process_info suspended does not link with CF and dyld\n");
         printf("[FAIL] dyld_process_info suspended does not link with CF and dyld\n");
-        task_terminate(childTask);
+        killTest(child);
         exit(0);
     }
         exit(0);
     }
-    task_resume(childTask);
-    task_terminate(childTask);
+    (void)kill(child.pid, SIGCONT);
+    killTest(child);
 
 
-#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) ) {
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
+    // only mac supports multiple architectures, run test program as other arch too
+    child = launchTest(testProgPath, true, false);
+    if ( ! hasCF(child.task, false) ) {
         printf("[FAIL] dyld_process_info other arch does not link with CF and dyld\n");
         printf("[FAIL] dyld_process_info other arch does not link with CF and dyld\n");
-        task_terminate(childTask);
+        killTest(child);
+        exit(0);
+    }
+    killTest(child);
+
+    // launch test program suspended
+    child = launchTest(testProgPath, true, true);
+    if ( ! hasCF(child.task, true) ) {
+        printf("[FAIL] dyld_process_info suspended does not link with CF and dyld\n");
+        killTest(child);
         exit(0);
     }
         exit(0);
     }
-    task_terminate(childTask);
+    (void)kill(child.pid, SIGCONT);
+    killTest(child);
 #endif
 
     // verify this program does not use CF
 #endif
 
     // verify this program does not use CF
index e04e618cbf90bbe3a219cd8e54ad67cf86ef82ef..d76d44f5fdc4035454c988a39c87c2640f0094d8 100644 (file)
@@ -18,6 +18,7 @@
 #include <mach/machine.h>
 #include <mach-o/dyld_process_info.h>
 #include <dispatch/dispatch.h>
 #include <mach/machine.h>
 #include <mach-o/dyld_process_info.h>
 #include <dispatch/dispatch.h>
+#include <Availability.h>
 
 
 extern char** environ;
 
 
 extern char** environ;
@@ -32,10 +33,15 @@ extern char** environ;
     cpu_type_t otherArch[] = { CPU_TYPE_ARM64 };
 #endif
 
     cpu_type_t otherArch[] = { CPU_TYPE_ARM64 };
 #endif
 
-static task_t launchTest(const char* testProgPath, const char* arg1, bool launchOtherArch, bool launchSuspended)
+struct task_and_pid {
+    pid_t pid;
+    task_t task;
+};
+
+static struct task_and_pid launchTest(const char* testProgPath, const char* arg1, bool launchOtherArch, bool launchSuspended)
 {
     //fprintf(stderr, "launchTest() launchOtherArch=%d, launchSuspended=%d, arg=%s\n", launchOtherArch, launchSuspended, arg1);
 {
     //fprintf(stderr, "launchTest() launchOtherArch=%d, launchSuspended=%d, arg=%s\n", launchOtherArch, launchSuspended, arg1);
-    posix_spawnattr_t attr;
+    posix_spawnattr_t attr = 0;
     if ( posix_spawnattr_init(&attr) != 0 ) {
         printf("[FAIL] dyld_process_info_notify posix_spawnattr_init()\n");
         exit(0);
     if ( posix_spawnattr_init(&attr) != 0 ) {
         printf("[FAIL] dyld_process_info_notify posix_spawnattr_init()\n");
         exit(0);
@@ -54,23 +60,29 @@ static task_t launchTest(const char* testProgPath, const char* arg1, bool launch
         }
     }
 
         }
     }
 
-    pid_t childPid;
+    struct task_and_pid child;
     const char* argv[] = { testProgPath, arg1, NULL };
     const char* argv[] = { testProgPath, arg1, NULL };
-    int psResult = posix_spawn(&childPid, testProgPath, NULL, &attr, (char**)argv, environ);
+    int psResult = posix_spawn(&child.pid, 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);
     }
     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 ) {
+    if (posix_spawnattr_destroy(&attr) != 0) {
+        printf("[FAIL] dyld_process_info_notify posix_spawnattr_destroy()\n");
+        exit(0);
+    }
+    if ( task_for_pid(mach_task_self(), child.pid, &child.task) != KERN_SUCCESS ) {
         printf("[FAIL] dyld_process_info_notify task_for_pid()\n");
         printf("[FAIL] dyld_process_info_notify task_for_pid()\n");
-        kill(childPid, SIGKILL);
+        (void)kill(child.pid, SIGKILL);
         exit(0);
     }
 
         exit(0);
     }
 
-    return childTask;
+    return child;
+}
+
+static void killTest(struct task_and_pid tp) {
+    int r = kill(tp.pid, SIGKILL);
+    waitpid(tp.pid, &r, 0);
 }
 
 static void wait_util_task_suspended(task_t task)
 }
 
 static void wait_util_task_suspended(task_t task)
@@ -85,7 +97,7 @@ static void wait_util_task_suspended(task_t task)
 }
 
 
 }
 
 
-static bool monitor(task_t task, bool disconnectEarly, bool attachLate)
+static bool monitor(struct task_and_pid tp, bool disconnectEarly, bool attachLate)
 {
     kern_return_t kr;
     __block bool sawMainExecutable = false;
 {
     kern_return_t kr;
     __block bool sawMainExecutable = false;
@@ -105,7 +117,7 @@ static bool monitor(task_t task, bool disconnectEarly, bool attachLate)
     unsigned count = 0;
     dyld_process_info_notify handle;
     do {
     unsigned count = 0;
     dyld_process_info_notify handle;
     do {
-        handle = _dyld_process_info_notify(task, serviceQueue,
+        handle = _dyld_process_info_notify(tp.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;
                                           ^(bool unload, uint64_t timestamp, uint64_t machHeader, const uuid_t uuid, const char* path) {
                                             if ( strstr(path, "/target.exe") != NULL )
                                                 sawMainExecutable = true;
@@ -142,20 +154,23 @@ static bool monitor(task_t task, bool disconnectEarly, bool attachLate)
         return false;
     }
 
         return false;
     }
 
-    // register for notification that it is entrying main()
-    _dyld_process_info_notify_main(handle, ^{
-                                            //fprintf(stderr, "target entering main()\n");
-                                            gotMainNotice = true;
-                                            if ( !sawMainExecutable || !sawlibSystem )
-                                                gotMainNoticeBeforeAllInitialDylibs = true;
-                                            });
-
-    // if process suspends itself, wait until it has done so
-    if ( attachLate )
-        wait_util_task_suspended(task);
+    if (!attachLate) {
+        // If the process starts suspended register for main(),
+        // otherwise skip since this test is a race between
+        // process setup and notification registration
+        _dyld_process_info_notify_main(handle, ^{
+                                                //fprintf(stderr, "target entering main()\n");
+                                                gotMainNotice = true;
+                                                if ( !sawMainExecutable || !sawlibSystem )
+                                                    gotMainNoticeBeforeAllInitialDylibs = true;
+                                                });
+    } else {
+        // if process suspends itself, wait until it has done so
+        wait_util_task_suspended(tp.task);
+    }
 
     // resume from initial suspend
 
     // resume from initial suspend
-    task_resume(task);
+    kill(tp.pid, SIGCONT);
 
     // block waiting for notification that target has exited
     bool gotSignal = (dispatch_semaphore_wait(taskDone, dispatch_time(DISPATCH_TIME_NOW, 10LL * NSEC_PER_SEC)) == 0);
 
     // block waiting for notification that target has exited
     bool gotSignal = (dispatch_semaphore_wait(taskDone, dispatch_time(DISPATCH_TIME_NOW, 10LL * NSEC_PER_SEC)) == 0);
@@ -167,29 +182,33 @@ static bool monitor(task_t task, bool disconnectEarly, bool attachLate)
         return false;
     }
 
         return false;
     }
 
-    if ( !attachLate && !sawMainExecutable ) {
-        fprintf(stderr, "did not get load notification of main executable\n");
-        return false;
-    }
+    // Do not run any tests associated with startup unless the kernel suspended us
+    // before main()
+    if (!attachLate) {
+        if ( !sawMainExecutable ) {
+            fprintf(stderr, "did not get load notification of main executable\n");
+            return false;
+        }
 
 
-    if ( !gotMainNotice ) {
-        fprintf(stderr, "did not get notification of main()\n");
-        return false;
-    }
+        if ( !gotMainNotice ) {
+            fprintf(stderr, "did not get notification of main()\n");
+            return false;
+        }
 
 
-    if ( gotMainNoticeBeforeAllInitialDylibs ) {
-        fprintf(stderr, "notification of main() arrived before all initial dylibs\n");
-        return false;
-    }
+        if ( gotMainNoticeBeforeAllInitialDylibs ) {
+            fprintf(stderr, "notification of main() arrived before all initial dylibs\n");
+            return false;
+        }
 
 
-    if ( gotFooNoticeBeforeMain ) {
-        fprintf(stderr, "notification of main() arrived after libfoo load notice\n");
-        return false;
-    }
+        if ( gotFooNoticeBeforeMain ) {
+            fprintf(stderr, "notification of main() arrived after libfoo load notice\n");
+            return false;
+        }
 
 
-    if ( !attachLate && !sawlibSystem ) {
-        fprintf(stderr, "did not get load notification of libSystem\n");
-        return false;
+        if ( !sawlibSystem ) {
+            fprintf(stderr, "did not get load notification of libSystem\n");
+            return false;
+        }
     }
 
     if ( disconnectEarly ) {
     }
 
     if ( disconnectEarly ) {
@@ -216,13 +235,13 @@ static bool monitor(task_t task, bool disconnectEarly, bool attachLate)
     return true;
 }
 
     return true;
 }
 
-static void validateMaxNotifies(task_t task)
+static void validateMaxNotifies(struct task_and_pid tp)
 {
     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;
 {
     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,
+        handles[i] = _dyld_process_info_notify(tp.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],
                                           ^(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],
@@ -241,7 +260,7 @@ static void validateMaxNotifies(task_t task)
             }
             else {
                 fprintf(stderr, "_dyld_process_info_notify() returned NULL and kern_result=%d, on count=%d\n", kr, i);
             }
             else {
                 fprintf(stderr, "_dyld_process_info_notify() returned NULL and kern_result=%d, on count=%d\n", kr, i);
-                task_terminate(task);
+                killTest(tp);
                 exit(0);
             }
         }
                 exit(0);
             }
         }
@@ -265,55 +284,55 @@ int main(int argc, const char* argv[])
     const char* testProgPath = argv[1];
 
     dispatch_async(dispatch_get_main_queue(), ^{
     const char* testProgPath = argv[1];
 
     dispatch_async(dispatch_get_main_queue(), ^{
-        task_t childTask;
+        struct task_and_pid child;
 
         // test 1) launch test program suspended in same arch as this program
 
         // test 1) launch test program suspended in same arch as this program
-        childTask = launchTest(testProgPath, "", false, true);
-        if ( ! monitor(childTask, false, false) ) {
+        child = launchTest(testProgPath, "", false, true);
+        if ( ! monitor(child, false, false) ) {
             printf("[FAIL] dyld_process_info_notify launch suspended missed some notifications\n");
             printf("[FAIL] dyld_process_info_notify launch suspended missed some notifications\n");
-            task_terminate(childTask);
+            killTest(child);
             exit(0);
         }
             exit(0);
         }
-        task_terminate(childTask);
+        killTest(child);
 
         // test 2) launch test program in same arch as this program where it sleeps itself
 
         // 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) ) {
+        child = launchTest(testProgPath, "suspend-in-main", false, false);
+        validateMaxNotifies(child);
+        if ( ! monitor(child, false, true) ) {
             printf("[FAIL] dyld_process_info_notify launch suspend-in-main missed some notifications\n");
             printf("[FAIL] dyld_process_info_notify launch suspend-in-main missed some notifications\n");
-            task_terminate(childTask);
+             killTest(child);
             exit(0);
         }
             exit(0);
         }
-        task_terminate(childTask);
+        killTest(child);
 
 
-#if !TARGET_OS_WATCH && !TARGET_OS_TV && __LP64__
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
         // test 3) launch test program suspended in opposite arch as this program
         // test 3) launch test program suspended in opposite arch as this program
-        childTask = launchTest(testProgPath, "", true, true);
-        if ( ! monitor(childTask, false, false) ) {
+        child = launchTest(testProgPath, "", true, true);
+        if ( ! monitor(child, false, false) ) {
             printf("[FAIL] dyld_process_info_notify launch suspended other arch missed some notifications\n");
             printf("[FAIL] dyld_process_info_notify launch suspended other arch missed some notifications\n");
-            task_terminate(childTask);
+            killTest(child);
             exit(0);
         }
             exit(0);
         }
-        task_terminate(childTask);
+        killTest(child);
 
         // test 4) launch test program in opposite arch as this program where it sleeps itself
 
         // 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) ) {
+        child = launchTest(testProgPath, "suspend-in-main", true, false);
+        if ( ! monitor(child, false, true) ) {
             printf("[FAIL] dyld_process_info_notify launch other arch suspend-in-main missed some notifications\n");
             printf("[FAIL] dyld_process_info_notify launch other arch suspend-in-main missed some notifications\n");
-            task_terminate(childTask);
+            killTest(child);
             exit(0);
         }
             exit(0);
         }
-        task_terminate(childTask);
+        killTest(child);
 #endif
 
         // test 5) launch test program where we disconnect from it after first dlopen
 #endif
 
         // test 5) launch test program where we disconnect from it after first dlopen
-        childTask = launchTest(testProgPath, "", false, true);
-        if ( ! monitor(childTask, true, false) ) {
+        child = launchTest(testProgPath, "", false, true);
+        if ( ! monitor(child, true, false) ) {
             printf("[FAIL] dyld_process_info_notify connect/disconnect missed some notifications\n");
             printf("[FAIL] dyld_process_info_notify connect/disconnect missed some notifications\n");
-            task_terminate(childTask);
+            killTest(child);
             exit(0);
         }
             exit(0);
         }
-        task_terminate(childTask);
+        killTest(child);
 
         printf("[PASS] dyld_process_info_notify\n");
         exit(0);
 
         printf("[PASS] dyld_process_info_notify\n");
         exit(0);
index f73e5a0814070045822d8030a33eade253f43bc1..55fd668c8aa39f9c6914ac5cecd2a8fced83d89a 100644 (file)
@@ -2,6 +2,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <dlfcn.h>
 #include <stdlib.h>
 #include <string.h>
 #include <dlfcn.h>
+#include <signal.h>
 #include <unistd.h>
 #include <mach/mach.h>
 
 #include <unistd.h>
 #include <mach/mach.h>
 
@@ -10,7 +11,7 @@
 int main(int argc, const char* argv[])
 {
     if ( (argc > 1) && (strcmp(argv[1], "suspend-in-main") == 0) )
 int main(int argc, const char* argv[])
 {
     if ( (argc > 1) && (strcmp(argv[1], "suspend-in-main") == 0) )
-        task_suspend(mach_task_self());
+        (void)kill(getpid(), SIGSTOP);
 
     for (int i=0; i < 3; ++i) {
         void* h = dlopen("./libfoo.dylib", 0);
 
     for (int i=0; i < 3; ++i) {
         void* h = dlopen("./libfoo.dylib", 0);
index 3a8bfd53c74b4acfdc8c32ff3f25824cf5e30307..6954ad1db3d271f22bf6eded121a656e2ad2514d 100644 (file)
@@ -30,40 +30,47 @@ extern char** environ;
     cpu_type_t otherArch[] = { CPU_TYPE_ARM64 };
 #endif
 
     cpu_type_t otherArch[] = { CPU_TYPE_ARM64 };
 #endif
 
-static task_t launchTest(const char* testProgPath, bool launchOtherArch, bool launchSuspended)
+struct task_and_pid {
+    pid_t pid;
+    task_t task;
+};
+
+static struct task_and_pid launchTest(const char* testProgPath, bool launchOtherArch, bool launchSuspended)
 {
 {
-    posix_spawnattr_t attr;
+    posix_spawnattr_t attr = 0;
     if ( posix_spawnattr_init(&attr) != 0 ) {
     if ( posix_spawnattr_init(&attr) != 0 ) {
-        printf("[FAIL] dyld_process_info posix_spawnattr_init()\n");
+        printf("[FAIL] dyld_process_info_unload posix_spawnattr_init()\n");
         exit(0);
     }
     if ( launchSuspended ) {
         if ( posix_spawnattr_setflags(&attr, POSIX_SPAWN_START_SUSPENDED) != 0 ) {
         exit(0);
     }
     if ( launchSuspended ) {
         if ( posix_spawnattr_setflags(&attr, POSIX_SPAWN_START_SUSPENDED) != 0 ) {
-            printf("[FAIL] dyld_process_info POSIX_SPAWN_START_SUSPENDED\n");
+            printf("[FAIL] dyld_process_info_unload POSIX_SPAWN_START_SUSPENDED\n");
             exit(0);
         }
     }
     if ( launchOtherArch ) {
         size_t copied;
         if ( posix_spawnattr_setbinpref_np(&attr, 1, otherArch, &copied) != 0 ) {
             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");
+           printf("[FAIL] dyld_process_info_unload posix_spawnattr_setbinpref_np()\n");
             exit(0);
         }
     }
 
             exit(0);
         }
     }
 
-    pid_t childPid;
+    struct task_and_pid child;
     const char* argv[] = { testProgPath, NULL };
     const char* argv[] = { testProgPath, NULL };
-    int psResult = posix_spawn(&childPid, testProgPath, NULL, &attr, (char**)argv, environ);
+    int psResult = posix_spawn(&child.pid, testProgPath, NULL, &attr, (char**)argv, environ);
     if ( psResult != 0 ) {
     if ( psResult != 0 ) {
-        printf("[FAIL] dyld_process_info posix_spawn(%s) failed, err=%d\n", testProgPath, psResult);
+        printf("[FAIL] dyld_process_info_unload posix_spawn(%s) failed, err=%d\n", testProgPath, psResult);
+        exit(0);
+    }
+    if (posix_spawnattr_destroy(&attr) != 0) {
+        printf("[FAIL] dyld_process_info_unload posix_spawnattr_destroy()\n");
         exit(0);
     }
         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);
+    if ( task_for_pid(mach_task_self(), child.pid, &child.task) != KERN_SUCCESS ) {
+        printf("[FAIL] dyld_process_info_unload task_for_pid()\n");
+        kill(child.pid, SIGKILL);
         exit(0);
     }
 
         exit(0);
     }
 
@@ -71,22 +78,27 @@ static task_t launchTest(const char* testProgPath, bool launchOtherArch, bool la
     struct task_basic_info info;
     do {
         unsigned count = TASK_BASIC_INFO_COUNT;
     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);
+        kern_return_t kr = task_info(child.task, TASK_BASIC_INFO, (task_info_t)&info, &count);
         sleep(1);
     } while ( info.suspend_count == 0 );
 
         sleep(1);
     } while ( info.suspend_count == 0 );
 
-    return childTask;
+    return child;
 }
 
 }
 
-static bool alwaysGetImages(task_t task, bool launchedSuspended)
+static void killTest(struct task_and_pid tp) {
+    int r = kill(tp.pid, SIGKILL);
+    waitpid(tp.pid, &r, 0);
+}
+
+static bool alwaysGetImages(struct task_and_pid tp, bool launchedSuspended)
 {
     int failCount = 0;
     for (int i=0; i < 100; ++i ) {
         kern_return_t result;
 {
     int failCount = 0;
     for (int i=0; i < 100; ++i ) {
         kern_return_t result;
-        dyld_process_info info = _dyld_process_info_create(task, 0, &result);
+        dyld_process_info info = _dyld_process_info_create(tp.task, 0, &result);
         //fprintf(stderr, "info=%p, result=%08X\n", info, result);
         if ( i == 0 )
         //fprintf(stderr, "info=%p, result=%08X\n", info, result);
         if ( i == 0 )
-            task_resume(task);
+            (void)kill(tp.pid, SIGCONT);
         if ( info == NULL ) {
             failCount++;
             //fprintf(stderr, "info=%p, result=%08X\n", info, result);
         if ( info == NULL ) {
             failCount++;
             //fprintf(stderr, "info=%p, result=%08X\n", info, result);
@@ -96,7 +108,10 @@ static bool alwaysGetImages(task_t task, bool launchedSuspended)
             _dyld_process_info_release(info);
         }
     }
             _dyld_process_info_release(info);
         }
     }
-    if ( failCount !=0 ) {
+    // ideally the fail count would be zero.  But the target is dlopen/dlclosing in a tight loop, so there may never be a stable set of images.
+    // The real bug driving this test case was _dyld_process_info_create() crashing when the the image list changed too fast.
+    // The important thing is to not crash.  Getting NULL back is ok.
+    if ( failCount > 50 ) {
         printf("[FAIL] dyld_process_info_unload %d out of 100 calls to _dyld_process_info_create() failed\n", failCount);
         return false;
     }
         printf("[FAIL] dyld_process_info_unload %d out of 100 calls to _dyld_process_info_create() failed\n", failCount);
         return false;
     }
@@ -113,16 +128,15 @@ int main(int argc, const char* argv[])
         exit(0);
     }
     const char* testProgPath = argv[1];
         exit(0);
     }
     const char* testProgPath = argv[1];
-    task_t childTask;
+    struct task_and_pid child;
 
     // launch test program suspended
 
     // launch test program suspended
-    childTask = launchTest(testProgPath, false, true);
-    if ( ! alwaysGetImages(childTask, true) ) {
-        task_terminate(childTask);
+    child = launchTest(testProgPath, false, true);
+    if ( ! alwaysGetImages(child, true) ) {
+        killTest(child);
         exit(0);
     }
         exit(0);
     }
-    task_terminate(childTask);
-
+    killTest(child);
 
     printf("[PASS] dyld_process_info_unload\n");
        return 0;
 
     printf("[PASS] dyld_process_info_unload\n");
        return 0;
diff --git a/testing/test-cases/env-DYLD_FALLBACK_FRAMEWORK_PATH.dtest/foo.c b/testing/test-cases/env-DYLD_FALLBACK_FRAMEWORK_PATH.dtest/foo.c
new file mode 100644 (file)
index 0000000..b6dfea2
--- /dev/null
@@ -0,0 +1,5 @@
+int foo()
+{
+       return VALUE;
+}
+
diff --git a/testing/test-cases/env-DYLD_FALLBACK_FRAMEWORK_PATH.dtest/main.c b/testing/test-cases/env-DYLD_FALLBACK_FRAMEWORK_PATH.dtest/main.c
new file mode 100644 (file)
index 0000000..c26697b
--- /dev/null
@@ -0,0 +1,25 @@
+
+// BUILD:  mkdir -p $TEMP_DIR/Foo.framework $BUILD_DIR/FallbackFrameworks/Foo.framework
+// BUILD:  $CC foo.c -dynamiclib -o $TEMP_DIR/Foo.framework/Foo                     -install_name $RUN_DIR/Foo.framework/Foo -DVALUE=1
+// BUILD:  $CC foo.c -dynamiclib -o $BUILD_DIR/FallbackFrameworks/Foo.framework/Foo -install_name $RUN_DIR/Foo.framework/Foo -DVALUE=42
+// BUILD:  $CC main.c            -o $BUILD_DIR/main.exe $TEMP_DIR/Foo.framework/Foo
+// BUILD:  $DYLD_ENV_VARS_ENABLE $BUILD_DIR/main.exe
+
+// RUN:  DYLD_FALLBACK_FRAMEWORK_PATH=$RUN_DIR/FallbackFrameworks/ ./main.exe
+
+#include <stdio.h>
+#include <stdlib.h>
+
+extern int foo();
+
+int main()
+{
+    printf("[BEGIN] env-DYLD_FALLBACK_FRAMEWORK_PATH\n");
+
+       if ( foo() == 42 )
+        printf("[PASS] env-DYLD_FALLBACK_FRAMEWORK_PATH\n");
+    else
+        printf("[FAIL] env-DYLD_FALLBACK_FRAMEWORK_PATH\n");
+
+       return 0;
+}
diff --git a/testing/test-cases/env-DYLD_FALLBACK_LIBRARY_PATH.dtest/foo.c b/testing/test-cases/env-DYLD_FALLBACK_LIBRARY_PATH.dtest/foo.c
new file mode 100644 (file)
index 0000000..b6dfea2
--- /dev/null
@@ -0,0 +1,5 @@
+int foo()
+{
+       return VALUE;
+}
+
diff --git a/testing/test-cases/env-DYLD_FALLBACK_LIBRARY_PATH.dtest/main.c b/testing/test-cases/env-DYLD_FALLBACK_LIBRARY_PATH.dtest/main.c
new file mode 100644 (file)
index 0000000..1c94836
--- /dev/null
@@ -0,0 +1,25 @@
+
+// BUILD:  mkdir -p $BUILD_DIR/fallback
+// BUILD:  $CC foo.c -dynamiclib -o $TEMP_DIR/libfoo.dylib           -install_name $RUN_DIR/libfoo.dylib -DVALUE=1
+// BUILD:  $CC foo.c -dynamiclib -o $BUILD_DIR/fallback/libfoo.dylib -install_name $RUN_DIR/libfoo.dylib -DVALUE=42
+// BUILD:  $CC main.c            -o $BUILD_DIR/main.exe $TEMP_DIR/libfoo.dylib
+// BUILD:  $DYLD_ENV_VARS_ENABLE $BUILD_DIR/main.exe
+
+// RUN:  DYLD_FALLBACK_LIBRARY_PATH=$RUN_DIR/fallback/ ./main.exe
+
+#include <stdio.h>
+
+extern int foo();
+
+int main()
+{
+     printf("[BEGIN] env-DYLD_FALLBACK_LIBRARY_PATH\n");
+
+       if ( foo() == 42 )
+        printf("[PASS] env-DYLD_FALLBACK_LIBRARY_PATH\n");
+    else
+        printf("[FAIL] env-DYLD_FALLBACK_LIBRARY_PATH\n");
+
+       return 0;
+}
+
diff --git a/testing/test-cases/env-DYLD_FRAMEWORK_PATH.dtest/foo.c b/testing/test-cases/env-DYLD_FRAMEWORK_PATH.dtest/foo.c
new file mode 100644 (file)
index 0000000..b6dfea2
--- /dev/null
@@ -0,0 +1,5 @@
+int foo()
+{
+       return VALUE;
+}
+
diff --git a/testing/test-cases/env-DYLD_FRAMEWORK_PATH.dtest/main.c b/testing/test-cases/env-DYLD_FRAMEWORK_PATH.dtest/main.c
new file mode 100644 (file)
index 0000000..915729f
--- /dev/null
@@ -0,0 +1,29 @@
+
+// BUILD:  mkdir -p $BUILD_DIR/Frameworks/Foo.framework $BUILD_DIR/Frameworks-alt/Foo.framework
+// BUILD:  $CC foo.c -dynamiclib -o $BUILD_DIR/Frameworks/Foo.framework/Foo     -install_name $RUN_DIR/Frameworks/Foo.framework/Foo -DVALUE=1
+// BUILD:  $CC foo.c -dynamiclib -o $BUILD_DIR/Frameworks-alt/Foo.framework/Foo -install_name $RUN_DIR/Frameworks/Foo.framework/Foo -DVALUE=42
+// BUILD:  $CC main.c            -o $BUILD_DIR/main.exe $BUILD_DIR/Frameworks/Foo.framework/Foo
+// BUILD:  $DYLD_ENV_VARS_ENABLE $BUILD_DIR/main.exe
+
+// RUN:  ./main.exe
+// RUN:  DYLD_FRAMEWORK_PATH=$RUN_DIR/Frameworks-alt/ ./main.exe
+
+#include <stdio.h>
+#include <stdlib.h>
+
+extern int foo();
+
+int main()
+{
+    int expected = (getenv("DYLD_FRAMEWORK_PATH") != NULL) ? 42 : 1;
+
+    printf("[BEGIN] env-DYLD_FRAMEWORK_PATH, expect %d\n", expected);
+
+       if ( foo() == expected )
+        printf("[PASS] env-DYLD_FRAMEWORK_PATH\n");
+    else
+        printf("[FAIL] env-DYLD_FRAMEWORK_PATH\n");
+
+       return 0;
+}
+
diff --git a/testing/test-cases/env-DYLD_LIBRARY_PATH.dtest/foo.c b/testing/test-cases/env-DYLD_LIBRARY_PATH.dtest/foo.c
new file mode 100644 (file)
index 0000000..b6dfea2
--- /dev/null
@@ -0,0 +1,5 @@
+int foo()
+{
+       return VALUE;
+}
+
diff --git a/testing/test-cases/env-DYLD_LIBRARY_PATH.dtest/main.c b/testing/test-cases/env-DYLD_LIBRARY_PATH.dtest/main.c
new file mode 100644 (file)
index 0000000..69d199e
--- /dev/null
@@ -0,0 +1,29 @@
+
+// BUILD:  mkdir -p $BUILD_DIR/door1 $BUILD_DIR/door2
+// BUILD:  $CC foo.c -dynamiclib -o $BUILD_DIR/door1/libfoo.dylib -install_name $RUN_DIR/door1/libfoo.dylib -DVALUE=1
+// BUILD:  $CC foo.c -dynamiclib -o $BUILD_DIR/door2/libfoo.dylib -install_name $RUN_DIR/door2/libfoo.dylib -DVALUE=42
+// BUILD:  $CC main.c            -o $BUILD_DIR/main.exe $BUILD_DIR/door1/libfoo.dylib
+// BUILD:  $DYLD_ENV_VARS_ENABLE $BUILD_DIR/main.exe
+
+// RUN:  ./main.exe
+// RUN:  DYLD_LIBRARY_PATH=$RUN_DIR/door2/ ./main.exe
+
+#include <stdio.h>
+#include <stdlib.h>
+
+extern int foo();
+
+int main()
+{
+    int expected = (getenv("DYLD_LIBRARY_PATH") != NULL) ? 42 : 1;
+
+    printf("[BEGIN] env-DYLD_LIBRARY_PATH, expect %d\n", expected);
+
+       if ( foo() == expected )
+        printf("[PASS] env-DYLD_LIBRARY_PATH\n");
+    else
+        printf("[FAIL] env-DYLD_LIBRARY_PATH\n");
+
+       return 0;
+}
+
diff --git a/testing/test-cases/flat-namespace.dtest/foo.c b/testing/test-cases/flat-namespace.dtest/foo.c
new file mode 100644 (file)
index 0000000..20ae519
--- /dev/null
@@ -0,0 +1,21 @@
+
+#include <stdlib.h>
+#include <string.h>
+
+char buffer[100000];
+char* p = buffer;
+
+void* malloc(size_t size)
+{
+    // bump ptr allocate and fill second half with '#'
+    char* result = p;
+    p += size;
+    memset(result, '#', size);
+    p = (char*)(((long)p + 15) & (-16)); // 16-byte align next malloc
+    return result;
+}
+
+void free(void* p)
+{
+}
+
diff --git a/testing/test-cases/flat-namespace.dtest/main.c b/testing/test-cases/flat-namespace.dtest/main.c
new file mode 100644 (file)
index 0000000..37c99fc
--- /dev/null
@@ -0,0 +1,28 @@
+// BUILD_ONLY: MacOSX
+
+// 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/flat-namespace.exe -flat_namespace
+
+// RUN:    ./flat-namespace.exe
+
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+int main()
+{
+    printf("[BEGIN] flat-namespace\n");
+
+    // check that the malloc in libfoo.dylib was used by looking at the content the allocated buffer
+    // <rdar://problem/31921090> strncmp is tricky for flat namespace because it is re-exporte and renamed
+    char* p1 = malloc(10);
+    if ( strncmp(p1, "##########", 10) != 0 ) {
+        printf("[FAIL] malloc() from main executable not interposed\n");
+        return 0;
+    }
+
+    printf("[PASS] flat-namespace\n");
+       return 0;
+}
diff --git a/testing/test-cases/interpose-malloc.dtest/foo.c b/testing/test-cases/interpose-malloc.dtest/foo.c
new file mode 100644 (file)
index 0000000..d539c96
--- /dev/null
@@ -0,0 +1,15 @@
+
+#include <stdlib.h>
+
+void* myalloc1(size_t sz)
+{
+    return malloc(sz);
+}
+
+void* (*pMalloc)(size_t) = &malloc;
+
+void* myalloc2(size_t sz)
+{
+    return (*pMalloc)(sz);
+}
+
diff --git a/testing/test-cases/interpose-malloc.dtest/interposer.c b/testing/test-cases/interpose-malloc.dtest/interposer.c
new file mode 100644 (file)
index 0000000..ceac979
--- /dev/null
@@ -0,0 +1,25 @@
+#include <stdlib.h>
+#include <string.h>
+#include <mach-o/dyld-interposing.h>
+
+
+char buffer[100000];
+char* p = buffer;
+
+void* mymalloc(size_t size)
+{
+    // bump ptr allocate twice the size and fill second half with '#'
+    char* result = p;
+    p += size;
+    memset(p, '#', size);
+    p += size;
+    p = (char*)(((long)p + 15) & (-16)); // 16-byte align next malloc
+    return result;
+}
+
+void myfree(void* p)
+{
+}
+
+DYLD_INTERPOSE(mymalloc, malloc)
+DYLD_INTERPOSE(myfree, free)
diff --git a/testing/test-cases/interpose-malloc.dtest/main.c b/testing/test-cases/interpose-malloc.dtest/main.c
new file mode 100644 (file)
index 0000000..9ef1842
--- /dev/null
@@ -0,0 +1,48 @@
+// 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-malloc.exe
+// BUILD:  $DYLD_ENV_VARS_ENABLE $BUILD_DIR/interpose-malloc.exe
+// BUILD:  $CC interposer.c -dynamiclib -o $BUILD_DIR/libmyalloc.dylib -install_name libmyalloc.dylib
+
+// RUN:    DYLD_INSERT_LIBRARIES=libmyalloc.dylib   ./interpose-malloc.exe
+
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+extern void* myalloc1(size_t);
+extern void* myalloc2(size_t);
+
+int main()
+{
+    printf("[BEGIN] interpose-malloc\n");
+
+    char* p1 = malloc(10);
+    if ( strncmp(p1+10, "##########", 10) != 0 ) {
+        printf("[FAIL] interpose-malloc malloc() from main executable not interposed\n");
+        return 0;
+    }
+
+    void* p2 = myalloc1(6);
+    if ( strncmp(p2+6, "######", 6) != 0 ) {
+        printf("[FAIL] interpose-malloc myalloc1() from libfoo.dylib not interposed\n");
+        return 0;
+    }
+
+    void* p3 = myalloc2(10);
+    if ( strncmp(p3+10, "##########", 10) != 0 ) {
+        printf("[FAIL] interpose-malloc myalloc2() from libfoo.dylib not interposed\n");
+        return 0;
+    }
+
+    void* p4 = strdup("hello");
+     if ( strncmp(p4+6, "#######", 6) != 0 ) {
+        printf("[FAIL] interpose-malloc malloc() from strdup not interposed\n");
+        return 0;
+    }
+
+    //printf("%p %p %p %p\n", p1, p2, p3, p4);
+    printf("[PASS] interpose-malloc\n");
+       return 0;
+}
diff --git a/testing/test-cases/operator-new.dtest/main.cxx b/testing/test-cases/operator-new.dtest/main.cxx
new file mode 100644 (file)
index 0000000..d985e8f
--- /dev/null
@@ -0,0 +1,37 @@
+
+// BUILD:  $CXX main.cxx  -o $BUILD_DIR/operator-new.exe
+
+// RUN:  ./operator-new.exe
+
+#include <stdio.h>
+#include <new>
+
+
+
+//
+// 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()
+{
+       printf("[BEGIN] operator-new\n");
+
+    char* stuff = new char[24];
+    if ( (void*)stuff == ptr )
+        printf("[PASS] operator-new\n");
+    else
+        printf("[FAIL] operator-new\n");
+
+       return 0;
+}
+
+
diff --git a/testing/test-cases/thread-local-cleanup.dtest/foo.c b/testing/test-cases/thread-local-cleanup.dtest/foo.c
new file mode 100644 (file)
index 0000000..ac354a5
--- /dev/null
@@ -0,0 +1,6 @@
+
+__thread int a;
+__thread int b = 5;
+
+
+
diff --git a/testing/test-cases/thread-local-cleanup.dtest/main.c b/testing/test-cases/thread-local-cleanup.dtest/main.c
new file mode 100644 (file)
index 0000000..e301792
--- /dev/null
@@ -0,0 +1,36 @@
+
+// BUILD:  $CC foo.c -dynamiclib -install_name $RUN_DIR/libtlv.dylib -o $BUILD_DIR/libtlv.dylib
+// BUILD:  $CC main.c  -DRUN_DIR="$RUN_DIR"  -o $BUILD_DIR/thread-local-cleanup.exe
+
+// RUN:  ./thread-local-cleanup.exe
+
+#include <stdio.h>
+#include <dlfcn.h>
+
+
+
+
+
+int main()
+{
+    printf("[BEGIN] thread-local-cleanup\n");
+
+    for (int i=0; i < 1000; ++i) {
+        void* handle = dlopen(RUN_DIR "/libtlv.dylib", RTLD_FIRST);
+        if ( handle == NULL ) {
+            printf("[FAIL] thread-local-cleanup: iteration %d %s\n", i, dlerror());
+            return 0;
+        }
+
+        int result = dlclose(handle);
+        if ( result != 0 ) {
+            printf("[FAIL] thread-local-cleanup: iteration %d %s\n", i, dlerror());
+            return 0;
+        }
+    }
+    
+    printf("[PASS] thread-local-cleanup\n");
+
+       return 0;
+}
+
index 228935bebceb2e0b4ca80e56f307df19a4fb4fda..32ac531f562c3880c8642531fa6fbfb75dce5daa 100644 (file)
 
 int main()
 {
 
 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;
     uuid_t curUuid;
-    memcpy(curUuid, allInfo->sharedCacheUUID, 16);
+    _dyld_get_shared_cache_uuid(curUuid);
 
     int __block imageCount = 0;
     int result = dyld_shared_cache_iterate_text(curUuid, ^(const dyld_shared_cache_dylib_text_info* info) {
         ++imageCount;
 
     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);
+        //printf("  cur: 0x%09llX -> 0x%09llX  off=0x%0llX %s\n", info->loadAddressUnslid, info->loadAddressUnslid +  info->textSegmentSize, info->textSegmentOffset, info->path);
     });
        if ( result != 0 ) {
                FAIL("dyld_shared_cache_iterate_text: dyld_shared_cache_iterate_text() failed");
     });
        if ( result != 0 ) {
                FAIL("dyld_shared_cache_iterate_text: dyld_shared_cache_iterate_text() failed");