]> 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
+
+//: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
 
 
+//: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
-dyld \- the dynamic link editor
+dyld \- the dynamic linker
 .SH SYNOPSIS
 DYLD_FRAMEWORK_PATH
 .br
@@ -30,8 +30,6 @@ DYLD_PRINT_ENV
 .br
 DYLD_PRINT_LIBRARIES
 .br
-DYLD_PRINT_LIBRARIES_POST_LAUNCH
-.br
 DYLD_BIND_AT_LAUNCH
 .br
 DYLD_DISABLE_DOFS
@@ -56,8 +54,12 @@ DYLD_SHARED_CACHE_DIR
 .br
 DYLD_SHARED_CACHE_DONT_VALIDATE
 .SH DESCRIPTION
-The dynamic linker uses the following environment variables.
-They affect any program that uses the dynamic linker.
+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.
@@ -197,11 +199,6 @@ This is useful to make sure that the use of
 .SM DYLD_LIBRARY_PATH
 is getting what you want.
 .TP
-.B DYLD_PRINT_LIBRARIES_POST_LAUNCH
-This does the same as
-.SM DYLD_PRINT_LIBRARIES
-but the printing starts after the program gets to its entry point.
-.TP
 .B DYLD_BIND_AT_LAUNCH
 When this is set, the dynamic linker binds all undefined symbols
 the program needs at launch time. This includes function symbols that can are normally 
@@ -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"
-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
@@ -12,7 +12,6 @@
 .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
@@ -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
-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
@@ -34,11 +30,8 @@ the original file.  This results in significant performance improvements to
 launch time.
 .Pp
 .Nm update_dyld_shared_cache
-scans the directory /var/db/dyld/shared_region_roots for text files containing paths to
-mach-o executables.  The full dependencies of all dylibs required by those executables is
-used to determine which libraries are commonly used and should be placed in the
-shared cache. If one of the text files contains a path to a dylib, that dylib and its
-dependents will be forced into the cache.
+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
@@ -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.
-.It Fl verify
-Will regenerate a shared cache in-memory that matches the randomization of the existing shared 
-cache file.  Then instead of writing the cache file, it compares the in-memory cache file to
-the on disk version and reports any differences.  
 .El
-.Sh FILES
-.Tp
-/var/db/dyld/shared_region_roots
-directory of text files with paths to mach-o images used to determine what should be in shared cache.
 .Sh SEE ALSO
 .Xr dyld 1
index 49d05fff96724bcd7a1418870b4953b3520abac8..121b7fac78bda25bd7cb4fdb956ed11423edd61b 100644 (file)
                        isa = PBXAggregateTarget;
                        buildConfigurationList = 37A0AD0B1C15FFF500731E50 /* Build configuration list for PBXAggregateTarget "update_dyld_shared_cache" */;
                        buildPhases = (
+                               F94182D61E60E74E00D8EF25 /* pre-platform builds */,
                        );
                        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;
                        isa = PBXAggregateTarget;
                        buildConfigurationList = F908135211D3ED9000626CC1 /* Build configuration list for PBXAggregateTarget "libdyld" */;
                        buildPhases = (
-                               F908135111D3ED9000626CC1 /* usr|include|mach-o */,
-                               F908137011D3FB5000626CC1 /* usr|include */,
                                F9C69EFC14EC8AB8009CAE2E /* usr|local|include */,
-                               F908137111D3FB5000626CC1 /* usr|local|include|mach-o */,
                                F908137211D3FB5000626CC1 /* usr|share|man|man1 */,
                                F908137311D3FB5000626CC1 /* usr|share|man|man3 */,
-                               F96D19701D7F62C3007AF3CE /* Install dyld_priv.h */,
                        );
                        dependencies = (
                                F9B4D78012AD9736000605A6 /* PBXTargetDependency */,
 /* 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 */; };
-               376ABDBA1C5930E7009F0011 /* MachOProxy.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 376ABDB71C5930E7009F0011 /* MachOProxy.cpp */; };
-               376ABDBB1C5930E7009F0011 /* MachOProxy.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 376ABDB71C5930E7009F0011 /* MachOProxy.cpp */; };
                376ED1D81C46F2710051DD54 /* Metabom.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 376ED1D71C46F2710051DD54 /* Metabom.framework */; };
-               377685F51AC4B27D00026E6C /* AdjustForNewSegmentLocation.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9D0FE6D1A367093001E839B /* AdjustForNewSegmentLocation.cpp */; };
-               377685F61AC4B27D00026E6C /* BindAllImages.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9D0FE6E1A367093001E839B /* BindAllImages.cpp */; };
-               377685F71AC4B27D00026E6C /* OptimizerObjC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98E19031A3FC1A9008DB73F /* OptimizerObjC.cpp */; };
-               377685F81AC4B27D00026E6C /* OptimizerBranches.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98E18FF1A3FC1A9008DB73F /* OptimizerBranches.cpp */; };
-               377685F91AC4B27D00026E6C /* OptimizerLinkedit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98E19011A3FC1A9008DB73F /* OptimizerLinkedit.cpp */; };
-               377685FA1AC4B27D00026E6C /* FileCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9D0FE6F1A367093001E839B /* FileCache.cpp */; };
-               377685FB1AC4B27D00026E6C /* SharedCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9D0FE701A367093001E839B /* SharedCache.cpp */; };
-               377686041AC4B2ED00026E6C /* multi_dyld_shared_cache_builder.mm in Sources */ = {isa = PBXBuildFile; fileRef = 377686031AC4B2ED00026E6C /* multi_dyld_shared_cache_builder.mm */; };
                378EE3B11BE88C47001C99FB /* Bom.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 37F7A5961BB363820039043A /* Bom.framework */; };
                378EE3B21BE88C4A001C99FB /* Bom.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 37F7A5961BB363820039043A /* Bom.framework */; };
-               37BF1D761B6168150048BC27 /* Manifest.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37BF1D731B6168150048BC27 /* Manifest.mm */; };
-               37BF1D771B6168150048BC27 /* Manifest.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37BF1D731B6168150048BC27 /* Manifest.mm */; };
-               37F7A5951BB362CA0039043A /* update_dyld_shared_cache.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37E93E291AAFB0B10080E640 /* update_dyld_shared_cache.mm */; };
-               37F7A5981BB364130039043A /* Manifest.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37BF1D731B6168150048BC27 /* Manifest.mm */; };
-               37F7A5991BB364130039043A /* MultiCacheBuilder.mm in Sources */ = {isa = PBXBuildFile; fileRef = 371D29811B2F53C8000BBE48 /* MultiCacheBuilder.mm */; };
-               37F7A59A1BB3642F0039043A /* FileCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9D0FE6F1A367093001E839B /* FileCache.cpp */; };
-               37F7A59B1BB3642F0039043A /* SharedCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9D0FE701A367093001E839B /* SharedCache.cpp */; };
-               37F7A59C1BB364530039043A /* OptimizerBranches.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98E18FF1A3FC1A9008DB73F /* OptimizerBranches.cpp */; };
-               37F7A59D1BB364530039043A /* OptimizerLinkedit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98E19011A3FC1A9008DB73F /* OptimizerLinkedit.cpp */; };
-               37F7A59E1BB364530039043A /* OptimizerObjC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98E19031A3FC1A9008DB73F /* OptimizerObjC.cpp */; };
-               37F7A59F1BB364530039043A /* AdjustForNewSegmentLocation.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9D0FE6D1A367093001E839B /* AdjustForNewSegmentLocation.cpp */; };
-               37F7A5A01BB364530039043A /* BindAllImages.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9D0FE6E1A367093001E839B /* BindAllImages.cpp */; };
-               F908134C11D3ED6200626CC1 /* dyld.h in usr|include|mach-o */ = {isa = PBXBuildFile; fileRef = F9ED4CEA0630A80600DF4E74 /* dyld.h */; };
-               F908134D11D3ED6200626CC1 /* dyld_images.h in usr|include|mach-o */ = {isa = PBXBuildFile; fileRef = F98D274C0AA79D7400416316 /* dyld_images.h */; };
-               F908135911D3FA8700626CC1 /* dlfcn.h in usr|include */ = {isa = PBXBuildFile; fileRef = F99EE6AE06B48D4200BF1992 /* dlfcn.h */; };
-               F908135D11D3FACD00626CC1 /* dyld-interposing.h in usr|local|include|mach-o */ = {isa = PBXBuildFile; fileRef = F918691408B16D2500E0F9DB /* dyld-interposing.h */; };
-               F908135E11D3FACD00626CC1 /* dyld_cache_format.h in usr|local|include|mach-o */ = {isa = PBXBuildFile; fileRef = F93937400A94FC4700070A07 /* dyld_cache_format.h */; };
-               F908135F11D3FACD00626CC1 /* dyld_gdb.h in usr|local|include|mach-o */ = {isa = PBXBuildFile; fileRef = F9ED4CE80630A80600DF4E74 /* dyld_gdb.h */; };
+               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 */; };
                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 */; };
+               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 */; };
+               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 */; };
-               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 */; };
+               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 */; };
+               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 */; };
+               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 */; };
+               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 */; };
+               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 */; };
+               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 */; };
+               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 */; };
                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 */
                        remoteGlobalIDString = 37A0AD0A1C15FFF500731E50;
                        remoteInfo = update_dyld_shared_cache;
                };
-               37A0AD101C16003600731E50 /* PBXContainerItemProxy */ = {
+               D8668ACF1ECE335F005E7D31 /* PBXContainerItemProxy */ = {
                        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;
-                       remoteGlobalIDString = F9F2A5580F7AEE9800B7C9EB;
-                       remoteInfo = libdsc;
+                       remoteGlobalIDString = F9ED4C9E0630A76B00DF4E74;
+                       remoteInfo = libdyld.dylib;
                };
-               37A0AD141C16003600731E50 /* PBXContainerItemProxy */ = {
+               F922C8111F33B62700D8F479 /* PBXContainerItemProxy */ = {
                        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;
-                       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;
-                       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;
-                       remoteGlobalIDString = F99B8E550FEC10F600701838;
-                       remoteInfo = dyld_shared_cache_util;
+                       remoteGlobalIDString = F9F2A5580F7AEE9800B7C9EB;
+                       remoteInfo = libdsc;
                };
-               F908134711D3ED1A00626CC1 /* PBXContainerItemProxy */ = {
+               F94182DB1E60F16900D8EF25 /* PBXContainerItemProxy */ = {
                        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;
                        );
                        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;
                        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;
 
 /* 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; };
-               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; };
                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>"; };
+               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; };
                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>"; };
                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>"; };
+               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>"; };
+               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>"; };
+               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>"; };
-               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>"; };
-               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>"; };
-               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>"; };
                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; };
+               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>"; };
-               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>"; };
+               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>"; };
-               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; };
                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>"; };
+               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 */
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
+               F922C8141F33B73800D8F479 /* Frameworks */ = {
+                       isa = PBXFrameworksBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
                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;
                };
+               F9AB02B51F329FAA00EE96C4 /* Frameworks */ = {
+                       isa = PBXFrameworksBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
                F9D1001014D8D0BA00099D91 /* Frameworks */ = {
                        isa = PBXFrameworksBuildPhase;
                        buildActionMask = 2147483647;
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
+               F9DDEDAF1E2878CA00A753DC /* Frameworks */ = {
+                       isa = PBXFrameworksBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               F90F7C081E9C6B8B00535722 /* libCrashReporterClient.a in Frameworks */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
                F9F2A5570F7AEE9800B7C9EB /* Frameworks */ = {
                        isa = PBXFrameworksBuildPhase;
                        buildActionMask = 2147483647;
                EF799FE8070D27BB00F78484 /* man1 */ = {
                        isa = PBXGroup;
                        children = (
+                               F94942B21E6796D40019AE08 /* closured.1 */,
                                EF799FE9070D27BB00F78484 /* dyld.1 */,
                                F97FF3631C237F5C000ACDD2 /* nocr.1 */,
                                F9D238D90A9E19A0002B55C7 /* update_dyld_shared_cache.1 */,
                F939373D0A94FC4700070A07 /* launch-cache */ = {
                        isa = PBXGroup;
                        children = (
-                               F90C540A1A82D78000558E8C /* update_dyld_shared_cache_entitlements.plist */,
                                F939373E0A94FC4700070A07 /* Architectures.hpp */,
                                F939373F0A94FC4700070A07 /* CacheFileAbstraction.hpp */,
                                F93937400A94FC4700070A07 /* dyld_cache_format.h */,
                                F93937410A94FC4700070A07 /* FileAbstraction.hpp */,
                                F93937430A94FC4700070A07 /* MachOFileAbstraction.hpp */,
-                               F93937440A94FC4700070A07 /* MachOLayout.hpp */,
-                               F98935BA0A9A412B00FB6228 /* MachORebaser.hpp */,
-                               F98935B90A9A412B00FB6228 /* MachOBinder.hpp */,
                                F95C95160E994796007B7CB8 /* MachOTrie.hpp */,
                                F9F2A56E0F7AEEE300B7C9EB /* dsc_iterator.cpp */,
                                F9F2A56F0F7AEEE300B7C9EB /* dsc_iterator.h */,
                        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 = (
                                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 = nocr;
                        sourceTree = "<group>";
                };
-               F9D0FE6C1A367093001E839B /* interlinked-dylibs */ = {
+               F98692161DC3EF7700CBEDE6 /* shared-cache */ = {
                        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 = (
+                               F988F0BA1E2FDF5B003AED79 /* execserver.defs */,
                                F9F6F4261C1FAF8000BD8FED /* testing */,
                                F971DD121A4A0E0700BBDD52 /* configs */,
                                F9ED4CBB0630A7AA00DF4E74 /* src */,
                                F9ED4CBE0630A7B100DF4E74 /* include */,
                                F97FF3571C23638F000ACDD2 /* nocr */,
                                F9ED4C990630A76000DF4E74 /* Products */,
-                               F9D0FE6C1A367093001E839B /* interlinked-dylibs */,
+                               F96D19A41D9363B7007AF3CE /* dyld3 */,
                                F939373D0A94FC4700070A07 /* launch-cache */,
+                               F94C22231E513CA90079E5DD /* Frameworks */,
                        );
                        indentWidth = 4;
                        sourceTree = "<group>";
                        tabWidth = 4;
-                       usesTabs = 1;
+                       usesTabs = 0;
                };
                F9ED4C990630A76000DF4E74 /* Products */ = {
                        isa = PBXGroup;
                                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>";
                F9ED4CBB0630A7AA00DF4E74 /* src */ = {
                        isa = PBXGroup;
                        children = (
-                               F97FF35E1C236402000ACDD2 /* execserver.defs */,
                                F97FF35F1C236402000ACDD2 /* nocr.c */,
                                F9ED4CC60630A7F100DF4E74 /* dyld_gdb.cpp */,
                                F9ED4CC70630A7F100DF4E74 /* dyld.cpp */,
                                F94DB9010F0A9B1700323715 /* ImageLoaderMachOClassic.h */,
                                F94DB9020F0A9B1700323715 /* ImageLoaderMachOCompressed.cpp */,
                                F94DB9030F0A9B1700323715 /* ImageLoaderMachOCompressed.h */,
-                               F9280B791AB9DCA000B18AEC /* ImageLoaderMegaDylib.cpp */,
                                F9280B7A1AB9DCA000B18AEC /* ImageLoaderMegaDylib.h */,
+                               F9280B791AB9DCA000B18AEC /* ImageLoaderMegaDylib.cpp */,
                                F99DE0361AAE4F0400669496 /* libdyld_data_symbols.dirty */,
                                F9ED4CD50630A7F100DF4E74 /* stub_binding_helper.s */,
                                F9B01E3D0739ABDE00CF981B /* dyld.exp */,
                        children = (
                                F96D19711D7F63EE007AF3CE /* expand.pl */,
                                F95090D01C5AB89A0031F81D /* dyld_process_info.h */,
-                               F918691408B16D2500E0F9DB /* dyld-interposing.h */,
                                F98D274C0AA79D7400416316 /* dyld_images.h */,
+                               F918691408B16D2500E0F9DB /* dyld-interposing.h */,
+                               F9ED4CEA0630A80600DF4E74 /* dyld.h */,
                                F9ED4CE80630A80600DF4E74 /* dyld_gdb.h */,
                                F9ED4CE90630A80600DF4E74 /* dyld_priv.h */,
-                               F9ED4CEA0630A80600DF4E74 /* dyld.h */,
                                F99EE6AE06B48D4200BF1992 /* dlfcn.h */,
                                F9C69EFD14EC8ABF009CAE2E /* objc-shared-cache.h */,
                        );
                };
 /* 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;
                        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" */;
                                F93937300A94FAF700070A07 /* Frameworks */,
                                F9D238DD0A9E2FEE002B55C7 /* usr|share|man|man1 */,
                                F991E3030FF1A4EC0082CCC9 /* do not install duplicates */,
+                               F981C8C21F058F8200452F35 /* mkdir /var/db/dyld */,
                        );
                        buildRules = (
                        );
                        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" */;
                        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" */;
                        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" */;
                                F921D3160703769A000D1056 /* PBXBuildRule */,
                        );
                        dependencies = (
+                               F922C8121F33B62700D8F479 /* PBXTargetDependency */,
                                F99B8EB20FEC220C00701838 /* PBXTargetDependency */,
+                               F96543A11E343601003C5540 /* PBXTargetDependency */,
                        );
                        name = dyld;
                        productName = dyld;
                        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 = (
+                               F922C81E1F33B96300D8F479 /* PBXTargetDependency */,
                        );
                        name = libdyld.dylib;
                        productName = libdyld;
                F9ED4C8B0630A72300DF4E74 /* Project object */ = {
                        isa = PBXProject;
                        attributes = {
-                               LastUpgradeCheck = 0630;
+                               LastUpgradeCheck = 0900;
                                TargetAttributes = {
                                        37A0AD0A1C15FFF500731E50 = {
-                                               CreatedOnToolsVersion = 7.0;
+                                               CreatedOnToolsVersion = 8.0;
+                                       };
+                                       F922C8161F33B73800D8F479 = {
+                                               CreatedOnToolsVersion = 9.0;
+                                       };
+                                       F97C61A61DBAD1A900A84CD7 = {
+                                               CreatedOnToolsVersion = 8.0;
+                                               DevelopmentTeam = 59GAB85EFG;
+                                               ProvisioningStyle = Automatic;
                                        };
                                        F97FF3551C23638F000ACDD2 = {
-                                               CreatedOnToolsVersion = 7.1;
+                                               CreatedOnToolsVersion = 8.0;
+                                       };
+                                       F9AB02B71F329FAA00EE96C4 = {
+                                               CreatedOnToolsVersion = 9.0;
+                                       };
+                                       F9DDEDB11E2878CA00A753DC = {
+                                               CreatedOnToolsVersion = 8.2;
+                                               DevelopmentTeam = 59GAB85EFG;
+                                               ProvisioningStyle = Automatic;
                                        };
                                        F9F6F4271C1FB0A700BD8FED = {
-                                               CreatedOnToolsVersion = 7.1;
+                                               CreatedOnToolsVersion = 8.0;
                                        };
                                };
                        };
                        targets = (
                                F9ED4C920630A73900DF4E74 /* all */,
                                37A0AD0A1C15FFF500731E50 /* update_dyld_shared_cache */,
-                               F9ED4C970630A76000DF4E74 /* dyld */,
                                F908134211D3ED0B00626CC1 /* libdyld */,
+                               F9ED4C970630A76000DF4E74 /* dyld */,
+                               F9DDEDB11E2878CA00A753DC /* closured */,
                                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 */,
+                               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 */,
+                               F9AB02B71F329FAA00EE96C4 /* libclosured */,
+                               F922C8161F33B73800D8F479 /* libclosured-stub */,
                        );
                };
 /* 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;
                };
+               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;
                        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;
                        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 = (
+                               "${SRCROOT}/include/mach-o/dyld_priv.h",
                        );
-                       name = "Install dyld_priv.h";
+                       name = "expand dyld_priv.h macros";
                        outputPaths = (
+                               "${DSTROOT}/usr/local/include/mach-o/dyld_priv.h",
                        );
                        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 */ = {
                        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 = (
                        );
-                       name = "do not install duplicates";
+                       name = "mkdir /var/db/dyld";
                        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;
                };
-               F99B8EB60FEC236500701838 /* suppress macosx dyld_shared_cache_util */ = {
+               F99006DF1E411C500013456D /* install dlfcn.h */ = {
                        isa = PBXShellScriptBuildPhase;
                        buildActionMask = 8;
                        files = (
                        );
                        inputPaths = (
                        );
-                       name = "suppress macosx dyld_shared_cache_util";
+                       name = "install dlfcn.h";
                        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;
                };
-               F9D050C811DD701A00FB0A29 /* configure archives */ = {
+               F991E3030FF1A4EC0082CCC9 /* do not install duplicates */ = {
                        isa = PBXShellScriptBuildPhase;
-                       buildActionMask = 12;
+                       buildActionMask = 8;
                        files = (
                        );
                        inputPaths = (
                        );
-                       name = "configure archives";
+                       name = "do not install duplicates";
                        outputPaths = (
-                               "$(DERIVED_SOURCES_DIR)/archives.txt",
                        );
-                       runOnlyForDeploymentPostprocessing = 0;
+                       runOnlyForDeploymentPostprocessing = 1;
                        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;
                };
-               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 = 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;
                };
                        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;
                };
                        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;
                };
                        isa = PBXSourcesBuildPhase;
                        buildActionMask = 2147483647;
                        files = (
-                               F97FF3601C236408000ACDD2 /* execserver.defs in Sources */,
                                F97FF3611C23640C000ACDD2 /* nocr.c in Sources */,
+                               F988F0BB1E2FDF5B003AED79 /* execserver.defs in Sources */,
                        );
                        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;
                        );
                        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 = (
+                               37D7DB001E96F0ED00D52CEA /* Tracing.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 */,
+                               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;
                };
                        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 */,
                                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;
                };
                        target = 37A0AD0A1C15FFF500731E50 /* update_dyld_shared_cache */;
                        targetProxy = 37A0AD0E1C16000F00731E50 /* PBXContainerItemProxy */;
                };
-               37A0AD111C16003600731E50 /* PBXTargetDependency */ = {
+               D8668AD01ECE335F005E7D31 /* 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;
-                       target = F9F2A5580F7AEE9800B7C9EB /* libdsc */;
-                       targetProxy = 37A0AD121C16003600731E50 /* PBXContainerItemProxy */;
+                       target = F9ED4C9E0630A76B00DF4E74 /* libdyld.dylib */;
+                       targetProxy = F908134711D3ED1A00626CC1 /* PBXContainerItemProxy */;
                };
-               37A0AD151C16003600731E50 /* PBXTargetDependency */ = {
+               F922C8121F33B62700D8F479 /* PBXTargetDependency */ = {
                        isa = PBXTargetDependency;
-                       target = F9D1001114D8D0BA00099D91 /* dsc_extractor */;
-                       targetProxy = 37A0AD141C16003600731E50 /* PBXContainerItemProxy */;
+                       target = F9AB02B71F329FAA00EE96C4 /* libclosured */;
+                       targetProxy = F922C8111F33B62700D8F479 /* PBXContainerItemProxy */;
                };
-               37A0AD171C16003600731E50 /* PBXTargetDependency */ = {
+               F922C81E1F33B96300D8F479 /* 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;
-                       target = 3703A1111B38C1B300ADBA7F /* dyld_shared_cache_builder */;
-                       targetProxy = 37A0AD181C16003600731E50 /* PBXContainerItemProxy */;
+                       target = F99B8E550FEC10F600701838 /* dyld_shared_cache_util */;
+                       targetProxy = F94182D71E60F0BE00D8EF25 /* PBXContainerItemProxy */;
                };
-               37A0AD1B1C16004600731E50 /* PBXTargetDependency */ = {
+               F94182DA1E60F0C000D8EF25 /* PBXTargetDependency */ = {
                        isa = PBXTargetDependency;
-                       target = F99B8E550FEC10F600701838 /* dyld_shared_cache_util */;
-                       targetProxy = 37A0AD1A1C16004600731E50 /* PBXContainerItemProxy */;
+                       target = F9F2A5580F7AEE9800B7C9EB /* libdsc */;
+                       targetProxy = F94182D91E60F0C000D8EF25 /* PBXContainerItemProxy */;
                };
-               F908134811D3ED1A00626CC1 /* PBXTargetDependency */ = {
+               F94182DC1E60F16900D8EF25 /* 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;
                                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)";
+                               DEAD_CODE_STRIPPING = YES;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                FRAMEWORK_SEARCH_PATHS = (
                                        "$(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_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;
+                               SUPPORTED_PLATFORMS = macosx;
+                               TOOLCHAINS = default;
                                USER_HEADER_SEARCH_PATHS = "../launch-cache/";
-                               VALID_ARCHS = "armv6 armv7 x86_64 x86_64h";
+                               VALID_ARCHS = "x86_64 x86_64h";
                        };
                        name = Debug;
                };
                                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)";
+                               DEAD_CODE_STRIPPING = 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;
+                               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_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;
+                               SUPPORTED_PLATFORMS = macosx;
+                               TOOLCHAINS = default;
                                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;
                                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)",
                                GCC_PREPROCESSOR_DEFINITIONS = (
                                        "DEBUG=1",
                                        "$(inherited)",
+                                       "BUILDING_CACHE_BUILDER=1",
                                );
                                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";
+                               MACOSX_DEPLOYMENT_TARGET = 10.11;
                                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/";
-                               VALID_ARCHS = "armv6 armv7 x86_64 x86_64h";
+                               VALID_ARCHS = "x86_64 x86_64h";
                        };
                        name = Debug;
                };
                                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;
                                        "$(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_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/";
-                               VALID_ARCHS = "armv6 armv7 x86_64 x86_64h";
+                               VALID_ARCHS = "x86_64 x86_64h";
                                VERSIONING_SYSTEM = "apple-generic";
                        };
                        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 = {
                                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)",
                                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";
+                               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;
+                               USE_HEADERMAP = NO;
                                VALID_ARCHS = x86_64;
                        };
                        name = Debug;
                                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)";
+                               DEAD_CODE_STRIPPING = YES;
                                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_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";
+                               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;
+                               USE_HEADERMAP = NO;
                                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 = {
                                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;
                                MTL_ENABLE_DEBUG_INFO = NO;
                                PRODUCT_NAME = "$(TARGET_NAME)";
                                SDKROOT = macosx.internal;
+                               VERSIONING_SYSTEM = "apple-generic";
                        };
                        name = Release;
                };
                                );
                                INSTALL_PATH = "$(INSTALL_LOCATION)/usr/local/bin";
                                PRODUCT_NAME = dyld_shared_cache_util;
+                               SDKROOT = macosx.internal;
+                               SUPPORTED_PLATFORMS = macosx;
                        };
                        name = Debug;
                };
                                );
                                INSTALL_PATH = "$(INSTALL_LOCATION)/usr/local/bin";
                                PRODUCT_NAME = dyld_shared_cache_util;
+                               SDKROOT = macosx.internal;
                                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;
                };
                        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 = "-";
                                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;
                                        "$(ALIGNMENT)",
                                        "$(ENTRY)",
                                );
-                               SDKROOT = macosx.internal;
                                STRIPFLAGS = "-S";
                                UNSTRIPPED_PRODUCT = NO;
                                VERSIONING_SYSTEM = "apple-generic";
                        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 = "-";
                                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;
                                        "$(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";
                        isa = XCBuildConfiguration;
                        baseConfigurationReference = F971DD151A4A0E0700BBDD52 /* libdyld.xcconfig */;
                        buildSettings = {
-                               ARCHS = "$(ARCHS_STANDARD_32_64_BIT)";
                                CLANG_CXX_LANGUAGE_STANDARD = "c++0x";
                                CLANG_WARN_EMPTY_BODY = YES;
+                               CODE_SIGN_IDENTITY = "-";
                                COMBINE_HIDPI_IMAGES = YES;
+                               CURRENT_PROJECT_VERSION = 500;
                                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;
+                               GENERATE_TEXT_BASED_STUBS = YES;
                                HEADER_SEARCH_PATHS = "$(SRCROOT)/include";
                                INSTALLHDRS_COPY_PHASE = YES;
+                               INSTALLHDRS_SCRIPT_PHASE = YES;
+                               ONLY_ACTIVE_ARCH = NO;
                                OTHER_CFLAGS = "";
+                               OTHER_CPLUSPLUSFLAGS = (
+                                       "$(OTHER_CFLAGS)",
+                                       "-Wglobal-constructors",
+                               );
                                OTHER_LDFLAGS = (
+                                       "-Wl,-no_inits",
                                        "-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;
+                               PUBLIC_HEADERS_FOLDER_PATH = "/usr//include/mach-o";
                                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",
                        isa = XCBuildConfiguration;
                        baseConfigurationReference = F971DD151A4A0E0700BBDD52 /* libdyld.xcconfig */;
                        buildSettings = {
-                               ARCHS = "$(ARCHS_STANDARD_32_64_BIT)";
                                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)";
                                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;
+                               INSTALLHDRS_SCRIPT_PHASE = YES;
                                OTHER_CFLAGS = "";
                                OTHER_CPLUSPLUSFLAGS = (
                                        "-fno-exceptions",
                                        "$(OTHER_CFLAGS)",
                                );
                                OTHER_LDFLAGS = (
+                                       "-Wl,-no_inits",
                                        "-nostdlib",
                                        "$(LIBSYSTEM_LIBS)",
                                        "-umbrella",
                                        System,
                                        "-L$(SDKROOT)/usr/lib/system",
+                                       "-Wl,-no_inits",
                                );
                                "OTHER_LDFLAGS[sdk=iphoneos*]" = (
+                                       "-Wl,-no_inits",
                                        "-nostdlib",
                                        "$(LIBSYSTEM_LIBS)",
                                        "-umbrella",
                                        "-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",
                                );
+                               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;
+                               PUBLIC_HEADERS_FOLDER_PATH = "/usr//include/mach-o";
                                SEPARATE_STRIP = YES;
                                SKIP_INSTALL = NO;
                                STRIP_INSTALLED_PRODUCT = YES;
+                               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",
                        isa = XCBuildConfiguration;
                        baseConfigurationReference = F971DD131A4A0E0700BBDD52 /* base.xcconfig */;
                        buildSettings = {
+                               CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
                                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;
-                               SUPPORTED_PLATFORMS = "iphonesimulator iphoneos macosx watchos appletvos";
                        };
                        name = Debug;
                };
                        isa = XCBuildConfiguration;
                        baseConfigurationReference = F971DD131A4A0E0700BBDD52 /* base.xcconfig */;
                        buildSettings = {
+                               CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
                                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;
-                               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;
                };
                        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 = (
                        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 = (
                        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 = (
                        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 = (
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  
@@ -134,24 +154,25 @@ typedef enum {
 
 typedef struct __NSObjectFileImage*  NSObjectFileImage;
 
+
+
 /* NSObjectFileImage can only be used with MH_BUNDLE files */
-extern NSObjectFileImageReturnCode NSCreateObjectFileImageFromFile(const char* pathName, NSObjectFileImage *objectFileImage)               __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_5,__IPHONE_NA,__IPHONE_NA);
-extern NSObjectFileImageReturnCode NSCreateObjectFileImageFromMemory(const void *address, size_t size, NSObjectFileImage *objectFileImage) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_5,__IPHONE_NA,__IPHONE_NA);
-extern bool                        NSDestroyObjectFileImage(NSObjectFileImage objectFileImage)                                             __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_5,__IPHONE_NA,__IPHONE_NA);
-
-extern uint32_t     NSSymbolDefinitionCountInObjectFileImage(NSObjectFileImage objectFileImage)                   __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_5,__IPHONE_NA,__IPHONE_NA);
-extern const char*  NSSymbolDefinitionNameInObjectFileImage(NSObjectFileImage objectFileImage, uint32_t ordinal)  __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_5,__IPHONE_NA,__IPHONE_NA);
-extern uint32_t     NSSymbolReferenceCountInObjectFileImage(NSObjectFileImage objectFileImage)                    __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_5,__IPHONE_NA,__IPHONE_NA);
-extern const char*  NSSymbolReferenceNameInObjectFileImage(NSObjectFileImage objectFileImage, uint32_t ordinal, bool *tentative_definition) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_5,__IPHONE_NA,__IPHONE_NA);
-extern bool         NSIsSymbolDefinedInObjectFileImage(NSObjectFileImage objectFileImage, const char* symbolName) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_4,__IPHONE_NA,__IPHONE_NA);
-extern void*        NSGetSectionDataInObjectFileImage(NSObjectFileImage objectFileImage, const char* segmentName, const char* sectionName, size_t *size) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_5,__IPHONE_NA,__IPHONE_NA);
-extern bool         NSHasModInitObjectFileImage(NSObjectFileImage objectFileImage)                                __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_3,__MAC_10_5,__IPHONE_NA,__IPHONE_NA);
+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;
-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
@@ -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
 
-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;
-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
-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 {
@@ -207,7 +228,7 @@ typedef enum {
     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);
@@ -216,28 +237,27 @@ typedef struct {
                           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
 
-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
index eaadf6488b8c5e32e81b0f7ea05799c23f11093d..f2495159dceeec1115f88e5d48264cdf5420e431 100644 (file)
 #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_ */
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
-       uintptr_t                                               reserved[12-DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT];
+       uintptr_t                                               reserved[13-DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT];
 #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
@@ -158,7 +160,7 @@ struct dyld_shared_cache_ranges {
                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 <TargetConditionals.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
 //
@@ -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);
 
 
-//
-// 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
 {
@@ -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 
+#if !__USING_SJLJ_EXCEPTIONS__
 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.
 //
-// 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 
@@ -214,20 +204,36 @@ extern uint32_t dyld_get_sdk_version(const struct mach_header* mh);
 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
-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);
+#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.
@@ -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
+       // 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;
 
+
+#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.
@@ -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));
+#endif /* __BLOCKS */
 
 
 //
@@ -389,6 +400,32 @@ struct 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 */
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.
 //
+//
 
 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 {
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:                
@@ -292,6 +293,7 @@ private:
        dyld_cache_accelerator_dof                      fields;
 };
 
+
 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
 
+#ifndef CPU_SUBTYPE_ARM64_E
+       #define CPU_SUBTYPE_ARM64_E    2
+#endif
+
 #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++;
-               result |= ((byte & 0x7f) << bit);
+               result |= (((int64_t)(byte & 0x7f)) << bit);
                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
-    dylib_data.resize(offsetInFatFile+newSize);
+    dylib_data.resize((size_t)(offsetInFatFile+newSize));
     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;
        }
        
-       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;
@@ -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>;
+       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");
-        munmap(mapped_cache, statbuf.st_size);
+        munmap(mapped_cache, (size_t)statbuf.st_size);
                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");
-        munmap(mapped_cache, statbuf.st_size);
+        munmap(mapped_cache, (size_t)statbuf.st_size);
                return result;
     }
 
@@ -602,7 +604,7 @@ int dyld_shared_cache_extract_dylibs_progress(const char* shared_cache_file_path
                 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);
@@ -635,7 +637,7 @@ int dyld_shared_cache_extract_dylibs_progress(const char* shared_cache_file_path
     dispatch_release(group);
     dispatch_release(writer_queue);
     
-    munmap(mapped_cache, statbuf.st_size);
+    munmap(mapped_cache, (size_t)statbuf.st_size);
        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>
-       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;      
@@ -104,13 +104,14 @@ namespace dyld {
                                        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.addressOffset = segInfo.address - cache_unslid_base_address;
                                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()];
+        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) )
@@ -168,7 +170,7 @@ namespace dyld {
                                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;
                }
@@ -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);
+       else if ( strcmp((char*)cache, "dyld_v1  arm64e") == 0 ) 
+                       return dyld::walkImages<arm64>(cache, shared_cache_size, callback);
        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
+       // 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
-       // 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;
 
index fd9d1c2b776a25c9af5dbdb7f712c5821fbce45a..57b5d074afc4621d52271a6d957e89dcc4644fcf 100644 (file)
@@ -54,6 +54,7 @@ struct dyld_cache_header
        uint64_t        imagesTextCount;                // number of dyld_cache_image_text_info entries
 };
 
+
 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
 };
 
-
 // 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";
-#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
@@ -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);
-       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);
        }
-       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);
@@ -549,6 +550,39 @@ int main (int argc, const char* argv[]) {
                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());
@@ -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);
-               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);
@@ -756,7 +790,7 @@ int main (int argc, const char* argv[]) {
                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:
@@ -840,7 +874,8 @@ int main (int argc, const char* argv[]) {
                                        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>;
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];
-#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;
                        }
-#endif
                        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);
-       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;
-       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 DYLD_SHARED_CACHE_SUPPORT               (__arm__ || __arm64__)
        #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 DYLD_SHARED_CACHE_SUPPORT               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; }
+    dev_t                               getDevice() const { return fDevice; }
 
                                                                                // 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 "Tracing.h"
 #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
 
+#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"
@@ -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->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 ) {
@@ -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";
-       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 ) {
@@ -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 build_version_command* buildVersCmd;
        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_BUILD_VERSION:
+                               buildVersCmd = (build_version_command*)cmd;
+                               return buildVersCmd->sdk;
                }
                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 build_version_command* buildVersCmd;
        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_BUILD_VERSION:
+                               buildVersCmd = (build_version_command*)cmd;
+                               return buildVersCmd->minos;
                }
                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;
-                       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);
@@ -2004,7 +2050,7 @@ struct DATAdyld {
 
 // 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)
 {
@@ -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());
-                                       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);
@@ -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);
-                                                       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
@@ -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) {
-               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;
@@ -2569,8 +2619,9 @@ const char* ImageLoaderMachO::findClosestSymbol(const mach_header* mh, const voi
                                        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;
+                }
                                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) {
-                       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) {
-                                       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
@@ -459,7 +459,7 @@ ImageLoaderMachOClassic::mapSplitSegDylibOutsideSharedRegion(int fd,
        }
        
        // 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) ) {
@@ -467,7 +467,7 @@ ImageLoaderMachOClassic::mapSplitSegDylibOutsideSharedRegion(int fd,
                }
                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;
@@ -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();
-       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) {
index fb3c04e25f723cd43503e27e8322c422dba08f4a..020ec51fcd87f62f20e1d9eb2f6bf68b4f24c07a 100644 (file)
@@ -35,6 +35,7 @@
 #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> 
        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, 
@@ -150,6 +157,10 @@ ImageLoaderMachOCompressed* ImageLoaderMachOCompressed::instantiateFromFile(cons
                // 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()) {
@@ -838,8 +849,45 @@ void ImageLoaderMachOCompressed::doBindJustLazies(const LinkContext& context)
        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)
 {
+#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;
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);
-       
+
        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 "Tracing.h"
 #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
@@ -77,9 +80,9 @@ extern void addImagesToAllImages(uint32_t infoCount, const dyld_image_info info[
 #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 {
@@ -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)
 {
@@ -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;
-       dyldSection->vars.mh = context.mainExecutable->machHeader();
+       dyldSection->vars.mh = mainMH;
        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);
+               rpathsFromMainExecutable.push_back("/System/Library/Frameworks/");
                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);
 }
@@ -839,7 +843,7 @@ void ImageLoaderMegaDylib::recursiveSpinLockRelease(unsigned int imageIndex, mac
 
 
 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;
@@ -858,7 +862,9 @@ void ImageLoaderMegaDylib::recursiveInitialization(const LinkContext& context, m
                        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
@@ -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);
-                               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 ) {
@@ -904,11 +912,20 @@ void ImageLoaderMegaDylib::recursiveInitialization(const LinkContext& context, m
 
 
 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) ) {
-               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;
-
        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 )
@@ -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();
-               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);
        }
index 425ec8e7b17d76c713833ff9a427114288c1f2d8..6046da63c653c46b71c36174769b12ea06deec42 100644 (file)
@@ -39,7 +39,7 @@
 //
 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();
@@ -185,7 +185,13 @@ protected:
        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;
@@ -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,
-                                                                                                                               InitializerTimingList& timingInfo);
+                                                                                                                               InitializerTimingList& timingInfo, UpwardIndexes&);
        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 <mach/mach.h>
 #include <mach-o/fat.h>
 #include <mach-o/loader.h> 
 #include <mach-o/ldsyms.h> 
 #include <sandbox.h>
 #include <sandbox/private.h>
 
+extern "C" int __fork();
+
 #include <array>
+#include <algorithm>
+#include <vector>
 
 #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 
 
+#ifndef CPU_SUBTYPE_ARM64_E
+       #define CPU_SUBTYPE_ARM64_E    2
+#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"
-#if DYLD_SHARED_CACHE_SUPPORT
 #include "dyld_cache_format.h"
-#endif
 #include "dyld_process_info_internal.h"
 #include <coreSymbolicationDyldSupport.h>
 #if TARGET_IPHONE_SIMULATOR
        #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
-#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
@@ -213,8 +231,7 @@ struct EnvironmentVariables {
        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
@@ -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 uintptr_t                                       sMainExecutableSlide = 0;
 #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 };
+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
-static const char*                                     sRestrictedFrameworkFallbackPaths[] = { "/System/Library/Frameworks", NULL };
-static const char*                                     sRestrictedLibraryFallbackPaths[] = { "/usr/lib", NULL };
 static UndefinedHandler                                sUndefinedHandler = NULL;
 static ImageLoader*                                    sBundleBeingLoaded = NULL;      // hack until OFI is reworked
-#if DYLD_SHARED_CACHE_SUPPORT
-static const dyld_cache_header*                sSharedCache = NULL;
-static long                                                    sSharedCacheSlide = 0;
-static bool                                                    sSharedCacheIgnoreInodeAndTimeStamp = false;
-          bool                                                 gSharedCacheOverridden = false;
-#if __IPHONE_OS_VERSION_MIN_REQUIRED
-       static const char*                              sSharedCacheDir = IPHONE_DYLD_SHARED_CACHE_DIR;
-       #define ENABLE_DYLIBS_TO_OVERRIDE_CACHE_SIZE 1024
-#else
-       static const char*                              sSharedCacheDir = MACOSX_DYLD_SHARED_CACHE_DIR;
-#endif
-#endif
+static dyld3::SharedCacheLoadInfo      sSharedCacheLoadInfo;
+static const char*                                     sSharedCacheOverrideDir;
+       bool                                                    gSharedCacheOverridden = false;
 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;
-#if __x86_64__ && DYLD_SHARED_CACHE_SUPPORT
+#if __x86_64__ && !TARGET_IPHONE_SIMULATOR
 static bool                                                    sHaswell = false;
 #endif
 static std::vector<ImageLoader::DynamicReference> sDynamicReferences;
@@ -322,12 +331,15 @@ static bool                                                       sForceStderr = 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
@@ -557,7 +569,8 @@ void warn(const char* format, ...)
        va_end(list);
 }
 
-
+#else
+       extern void vlog(const char* format, va_list list);
 #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];
-
+static bool        sZombieNotifiers[DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT];
 
 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 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.
@@ -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);
-       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
@@ -832,28 +848,35 @@ static void notifyMonitoringDyld(bool unloading, unsigned portSlot, unsigned ima
                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) {
-               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");
-                       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]);
@@ -862,44 +885,24 @@ static void notifyMonitoringDyldMain()
                                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() ) {
+               uint32_t baseCode = loading ? DBG_DYLD_UUID_MAP_A : DBG_DYLD_UUID_UNMAP_A;
+               uuid_t uuid;
                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)
@@ -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;
+                               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);
-       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();
@@ -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)
-                                       queueKernelNotification(*image, true, kernelInfos, kernelInfoCount);
+                                       notifyKernel(*image, true);
                                // 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 ) {
@@ -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)
-                                               (*callback)(infos[imageCount+i].imageLoadAddress, sSharedCacheSlide);
+                                               (*callback)(infos[imageCount+i].imageLoadAddress, sSharedCacheLoadInfo.slide);
                                }
                        }
                        imageCount += cacheCount;
@@ -1792,6 +1792,8 @@ static void paths_dump(const char **paths)
 }
 #endif
 
+
+
 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");
                }
        }
-#if DYLD_SHARED_CACHE_SUPPORT
        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;
@@ -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);
                }
        }
+       else if ( (strcmp(key, "DYLD_SKIP_MAIN") == 0)) {
+               if ( dyld3::loader::internalInstall() )
+                       sSkipMain = true;
+       }
 #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
 
+    // 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++) {
+               
            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
 }
 
-#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;
@@ -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 __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;
@@ -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__
-       #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 ) {
@@ -2288,12 +2309,21 @@ static void getHostInfo(const macho_header* mainExecutableMH, uintptr_t mainExec
 #endif
 }
 
-static void checkSharedRegionDisable()
+static void checkSharedRegionDisable(const mach_header* mainExecutableMH)
 {
 #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
-       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");
@@ -2305,7 +2335,7 @@ static void checkSharedRegionDisable()
        }
 #endif
 #endif
-       // iPhoneOS cannot run without shared region
+       // iOS cannot run without shared region
 }
 
 bool validImage(const ImageLoader* possibleImage)
@@ -2503,6 +2533,21 @@ static const cpu_subtype_t kARM[kARM_RowCount][9] = {
 };
 #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
@@ -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
-       { 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
-       { 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
@@ -2532,6 +2577,14 @@ static const cpu_subtype_t* findCPUSubtypeList(cpu_type_t cpu, cpu_subtype_t sub
                        }
                        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) {
@@ -2595,6 +2648,15 @@ static bool fatFindRunsOnAllCPUs(cpu_type_t cpu, const fat_header* fh, uint64_t*
                                        }
                                        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 ) {
@@ -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
-       if ( (cpu & CPU_TYPE_MASK) == sHostCPU ) {
+       if ( cpu == sHostCPU ) {
                // 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 ) {
-                       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 ) {
@@ -2789,104 +2851,47 @@ static ImageLoaderMachO* instantiateFromLoadedImage(const macho_header* mh, uint
        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()
 {
-       uint32_t devFlags = *((uint32_t*)_COMM_PAGE_DEV_FIRM);
-       if ( (devFlags & 1) == 0 )
+       if ( !dyld3::loader::internalInstall() )
                return false;
-       return ( (sSharedCache != NULL) && (sSharedCache->cacheType == kDyldSharedCacheTypeDevelopment) );
+       return ( (sSharedCacheLoadInfo.loadAddress != nullptr) && (sSharedCacheLoadInfo.loadAddress->header.cacheType == kDyldSharedCacheTypeDevelopment) );
 }
 #endif
 
-static bool findInSharedCacheImage(const char* path, bool searchByPath, const struct stat* stat_buf, const macho_header** mh, const char** pathInCache, long* slide)
+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)
 {
-       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)
 {
@@ -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 ) {
-               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;
        } 
@@ -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);
-       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;
        }
-  #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;
-                       if ( (const macho_header*)anImage->machHeader() == mhInCache )
+                       if ( (const mach_header*)anImage->machHeader() == shareCacheResults.mhInCache )
                                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;
-               // 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) )
@@ -3250,8 +3204,6 @@ static ImageLoader* loadPhase5load(const char* path, const char* orgPath, const
        }
        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)
@@ -3581,15 +3533,13 @@ static ImageLoader* loadPhase0(const char* path, const char* orgPath, const Load
        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
@@ -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));
                }
-#if DYLD_SHARED_CACHE_SUPPORT
                // if loaded image is not from cache, but original path is in cache
                // set gSharedCacheOverridden flag to disable some ObjC optimizations
                if ( !gSharedCacheOverridden && !image->inSharedCache() && image->isDylib() && cacheablePath(path) && inSharedCache(path) ) {
                        gSharedCacheOverridden = true;
                }
-#endif
                return image;
        }
        else if ( exceptions.size() == 0 ) {
@@ -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
+       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
@@ -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) {
-                       (*func)(infos[i].imageLoadAddress, sSharedCacheSlide);
+                       (*func)(infos[i].imageLoadAddress, sSharedCacheLoadInfo.slide);
                }
        }
 #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
        }
+
+       // <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)
 {
-#if DYLD_SHARED_CACHE_SUPPORT
-       if ( sSharedCache == NULL )
+       if ( sSharedCacheLoadInfo.loadAddress == nullptr )
                return false;
 
-       memcpy(uuid, sSharedCache->uuid, 16);
+       sSharedCacheLoadInfo.loadAddress->getUUID(uuid);
        return true;
-#else
-       return false;
-#endif
 }
 
 #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;
-#if DYLD_SHARED_CACHE_SUPPORT
        gLinkContext.inSharedCache                      = &inSharedCache;
-#endif
        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.sharedRegionMode           = ImageLoader::kDontUseSharedRegion;
+       gLinkContext.sharedRegionMode           = ImageLoader::kUsePrivateSharedRegion;
 #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.
 //
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
 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;
 }
+#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;
@@ -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) ) {
-       #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;
@@ -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;
-       #else
-               return false;
-       #endif
        }
 
        // check mach-o header
@@ -5351,13 +4655,13 @@ static void loadInsertedDylib(const char* path)
 //
 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;
+    uint32_t flags;
        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);
+    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 ) {
@@ -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;
@@ -5452,14 +4762,7 @@ void notifyKernelAboutDyld()
                        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;
                        }
                }
@@ -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 = {
-               7,
+               8,
                // 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_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))
@@ -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 "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
@@ -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);
 
-
        // 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
 
+// 
+// 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
@@ -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)
 {
+       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;
+       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 ) {
-               // 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]; 
@@ -5800,6 +5678,7 @@ _main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide,
                        sExecPath = s;
                }
        }
+
        // 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);
+
+       // 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);
@@ -5847,10 +5816,14 @@ _main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide,
        try {
                // add dyld itself to UUID list
                addDyldImageToUUIDList();
-               notifyKernelAboutDyld();
 
 #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
@@ -5900,23 +5873,6 @@ reloadAllImages:
                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
@@ -6036,16 +5992,15 @@ reloadAllImages:
                // <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 ( (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];
-                       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) {
-                                       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;
@@ -6056,7 +6011,6 @@ reloadAllImages:
                                gProcessInfo->notification(dyld_image_adding, count, info);
                        }
                }
-       #endif
 
                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()
+               dyld3::kdebug_trace_dyld_signpost(DBG_DYLD_SIGNPOST_START_MAIN_DYLD2, 0, 0);
                notifyMonitoringDyldMain();
 
                // find entry point for main executable
@@ -6095,12 +6050,17 @@ reloadAllImages:
        }
 
        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;
 }
 
 
-
 } // 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
-_dyld_shared_cache_ranges
 __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 DYLD_SHARED_CACHE_SUPPORT
        extern bool                                                             gSharedCacheOverridden;
-#endif
        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 int                                      openSharedCacheFile();
        extern const void*                      imMemorySharedCacheHeader();
        extern uintptr_t                        fastBindLazySymbol(ImageLoader** imageLoaderCache, uintptr_t lazyBindingInfoOffset);
-#if DYLD_SHARED_CACHE_SUPPORT
        extern bool                                     inSharedCache(const char* path);
-#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 "DyldSharedCache.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);
 
@@ -67,6 +74,7 @@ extern uint32_t allImagesCount();
 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   
@@ -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 },
-#if DYLD_SHARED_CACHE_SUPPORT
        {"__dyld_shared_cache_file_path",                                       (void*)dyld::getStandardSharedCacheFilePath },
-#endif
     {"__dyld_get_image_header_containing_address",             (void*)dyld_image_header_containing_address },
     {"__dyld_is_memory_immutable",                                             (void*)_dyld_is_memory_immutable },
     {"__dyld_objc_notify_register",                                            (void*)_dyld_objc_notify_register },
@@ -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_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 },
@@ -198,7 +203,6 @@ static struct dyld_func dyld_funcs[] = {
     {"__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 },
@@ -734,14 +738,6 @@ bool _dyld_all_twolevel_modules_prebound(void)
        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 )
@@ -849,8 +845,8 @@ NSObjectFileImageReturnCode NSCreateObjectFileImageFromMemory(const void* addres
 
 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;
        }
@@ -905,13 +901,6 @@ bool NSDestroyObjectFileImage(NSObjectFileImage objectFileImage)
        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 )
@@ -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       
-       const int ofiCount = sObjectFileImages.size();
+       const size_t ofiCount = sObjectFileImages.size();
        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;
@@ -1346,12 +1335,10 @@ bool dlopen_preflight(const char* path)
                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;
-#endif
        
        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) ) {
-                       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
@@ -1950,11 +1938,7 @@ const char* dyld_image_path_containing_address(const void* address)
 
 bool dyld_shared_cache_some_image_overridden()
 {
- #if DYLD_SHARED_CACHE_SUPPORT
        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;
 
-#if DYLD_SHARED_CACHE_SUPPORT
        // quick check to see if in r/o region of shared cache.  If so return true.
-       if ( dyld_shared_cache_ranges.sharedRegionsCount > 2 ) {
-               uintptr_t roStart    = dyld_shared_cache_ranges.ranges[0].start;
-               uintptr_t roEnd      = roStart + dyld_shared_cache_ranges.ranges[0].length;
+    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;
-       }
-#endif
+    }
 
        // 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)
 {
-#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 "../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);
 
+//
+// 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
@@ -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
 
+#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   
@@ -142,6 +195,9 @@ const char* libraryName)
 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);
@@ -159,6 +215,9 @@ const char*
 NSNameOfModule(
 NSModule module)
 {
+       if ( gUseDyld3 )
+               return dyld3::NSNameOfModule(module);
+
        DYLD_LOCK_THIS_BLOCK;
     static const char*  (*p)(NSModule module) = NULL;
 
@@ -171,6 +230,9 @@ const char*
 NSLibraryNameForModule(
 NSModule module)
 {
+       if ( gUseDyld3 )
+               return dyld3::NSLibraryNameForModule(module);
+
        DYLD_LOCK_THIS_BLOCK;
     static const char*  (*p)(NSModule module) = NULL;
 
@@ -183,6 +245,9 @@ bool
 NSIsSymbolNameDefined(
 const char* symbolName)
 {
+       if ( gUseDyld3 )
+               return dyld3::NSIsSymbolNameDefined(symbolName);
+
        DYLD_LOCK_THIS_BLOCK;
     static bool (*p)(const char* symbolName) = NULL;
 
@@ -196,6 +261,9 @@ NSIsSymbolNameDefinedWithHint(
 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;
@@ -210,6 +278,9 @@ NSIsSymbolNameDefinedInImage(
 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;
@@ -223,6 +294,9 @@ NSSymbol
 NSLookupAndBindSymbol(
 const char* symbolName)
 {
+       if ( gUseDyld3 )
+               return dyld3::NSLookupAndBindSymbol(symbolName);
+
        DYLD_LOCK_THIS_BLOCK;
     static NSSymbol (*p)(const char* symbolName) = NULL;
 
@@ -236,6 +310,9 @@ NSLookupAndBindSymbolWithHint(
 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;
@@ -250,6 +327,9 @@ NSLookupSymbolInModule(
 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;
 
@@ -264,8 +344,11 @@ const struct mach_header *image,
 const char* symbolName,
 uint32_t options)
 {
+       if ( gUseDyld3 )
+               return dyld3::NSLookupSymbolInImage(image, symbolName, options);
+
        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;
 
@@ -278,6 +361,9 @@ const char*
 NSNameOfSymbol(
 NSSymbol symbol)
 {
+       if ( gUseDyld3 )
+               return dyld3::NSNameOfSymbol(symbol);
+
        DYLD_LOCK_THIS_BLOCK;
     static char * (*p)(NSSymbol symbol) = NULL;
 
@@ -290,6 +376,9 @@ void *
 NSAddressOfSymbol(
 NSSymbol symbol)
 {
+       if ( gUseDyld3 )
+               return dyld3::NSAddressOfSymbol(symbol);
+
        DYLD_LOCK_THIS_BLOCK;
     static void * (*p)(NSSymbol symbol) = NULL;
 
@@ -302,6 +391,9 @@ NSModule
 NSModuleForSymbol(
 NSSymbol symbol)
 {
+       if ( gUseDyld3 )
+               return dyld3::NSModuleForSymbol(symbol);
+
        DYLD_LOCK_THIS_BLOCK;
     static NSModule (*p)(NSSymbol symbol) = NULL;
 
@@ -314,6 +406,9 @@ bool
 NSAddLibrary(
 const char* pathName)
 {
+       if ( gUseDyld3 )
+               return dyld3::NSAddLibrary(pathName);
+
        DYLD_LOCK_THIS_BLOCK;
     static bool (*p)(const char* pathName) = NULL;
 
@@ -326,6 +421,9 @@ bool
 NSAddLibraryWithSearching(
 const char* pathName)
 {
+       if ( gUseDyld3 )
+               return dyld3::NSAddLibrary(pathName);
+
        DYLD_LOCK_THIS_BLOCK;
     static bool (*p)(const char* pathName) = NULL;
 
@@ -339,6 +437,9 @@ NSAddImage(
 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;
@@ -362,6 +463,9 @@ uint32_t options)
  */
 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;
@@ -400,6 +504,9 @@ int32_t NSVersionOfLinkTimeLibrary(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);
@@ -429,7 +536,7 @@ int32_t NSVersionOfRunTimeLibrary(const char* libraryName)
 #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 )
@@ -447,15 +554,37 @@ static bool getVersionLoadCommandInfo(const mach_header* mh, uint32_t* loadComma
                        return 0;
                }
                const version_min_command* versCmd;
+               const build_version_command* buildVersCmd;
                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:
+                               versCmd = (version_min_command*)cmd;
+                               *platform       = PLATFORM_MACOS;
+                               *minOS          = versCmd->version;
+                               *sdk            = versCmd->sdk;
+                               return true;
                        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;
-                               *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;
@@ -589,27 +718,71 @@ static uint32_t watchVersToIOSVers(uint32_t vers)
 
 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();
-       uint32_t loadCommand;
+       uint32_t platform;
        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()
+{
+       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();
-       uint32_t loadCommand;
+       uint32_t platform;
        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;
@@ -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 loadCommand;
+       if ( gUseDyld3 )
+               return dyld3::dyld_get_sdk_version(mh);
+
+    uint32_t platform;
        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);
-                       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
-                       case LC_VERSION_MIN_TVOS:
-                       case LC_VERSION_MIN_IPHONEOS:
+                       case PLATFORM_TVOS:
+                       case PLATFORM_IOS:
                                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
-                       case LC_VERSION_MIN_MACOSX:
+                       case PLATFORM_MACOS:
                                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
@@ -670,33 +853,46 @@ uint32_t dyld_get_sdk_version(const mach_header* mh)
 
 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)
 {
-       uint32_t loadCommand;
+       if ( gUseDyld3 )
+               return dyld3::dyld_get_min_os_version(mh);
+
+       uint32_t platform;
        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);
-                       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
-                       case LC_VERSION_MIN_TVOS:
-                       case LC_VERSION_MIN_IPHONEOS:
+                       case PLATFORM_TVOS:
+                       case PLATFORM_IOS:
                                return minOS;
 #elif __IPHONE_OS_VERSION_MIN_REQUIRED
-                       case LC_VERSION_MIN_IPHONEOS:
+                       case PLATFORM_IOS:
                                return minOS;
 #else
-                       case LC_VERSION_MIN_MACOSX:
+                       case PLATFORM_MACOS:
                                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()
 {
+       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)
 {
+       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));
@@ -754,6 +956,9 @@ NSCreateObjectFileImageFromFile(
 const char* pathName,
 NSObjectFileImage *objectFileImage)
 {
+       if ( gUseDyld3 )
+               return dyld3::NSCreateObjectFileImageFromFile(pathName, objectFileImage);
+
        DYLD_LOCK_THIS_BLOCK;
     static NSObjectFileImageReturnCode (*p)(const char*, NSObjectFileImage*) = NULL;
 
@@ -776,6 +981,9 @@ const void* address,
 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;
 
@@ -809,6 +1017,9 @@ bool
 NSDestroyObjectFileImage(
 NSObjectFileImage objectFileImage)
 {
+       if ( gUseDyld3 )
+               return dyld3::NSDestroyObjectFileImage(objectFileImage);
+
        DYLD_LOCK_THIS_BLOCK;
     static bool (*p)(NSObjectFileImage) = NULL;
 
@@ -824,6 +1035,9 @@ NSObjectFileImage objectFileImage,
 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;
 
@@ -844,6 +1058,9 @@ uint32_t
 NSSymbolDefinitionCountInObjectFileImage(
 NSObjectFileImage objectFileImage)
 {
+       if ( gUseDyld3 )
+               return dyld3::NSSymbolDefinitionCountInObjectFileImage(objectFileImage);
+
        DYLD_LOCK_THIS_BLOCK;
     static uint32_t (*p)(NSObjectFileImage) = NULL;
 
@@ -864,6 +1081,9 @@ NSSymbolDefinitionNameInObjectFileImage(
 NSObjectFileImage objectFileImage,
 uint32_t ordinal)
 {
+       if ( gUseDyld3 )
+               return dyld3::NSSymbolDefinitionNameInObjectFileImage(objectFileImage, ordinal);
+
        DYLD_LOCK_THIS_BLOCK;
     static const char*  (*p)(NSObjectFileImage, uint32_t) = NULL;
 
@@ -881,6 +1101,9 @@ uint32_t
 NSSymbolReferenceCountInObjectFileImage(
 NSObjectFileImage objectFileImage)
 {
+       if ( gUseDyld3 )
+               return dyld3::NSSymbolReferenceCountInObjectFileImage(objectFileImage);
+
        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 */
 {
+       if ( gUseDyld3 )
+               return dyld3::NSSymbolReferenceNameInObjectFileImage(objectFileImage, ordinal, tentative_definition);
+
        DYLD_LOCK_THIS_BLOCK;
     static const char*  (*p)(NSObjectFileImage, uint32_t, bool*) = NULL;
 
@@ -920,6 +1146,9 @@ NSIsSymbolDefinedInObjectFileImage(
 NSObjectFileImage objectFileImage,
 const char* symbolName)
 {
+       if ( gUseDyld3 )
+               return dyld3::NSIsSymbolDefinedInObjectFileImage(objectFileImage, symbolName);
+
        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 */
 {
+       if ( gUseDyld3 )
+               return dyld3::NSGetSectionDataInObjectFileImage(objectFileImage, segmentName, sectionName, size);
+
        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)
 {
+       if ( gUseDyld3 )
+               return dyld3::NSLinkEditError(c, errorNumber, fileName, errorString);
+
        DYLD_LOCK_THIS_BLOCK;
     static void (*p)(NSLinkEditErrors *c,
                     int *errorNumber, 
@@ -977,6 +1212,9 @@ NSUnLinkModule(
 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;
 
@@ -1014,6 +1252,9 @@ _NSGetExecutablePath(
 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;
 
@@ -1106,6 +1347,9 @@ void
 _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;
@@ -1124,6 +1368,9 @@ void
 _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;
@@ -1207,21 +1454,6 @@ unsigned long *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
@@ -1236,6 +1468,9 @@ _dyld_present(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;
 
@@ -1247,6 +1482,9 @@ _dyld_image_count(void)
 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;
 
@@ -1258,6 +1496,9 @@ _dyld_get_image_header(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;
 
@@ -1269,6 +1510,9 @@ _dyld_get_image_vmaddr_slide(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;
 
@@ -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)
 {
+       if ( gUseDyld3 )
+               return dyld3::_dyld_get_image_slide(mh);
+
        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)
 {
+       if ( gUseDyld3 )
+               return dyld3::_dyld_image_containing_address(address);
+
        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)
 {
+       if ( gUseDyld3 )
+               return dyld3::_dyld_get_image_header_containing_address(address);
+
        DYLD_LOCK_THIS_BLOCK;
     static const struct mach_header * (*p)(const void*) = NULL;
 
@@ -1312,8 +1566,6 @@ const void* address)
        return p(address);
 }
 
-
-#if DEPRECATED_APIS_SUPPORTED
 bool _dyld_launched_prebound(void)
 {
        DYLD_LOCK_THIS_BLOCK;
@@ -1407,7 +1659,6 @@ static bool isLaunchdOwned()
        return ( val != 0 );
 }
 
-#if DYLD_SHARED_CACHE_SUPPORT
 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
 }
-#endif // DYLD_SHARED_CACHE_SUPPORT
 
 
 // the table passed to dyld containing thread helpers
 static dyld::LibSystemHelpers sHelpers = { 13, &dyldGlobalLockAcquire, &dyldGlobalLockRelease,
                                                                        &getPerThreadBufferFor_dlerror, &malloc, &free, &__cxa_atexit,
-                                               #if DYLD_SHARED_CACHE_SUPPORT
                                                                        &shared_cache_missing, &shared_cache_out_of_date,
-                                               #else
-                                                                       NULL, NULL,
-                                               #endif
                                                                        NULL, NULL,
                                                                        &pthread_key_create, &pthread_setspecific,
                                                                        &malloc_size,
@@ -1447,21 +1693,28 @@ static dyld::LibSystemHelpers sHelpers = { 13, &dyldGlobalLockAcquire, &dyldGlob
 // and call dyld, registering the helper functions.
 //
 extern "C" void tlv_initializer();
-extern "C" void _dyld_initializer();
 void _dyld_initializer()
 {      
    void (*p)(dyld::LibSystemHelpers*);
 
-       _dyld_func_lookup("__dyld_register_thread_helpers", (void**)&p);
-       if(p != NULL)
-               p(&sHelpers);
-               
+       if ( gUseDyld3 ) {
+               dyld3::gAllImages.applyInitialImages();
+       }
+       else {
+               _dyld_func_lookup("__dyld_register_thread_helpers", (void**)&p);
+               if(p != NULL)
+                       p(&sHelpers);
+       }
+
        tlv_initializer();
 }
 
 
 char* dlerror()
 {
+       if ( gUseDyld3 )
+               return dyld3::dlerror();
+
        DYLD_LOCK_THIS_BLOCK;
     static char* (*p)() = NULL;
 
@@ -1472,6 +1725,9 @@ char* dlerror()
 
 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;
 
@@ -1482,6 +1738,9 @@ int dladdr(const void* addr, Dl_info* info)
 
 int dlclose(void* handle)
 {
+       if ( gUseDyld3 )
+               return dyld3::dlclose(handle);
+
        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)
 {      
+       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;
        
@@ -1510,6 +1772,9 @@ void* dlopen(const char* path, int mode)
 
 bool dlopen_preflight(const char* path)
 {
+       if ( gUseDyld3 )
+               return dyld3::dlopen_preflight(path);
+
        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)
 {
+       if ( gUseDyld3 )
+               return dyld3::dlsym(handle, symbol);
+
        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));
 }
 
-
 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;
 
@@ -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 ( gUseDyld3 )
+               return dyld3::_dyld_find_unwind_sections(addr, info);
+
        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)
 {
+       if ( gUseDyld3 )
+               return dyld3::dyld_image_path_containing_address(addr);
+
        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)
 {
+       if ( gUseDyld3 )
+               return dyld3::dyld_image_header_containing_address(addr);
+
        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()
 {
+       if ( gUseDyld3 )
+               return dyld3::dyld_shared_cache_some_image_overridden();
+
        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)
 {
+       if ( gUseDyld3 )
+               return dyld3::_dyld_get_shared_cache_uuid(uuid);
+
        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)
 {
+       if ( gUseDyld3 )
+               return dyld3::_dyld_get_shared_cache_range(length);
+
        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()
 {
+       if ( gUseDyld3 )
+               return dyld3::dyld_process_is_restricted();
+
        DYLD_NO_LOCK_THIS_BLOCK;
     static bool (*p)() = NULL;
        
@@ -1628,9 +1919,11 @@ bool dyld_process_is_restricted()
        return p();
 }
 
-#if DYLD_SHARED_CACHE_SUPPORT
 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;
        
@@ -1638,10 +1931,12 @@ const char* dyld_shared_cache_file_path()
            _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)
 {
+       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;
 
@@ -1654,6 +1949,9 @@ void dyld_dynamic_interpose(const struct mach_header* mh, const struct dyld_inte
 // SPI called __fork
 void _dyld_fork_child()
 {
+       if ( gUseDyld3 )
+               return dyld3::_dyld_fork_child();
+
        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))
 {
+       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();
-       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
-               cacheHeader = (dyld_cache_header*)(SHARED_REGION_BASE + allInfo->sharedCacheSlide);
+               cacheHeader = (dyld_cache_header*)(allInfo->sharedCacheBaseAddress);
                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
+       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;
-               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.textSegmentOffset = p->loadAddress - cacheUnslidBaseAddress;
                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))
 {
+       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);
 }
@@ -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)
 {
+       if ( gUseDyld3 )
+               return dyld3::_dyld_is_memory_immutable(addr, length);
+
        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)
 {
+       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;
 
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_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;
 
 
index f13064d27362c2535fd6a4a046c64791533219b6..5f4e7b5ccea3074131214642e4c9eb910e412040 100644 (file)
@@ -36,6 +36,8 @@
 #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
index 51f7ad4ffe920befd36450c2fd233d241a8bed79..c6ed46addfa109af71fcf381e480b51b29f7e2ce 100644 (file)
 #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()
@@ -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();
-    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) {
@@ -181,7 +240,7 @@ dyld_process_info_base* dyld_process_info_base::make(task_t task, const dyld_all
             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) ) {
@@ -212,11 +271,13 @@ dyld_process_info_base* dyld_process_info_base::makeSuspended(task_t task, kern_
         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];
+    __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;
@@ -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;
-        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
-            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, "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 ) {
-        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);
@@ -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 ) {
-        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);
@@ -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)
 {
-    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;
@@ -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 {
-        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++;
@@ -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 kr;
+    __block kern_return_t kr = KERN_SUCCESS;
     _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;
     }
 
-    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;
@@ -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
-        
+
     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        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 {
@@ -91,8 +93,10 @@ struct dyld_all_image_infos_64 {
     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 {
index 760f06c558eed83e21b3004802db76b68b7c3746..6d5eee3afaf9d1c6c2acac78b8181c641317e17a 100644 (file)
 #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)();
@@ -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);
-                                                                                       ~dyld_process_info_notify_base();
+                                                                               ~dyld_process_info_notify_base();
 
     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();
-    void*               operator new (size_t, void* buf) { return buf; }
 
        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)
 {
-    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 )
@@ -123,7 +130,7 @@ dyld_process_info_notify_base* dyld_process_info_notify_base::make(task_t task,
     return obj;
 
 fail:
-    free(obj);
+    delete obj;
     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;
-    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");
 }
 
+// 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;
 
@@ -425,6 +457,16 @@ void _ZN4dyld3logEPKcz(const char* format, ...) {
        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);
@@ -708,6 +750,30 @@ kern_return_t      task_register_dyld_get_process_state(task_t task, dyld_kernel_proc
        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();
 } 
@@ -735,5 +801,20 @@ int myerrno_fallback = 0;
 #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);
        }
+    // no thread local storage in image: should never happen
+    if ( size == 0 )
+        return NULL;
        
        // 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.
@@ -394,7 +397,7 @@ void _tlv_atexit(TermFunc func, void* objAddr)
         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]);
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)
-    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 = ""
+    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, "")
+    else:
+        minOSOption = "mmacosx-version-min"
     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:
-            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"):
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 ) {
-        printf("[FAIL] dladdr(&main, xx) failed");
+        printf("[FAIL] dladdr(&main, xx) failed\n");
         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;
     }
 
index d047353f2dea02492c6783567dcadc4bf9acb8ad..eeeabca8a57ffca2e34d18ac2ea6d4d4bf0a92a6 100644 (file)
@@ -30,19 +30,19 @@ static void verifybar()
 {
     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 ) {
-        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) {
-        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) ) {
-        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);
     }
 }
@@ -52,19 +52,19 @@ static void verifyfoo()
 {
     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 ) {
-        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) {
-        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) ) {
-        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);
     }
 }
@@ -74,19 +74,19 @@ static void verifyhide()
 {
     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 ) {
-        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) {
-        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) ) {
-        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);
     }
 }
@@ -96,19 +96,19 @@ static void verifymalloc()
 {
     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 ) {
-        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) {
-        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) ) {
-        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);
     }
 }
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()
 {
-    task_suspend(mach_task_self());
+    (void)kill(getpid(), SIGSTOP);
     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 <Availability.h>
 
 
 extern char** environ;
@@ -29,9 +30,14 @@ extern char** environ;
     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);
@@ -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 };
-    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);
     }
-    //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");
-        kill(childPid, SIGKILL);
+        kill(child.pid, SIGKILL);
         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;
-        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 );
 
-    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)
@@ -127,43 +144,55 @@ static bool hasCF(task_t task, bool launchedSuspended)
 
 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);
     }
+
     const char* testProgPath = argv[1];
-    task_t childTask;
+    struct task_and_pid child;
 
     // 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");
-        task_terminate(childTask);
+        killTest(child);
         exit(0);
     }
-    task_terminate(childTask);
-    
+    killTest(child);
+
     // 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");
-        task_terminate(childTask);
+        killTest(child);
         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");
-        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);
     }
-    task_terminate(childTask);
+    (void)kill(child.pid, SIGCONT);
+    killTest(child);
 #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 <Availability.h>
 
 
 extern char** environ;
@@ -32,10 +33,15 @@ extern char** environ;
     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);
-    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);
@@ -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 };
-    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);
     }
-    //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");
-        kill(childPid, SIGKILL);
+        (void)kill(child.pid, SIGKILL);
         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)
@@ -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;
@@ -105,7 +117,7 @@ static bool monitor(task_t task, bool disconnectEarly, bool attachLate)
     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;
@@ -142,20 +154,23 @@ static bool monitor(task_t task, bool disconnectEarly, bool attachLate)
         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
-    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);
@@ -167,29 +182,33 @@ static bool monitor(task_t task, bool disconnectEarly, bool attachLate)
         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 ) {
@@ -216,13 +235,13 @@ static bool monitor(task_t task, bool disconnectEarly, bool attachLate)
     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;
-        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],
@@ -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);
-                task_terminate(task);
+                killTest(tp);
                 exit(0);
             }
         }
@@ -265,55 +284,55 @@ int main(int argc, const char* argv[])
     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
-        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");
-            task_terminate(childTask);
+            killTest(child);
             exit(0);
         }
-        task_terminate(childTask);
+        killTest(child);
 
         // 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");
-            task_terminate(childTask);
+             killTest(child);
             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
-        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");
-            task_terminate(childTask);
+            killTest(child);
             exit(0);
         }
-        task_terminate(childTask);
+        killTest(child);
 
         // 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");
-            task_terminate(childTask);
+            killTest(child);
             exit(0);
         }
-        task_terminate(childTask);
+        killTest(child);
 #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");
-            task_terminate(childTask);
+            killTest(child);
             exit(0);
         }
-        task_terminate(childTask);
+        killTest(child);
 
         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 <signal.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) )
-        task_suspend(mach_task_self());
+        (void)kill(getpid(), SIGSTOP);
 
     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
 
-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");
+        printf("[FAIL] dyld_process_info_unload posix_spawnattr_init()\n");
         exit(0);
     }
     if ( launchSuspended ) {
         if ( posix_spawnattr_setflags(&attr, POSIX_SPAWN_START_SUSPENDED) != 0 ) {
-            printf("[FAIL] dyld_process_info POSIX_SPAWN_START_SUSPENDED\n");
+            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 ) {
-           printf("[FAIL] dyld_process_info posix_spawnattr_setbinpref_np()\n");
+           printf("[FAIL] dyld_process_info_unload posix_spawnattr_setbinpref_np()\n");
             exit(0);
         }
     }
 
-    pid_t childPid;
+    struct task_and_pid child;
     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);
+        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);
     }
-    //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);
     }
 
@@ -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;
-        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 );
 
-    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;
-        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 )
-            task_resume(task);
+            (void)kill(tp.pid, SIGCONT);
         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);
         }
     }
-    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;
     }
@@ -113,16 +128,15 @@ int main(int argc, const char* argv[])
         exit(0);
     }
     const char* testProgPath = argv[1];
-    task_t childTask;
+    struct task_and_pid child;
 
     // 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);
     }
-    task_terminate(childTask);
-
+    killTest(child);
 
     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()
 {
-       const struct dyld_all_image_infos* allInfo = _dyld_get_all_image_infos();
-       if ( allInfo == NULL ) {
-               FAIL("dyld_shared_cache_iterate_text: _dyld_get_all_image_infos() failed");
-               exit(0);
-       }
     uuid_t curUuid;
-    memcpy(curUuid, allInfo->sharedCacheUUID, 16);
+    _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;
-        //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");